def _emit_method_array(self, llvm_module): """ Collect exported methods and emit a PyMethodDef array. :returns: a pointer to the PyMethodDef array. """ method_defs = [] for entry in self.export_entries: name = entry.symbol llvm_func_name = self._mangle_method_symbol(name) fnty = self.exported_function_types[entry] lfunc = ir.Function(llvm_module, fnty, llvm_func_name) method_name = self.context.insert_const_string(llvm_module, name) method_def_const = ir.Constant.literal_struct( (method_name, ir.Constant.bitcast(lfunc, lt._void_star), METH_VARARGS_AND_KEYWORDS, NULL)) method_defs.append(method_def_const) sentinel = ir.Constant.literal_struct([NULL, NULL, ZERO, NULL]) method_defs.append(sentinel) method_array_init = create_constant_array(self.method_def_ty, method_defs) method_array = cgutils.add_global_variable(llvm_module, method_array_init.type, '.module_methods') method_array.initializer = method_array_init method_array.linkage = 'internal' method_array_ptr = ir.Constant.gep(method_array, [ZERO, ZERO]) return method_array_ptr
def insert_const_string(self, mod, string): """Create a global string from the passed in string argument and return a void* in the GENERIC address space pointing to that string. Args: mod: LLVM module where the global string value is to be inserted. string: A Python string that will be converted to a global constant string and inserted into the module. Returns: A LLVM Constant pointing to the global string value inserted into the module. """ text = cgutils.make_bytearray(string.encode("utf-8") + b"\x00") name = "$".join(["__conststring__", self.mangler(string, ["str"])]) # Try to reuse existing global gv = mod.globals.get(name) if gv is None: # Not defined yet gv = cgutils.add_global_variable(mod, text.type, name=name, addrspace=address_space.GENERIC) gv.linkage = "internal" gv.global_constant = True gv.initializer = text # Cast to a i8* pointer charty = gv.type.pointee.element return gv.bitcast(charty.as_pointer(address_space.GENERIC))
def _generic_array(context, builder, shape, dtype, symbol_name, addrspace): elemcount = reduce(operator.mul, shape, 1) lldtype = context.get_data_type(dtype) laryty = Type.array(lldtype, elemcount) if addrspace == target.SPIR_LOCAL_ADDRSPACE: lmod = builder.module # Create global variable in the requested address-space gvmem = cgutils.add_global_variable(lmod, laryty, symbol_name, addrspace) if elemcount <= 0: raise ValueError("array length <= 0") else: gvmem.linkage = lc.LINKAGE_INTERNAL if dtype not in types.number_domain: raise TypeError("unsupported type: %s" % dtype) # Convert to generic address-space dataptr = context.addrspacecast(builder, gvmem, target.SPIR_GENERIC_ADDRSPACE) else: raise NotImplementedError("addrspace {addrspace}".format(**locals())) return _make_array(context, builder, dataptr, dtype, shape)
def get_c_value(self, builder, typ, name, dllimport=False): """ Get a global value through its C-accessible *name*, with the given LLVM type. If *dllimport* is true, the symbol will be marked as imported from a DLL (necessary for AOT compilation under Windows). """ module = builder.function.module try: gv = module.globals[name] except KeyError: gv = cgutils.add_global_variable(module, typ, name) if dllimport and self.aot_mode and sys.platform == 'win32': gv.storage_class = "dllimport" return gv
def make_constant_array(self, builder, aryty, arr): """ Unlike the parent version. This returns a a pointer in the constant addrspace. """ lmod = builder.module constvals = [ self.get_constant(types.byte, i) for i in iter(arr.tobytes(order='A')) ] constaryty = ir.ArrayType(ir.IntType(8), len(constvals)) constary = ir.Constant(constaryty, constvals) addrspace = nvvm.ADDRSPACE_CONSTANT gv = cgutils.add_global_variable(lmod, constary.type, "_cudapy_cmem", addrspace=addrspace) gv.linkage = 'internal' gv.global_constant = True gv.initializer = constary # Preserve the underlying alignment lldtype = self.get_data_type(aryty.dtype) align = self.get_abi_sizeof(lldtype) gv.align = 2**(align - 1).bit_length() # Convert to generic address-space conv = nvvmutils.insert_addrspace_conv(lmod, ir.IntType(8), addrspace) addrspaceptr = gv.bitcast(ir.PointerType(ir.IntType(8), addrspace)) genptr = builder.call(conv, [addrspaceptr]) # Create array object ary = self.make_array(aryty)(self, builder) kshape = [self.get_constant(types.intp, s) for s in arr.shape] kstrides = [self.get_constant(types.intp, s) for s in arr.strides] self.populate_array(ary, data=builder.bitcast(genptr, ary.data.type), shape=kshape, strides=kstrides, itemsize=ary.itemsize, parent=ary.parent, meminfo=None) return ary._getvalue()
def declare_string(builder, value): lmod = builder.basic_block.function.module cval = cgutils.make_bytearray(value.encode("utf-8") + b"\x00") gl = cgutils.add_global_variable(lmod, cval.type, name="_str", addrspace=nvvm.ADDRSPACE_CONSTANT) gl.linkage = 'internal' gl.global_constant = True gl.initializer = cval charty = ir.IntType(8) constcharptrty = ir.PointerType(charty, nvvm.ADDRSPACE_CONSTANT) charptr = builder.bitcast(gl, constcharptrty) conv = insert_addrspace_conv(lmod, charty, nvvm.ADDRSPACE_CONSTANT) return builder.call(conv, [charptr])
def add_dynamic_addr(self, builder, intaddr, info): """ Returns dynamic address as a void pointer `i8*`. Internally, a global variable is added to inform the lowerer about the usage of dynamic addresses. Caching will be disabled. """ assert self.allow_dynamic_globals, "dyn globals disabled in this target" assert isinstance(intaddr, int), 'dyn addr not of int type' mod = builder.module llvoidptr = self.get_value_type(types.voidptr) addr = self.get_constant(types.uintp, intaddr).inttoptr(llvoidptr) # Use a unique name by embedding the address value symname = 'numba.dynamic.globals.{:x}'.format(intaddr) gv = cgutils.add_global_variable(mod, llvoidptr, symname) # Use linkonce linkage to allow merging with other GV of the same name. # And, avoid optimization from assuming its value. gv.linkage = 'linkonce' gv.initializer = addr return builder.load(gv)
def insert_const_string(self, mod, string): """ Unlike the parent version. This returns a a pointer in the constant addrspace. """ text = cgutils.make_bytearray(string.encode("utf-8") + b"\x00") name = '$'.join(["__conststring__", itanium_mangler.mangle_identifier(string)]) # Try to reuse existing global gv = mod.globals.get(name) if gv is None: # Not defined yet gv = cgutils.add_global_variable(mod, text.type, name, addrspace=nvvm.ADDRSPACE_CONSTANT) gv.linkage = 'internal' gv.global_constant = True gv.initializer = text # Cast to a i8* pointer charty = gv.type.pointee.element return gv.bitcast(charty.as_pointer(nvvm.ADDRSPACE_CONSTANT))
def define_error_gv(postfix): name = wrapfn.name + postfix gv = cgutils.add_global_variable(wrapper_module, ir.IntType(32), name) gv.initializer = ir.Constant(gv.type.pointee, None) return gv
def _generic_array(context, builder, shape, dtype, symbol_name, addrspace, can_dynsized=False): elemcount = reduce(operator.mul, shape, 1) # Check for valid shape for this type of allocation. # Only 1d arrays can be dynamic. dynamic_smem = elemcount <= 0 and can_dynsized and len(shape) == 1 if elemcount <= 0 and not dynamic_smem: raise ValueError("array length <= 0") # Check that we support the requested dtype data_model = context.data_model_manager[dtype] other_supported_type = (isinstance(dtype, (types.Record, types.Boolean)) or isinstance(data_model, models.StructModel)) if dtype not in types.number_domain and not other_supported_type: raise TypeError("unsupported type: %s" % dtype) lldtype = context.get_data_type(dtype) laryty = ir.ArrayType(lldtype, elemcount) if addrspace == nvvm.ADDRSPACE_LOCAL: # Special case local address space allocation to use alloca # NVVM is smart enough to only use local memory if no register is # available dataptr = cgutils.alloca_once(builder, laryty, name=symbol_name) else: lmod = builder.module # Create global variable in the requested address space gvmem = cgutils.add_global_variable(lmod, laryty, symbol_name, addrspace) # Specify alignment to avoid misalignment bug align = context.get_abi_sizeof(lldtype) # Alignment is required to be a power of 2 for shared memory. If it is # not a power of 2 (e.g. for a Record array) then round up accordingly. gvmem.align = 1 << (align - 1).bit_length() if dynamic_smem: gvmem.linkage = 'external' else: ## Comment out the following line to workaround a NVVM bug ## which generates a invalid symbol name when the linkage ## is internal and in some situation. ## See _get_unique_smem_id() # gvmem.linkage = lc.LINKAGE_INTERNAL gvmem.initializer = ir.Constant(laryty, ir.Undefined) # Convert to generic address-space conv = nvvmutils.insert_addrspace_conv(lmod, ir.IntType(8), addrspace) addrspaceptr = gvmem.bitcast(ir.PointerType(ir.IntType(8), addrspace)) dataptr = builder.call(conv, [addrspaceptr]) targetdata = _get_target_data(context) lldtype = context.get_data_type(dtype) itemsize = lldtype.get_abi_size(targetdata) # Compute strides laststride = itemsize rstrides = [] for i, lastsize in enumerate(reversed(shape)): rstrides.append(laststride) laststride *= lastsize strides = [s for s in reversed(rstrides)] kstrides = [context.get_constant(types.intp, s) for s in strides] # Compute shape if dynamic_smem: # Compute the shape based on the dynamic shared memory configuration. # Unfortunately NVVM does not provide an intrinsic for the # %dynamic_smem_size register, so we must read it using inline # assembly. get_dynshared_size = ir.InlineAsm(ir.FunctionType(ir.IntType(32), []), "mov.u32 $0, %dynamic_smem_size;", '=r', side_effect=True) dynsmem_size = builder.zext(builder.call(get_dynshared_size, []), ir.IntType(64)) # Only 1-D dynamic shared memory is supported so the following is a # sufficient construction of the shape kitemsize = context.get_constant(types.intp, itemsize) kshape = [builder.udiv(dynsmem_size, kitemsize)] else: kshape = [context.get_constant(types.intp, s) for s in shape] # Create array object ndim = len(shape) aryty = types.Array(dtype=dtype, ndim=ndim, layout='C') ary = context.make_array(aryty)(context, builder) context.populate_array(ary, data=builder.bitcast(dataptr, ary.data.type), shape=kshape, strides=kstrides, itemsize=context.get_constant(types.intp, itemsize), meminfo=None) return ary._getvalue()
def _emit_python_wrapper(self, llvm_module): # Figure out the Python C API module creation function, and # get a LLVM function for it. create_module_fn = ir.Function(llvm_module, *self.module_create_definition) create_module_fn.linkage = 'external' # Define a constant string for the module name. mod_name_const = self.context.insert_const_string( llvm_module, self.module_name) mod_def_base_init = ir.Constant.literal_struct(( lt._pyobject_head_init, # PyObject_HEAD ir.Constant(self.m_init_ty, None), # m_init ir.Constant(lt._llvm_py_ssize_t, None), # m_index ir.Constant(lt._pyobject_head_p, None), # m_copy )) mod_def_base = cgutils.add_global_variable(llvm_module, mod_def_base_init.type, '.module_def_base') mod_def_base.initializer = mod_def_base_init mod_def_base.linkage = 'internal' method_array = self._emit_method_array(llvm_module) mod_def_init = ir.Constant.literal_struct(( mod_def_base_init, # m_base mod_name_const, # m_name ir.Constant(self._char_star, None), # m_doc ir.Constant(lt._llvm_py_ssize_t, -1), # m_size method_array, # m_methods ir.Constant(self.inquiry_ty, None), # m_reload ir.Constant(self.traverseproc_ty, None), # m_traverse ir.Constant(self.inquiry_ty, None), # m_clear ir.Constant(self.freefunc_ty, None) # m_free )) # Define a constant string for the module name. mod_def = cgutils.add_global_variable(llvm_module, mod_def_init.type, '.module_def') mod_def.initializer = mod_def_init mod_def.linkage = 'internal' # Define the module initialization function. mod_init_fn = ir.Function(llvm_module, *self.module_init_definition) entry = mod_init_fn.append_basic_block('Entry') builder = ir.IRBuilder(entry) pyapi = self.context.get_python_api(builder) mod = builder.call(create_module_fn, (mod_def, ir.Constant(lt._int32, sys.api_version))) # Test if module has been created correctly. # (XXX for some reason comparing with the NULL constant fails llvm # with an assertion in pydebug mode) with builder.if_then(cgutils.is_null(builder, mod)): builder.ret(NULL.bitcast(mod_init_fn.type.pointee.return_type)) env_array = self._emit_environment_array(llvm_module, builder, pyapi) envgv_array = self._emit_envgvs_array(llvm_module, builder, pyapi) ret = self._emit_module_init_code(llvm_module, builder, mod, method_array, env_array, envgv_array) if ret is not None: with builder.if_then(cgutils.is_not_null(builder, ret)): # Init function errored out builder.ret(ir.Constant(mod.type, None)) builder.ret(mod) self.dll_exports.append(mod_init_fn.name)