From 009239713eb97ead506584652325f9ddf260557a Mon Sep 17 00:00:00 2001 From: "Travis E. Oliphant" Date: Sat, 25 May 2013 13:35:37 -0500 Subject: [PATCH] Update llvm_array concepts. --- llvm_array/array.py | 261 +++++++++++++++++++++++++++++++------------- 1 file changed, 187 insertions(+), 74 deletions(-) diff --git a/llvm_array/array.py b/llvm_array/array.py index 12df60e..3cac244 100644 --- a/llvm_array/array.py +++ b/llvm_array/array.py @@ -24,19 +24,98 @@ # intp stride; #} diminfo # +# These are for low-level array-routines that need to know the number +# of dimensions at run-time (not just code-generation time): +# +# The first two are recommended +# +# struct { +# eltype *data; +# int32 nd; +# intp shape[nd]; +# } contiguous_array_nd(eltype) +# +# struct { +# eltype *data; +# int32 nd; +# diminfo shape[nd]; +# } strided_array_nd(eltype) +# +# +# Backward compatible but deprecated: +# struct { +# eltype *data; +# int32 nd; +# intp shape[nd]; +# intp stride[nd]; +# } strided_soa_array_nd(eltype) +# +# +# The most general (where the kind of array is stored as well as number +# of dimensions) +# Rarely needed. +# +# struct { +# eltype *data; +# int16 nd; +# int16 dimkind; +# ??? +# } array_nd(eltype) +# +# where ??? is run-time interpreted based on the dimkind to either: +# intp shape[nd]; for dimkind = C_CONTIGUOUS or F_CONTIGUOUS +# +# diminfo shape[nd]; for dimkind = STRIDED +# +# intp shape[ind]; +# intp strides[ind]; dimkind = STRIDED_SOA +# import llvm.core as lc from llvm.core import Type import llvm_cbuilder.shortnames as C -CONTIGUOUS = 1 << 8 -STRIDED = CONTIGUOUS + 1 -STRIDED_SOA = CONTIGUOUS + 2 +# Different Array Types +ARRAYBIT = 1<<4 +C_CONTIGUOUS = ARRAYBIT + 0 +F_CONTIGUOUS = ARRAYBIT + 1 +STRIDED = ARRAYBIT + 2 +STRIDED_SOA = ARRAYBIT + 3 -array_kinds = (CONTIGUOUS, STRIDED, STRIDED_SOA) +HAS_ND = 1<<5 +C_CONTIGUOUS_ND = C_CONTIGUOUS + HAS_ND +F_CONTIGUOUS_ND = F_CONTIGUOUS + HAS_ND +STRIDED_ND = STRIDED + HAS_ND +STRIDED_SOA_ND = STRIDED_SOA + HAS_ND + +HAS_DIMKIND = 1<<6 +C_CONTIGUOUS_DK = C_CONTIGUOUS + HAS_DIMKIND +F_CONTIGUOUS_DK = F_CONTIGUOUS + HAS_DIMKIND +STRIDED_DK = STRIDED + HAS_DIMKIND +STRIDED_SOA_DK = STRIDED_SOA + HAS_DIMKIND + +array_kinds = (C_CONTIGUOUS, F_CONTIGUOUS, STRIDED, STRIDED_SOA, + C_CONTIGUOUS_ND, F_CONTIGUOUS_ND, STRIDED_ND, STRIDED_SOA_DK, + C_CONTIGUOUS_DK, F_CONTIGUOUS_DK, STRIDED_DK, STRIDED_SOA_DK) + +_invmap = {} + +def kind_to_str(kind): + global _invmap + if not _invmap: + for key, value in globals().items(): + if isinstance(value, int) and value in array_kinds: + _invmap[value] = key + return _invmap[kind] + +def str_to_kind(str): + trial = eval(str) + if trial not in array_kinds: + raise ValueError("Invalid Array Kind") + return trial void_type = C.void -int_type = C.int +int32_type = C.int32 char_type = C.char int16_type = C.int16 intp_type = C.intp @@ -45,99 +124,133 @@ diminfo_type = Type.struct([intp_type, # shape intp_type # stride ], name='diminfo') -# This is the way we define LLVM arrays. -def cont_array_type(nd, el_type=char_type, name=''): - terms = [Type.pointer(el_type), # data - Type.array(intp_type, nd) # shape - ] - return Type.struct(terms, name=name) +_cache = {} +# This is the way we define LLVM arrays. +# CONTIGUOUS and STRIDED are strongly encouraged... +def array_type(nd, kind, el_type=char_type): + key = (kind, nd, el_type) + if _cache.has_key(key): + return _cache[key] -def strided_array_type(nd, el_type=char_type, name=''): - terms = [Type.pointer(el_type), # data - Type.array(diminfo_type, nd) # diminfo - ] - return Type.struct(terms, name=name) + base = kind & (~(HAS_ND | HAS_DIMKIND)) + if base == C_CONTIGUOUS: + dimstr = 'Array_C' + elif base == F_CONTIGUOUS: + dimstr = 'Array_F' + elif base == STRIDED: + dimstr = 'Array_S' + elif base == STRIDED_SOA: + dimstr = 'Array_A' + else: + raise TypeError("Do not understand Array kind of %d" % kind) + + terms = [Type.pointer(el_type)] # data + + if (kind & HAS_ND): + terms.append(int32_type) # nd + dimstr += '_ND' + elif (kind & HAS_DIMKIND): + terms.extend([int16_type, int16_type]) # nd, dimkind + dimstr += '_DK' + + if base in [C_CONTIGUOUS, F_CONTIGUOUS]: + terms.append(Type.array(intp_type, nd)) # shape + elif base == STRIDED: + terms.append(Type.array(diminfo_type, nd)) # diminfo + elif base == STRIDED_SOA: + terms.extend([Type.array(intp_type, nd), # shape + Type.array(intp_type, nd)]) # strides + + ret = Type.struct(terms, name=dimstr) + _cache[key] = ret + return ret -def strided_soa_type(nd, el_type=char_type, name=''): - terms = [Type.pointer(el_type), # data - Type.array(intp_type, nd), # shape[nd] - Type.array(intp_type, nd) # strides[nd] - ] - return Type.struct(terms, name=name) def check_array(arrtyp): if not isinstance(arrtyp, lc.StructType): return None - if arrtyp.element_count not in [2, 3]: - return None - if not isinstance(arrtyp.elements[0], lc.PointerType) or \ - not isinstance(arrtyp.elements[1], lc.ArrayType): + if arrtyp.element_count not in [2, 3, 4, 5]: return None - data_type = arrtyp.elements[0].pointee - s1 = arrtyp.elements[1] + # Look through _cache and see if it's there + for key, value in _cache.items(): + if arrtyp is value: + return key + + return _raw_check_array(arrtyp) + +# Manual check +def _raw_check_array(arrtyp): + a0 = arrtyp.elements[0] + a1 = arrtyp.elements[1] + if not isinstance(a0, lc.PointerType) or \ + not (isinstance(a1, lc.ArrayType) or + (a1 == int32_type) or (a1 == int16_type)): + return None + + data_type = a0.pointee + + if arrtyp.is_literal: + c_contig = True + else: + if arrtyp.name.startswith('Array_F'): + c_contig = False + else: + c_contig = True + + + if a1 == int32_type: + num = 2 + strided = STRIDED_ND + strided_soa = STRIDED_SOA_ND + c_contiguous = C_CONTIGUOUS_ND + f_contiguous = F_CONTIGUOUS_ND + elif a1 == int16_type: + if arrtyp.element_count < 3 or arrtyp.elements[2] != int16_type: + return None + num = 3 + strided = STRIDED_DK + strided_soa = STRIDED_SOA_DK + c_contiguous = C_CONTIGUOUS_DK + f_contiguous = F_CONTIGUOUS_DK + else: + num = 1 + strided = STRIDED + strided_soa = STRIDED_SOA + c_contiguous = C_CONTIGUOUS + f_contiguous = F_CONTIGUOUS + + elcount = num + 2 + # otherwise we have lc.ArrType as element [1] + if arrtyp.element_count not in [num+1,num+2]: + return None + s1 = arrtyp.elements[num] nd = s1.count - if arrtyp.element_count == 3: - if not isinstance(arrtyp.elements[2], lc.ArrayType): + if arrtyp.element_count == elcount: + if not isinstance(arrtyp.elements[num+1], lc.ArrayType): return None - s2 = arrtyp.elements[2] + s2 = arrtyp.elements[num+1] if s1.element != intp_type or s2.element != intp_type: return None if s1.count != s2.count: return None - return STRIDED_SOA, nd, data_type + return strided_soa, nd, data_type if s1.element == diminfo_type: - return STRIDED, nd, data_type + return strided, nd, data_type elif s1.element == intp_type: - return CONTIGUOUS, nd, data_type + return c_contiguous if c_contig else f_contiguous, nd, data_type else: return None -def is_cont_array(arrtyp): - if not isinstance(arrtyp, lc.StructType): - return False - if arrtyp.element_count != 2 or \ - not isinstance(arrtyp.elements[0], lc.PointerType) or \ - not isinstance(arrtyp.elements[1], lc.ArrayType): - return False - if arrtyp.elements[1].element != intp_type: - return False - return True - -def is_strided_array(arrtyp, kind=diminfo_type): - if not isinstance(arrtyp, lc.StructType): - return False - if arrtyp.element_count != 2 or \ - not isinstance(arrtyp.elements[0], lc.PointerType) or \ - not isinstance(arrtyp.elements[1], lc.ArrayType): - return False - if arrtyp.elements[1].element != kind: - return False - return True - -def is_strided_soa_array(arrtyp): - if not isinstance(arrtyp, lc.StructType): - return False - if arrtyp.element_count != 3 or \ - not isinstance(arrtyp.elements[0], lc.PointerType) or \ - not isinstance(arrtyp.elements[1], lc.ArrayType) or \ - not isinstance(arrtyp.elements[2], lc.ArrayType): - return False - s1, s2 = arrtyp.elements[1:] - if s1.element != intp_type or s2.element != intp_type: - return False - if s1.count != s2.count: - return False - return True def test(): - arr = cont_array_type(5) - assert check_array(arr) == (CONTIGUOUS, 5, char_type) - arr = strided_array_type(4) + arr = array_type(5, C_CONTIGUOUS) + assert check_array(arr) == (C_CONTIGUOUS, 5, char_type) + arr = array_type(4, STRIDED) assert check_array(arr) == (STRIDED, 4, char_type) - arr = strided_soa_type(3) + arr = array_type(3, STRIDED_SOA) assert check_array(arr) == (STRIDED_SOA, 3, char_type) if __name__ == '__main__':