def codegen(context, builder, signature, args): vis_type, out_shape_type = signature.args return_type = signature.return_type vis, out_shape = args # Create the outer tuple llvm_outer_tuple_type = context.get_value_type(return_type) outer_tuple = cgutils.get_null_value(llvm_outer_tuple_type) def gen_array_factory(numba_dtype): """ Create a funtion that creates an array. Bind the numpy dtype because I don't know how to create the numba version """ np_dtype = numpy_support.as_dtype(numba_dtype) return lambda shape: np.zeros(shape, np_dtype) for i, inner_type in enumerate(return_type.types): # Generate an array and insert into return tuple if have_vis_array: array_factory = gen_array_factory(inner_type.dtype) factory_sig = inner_type(out_shape_type) factory_args = [out_shape] # Compile function and get handle to output array inner_value = context.compile_internal(builder, array_factory, factory_sig, factory_args) # Insert inner tuple into outer tuple elif have_vis_tuple: # Create the inner tuple llvm_inner_type = context.get_value_type(inner_type) inner_value = cgutils.get_null_value(llvm_inner_type) for j, array_type in enumerate(inner_type.types): # Create function, it's signature and arguments array_factory = gen_array_factory(array_type.dtype) factory_sig = array_type(out_shape_type) factory_args = [out_shape] # Compile function and get handle to output data = context.compile_internal(builder, array_factory, factory_sig, factory_args) # Insert data into inner_value inner_value = builder.insert_value(inner_value, data, j) else: raise ValueError("Internal logic error") # Insert inner tuple into outer tuple outer_tuple = builder.insert_value(outer_tuple, inner_value, i) return outer_tuple
def optional_to_optional(context, builder, fromty, toty, val): """ The handling of optional->optional cast must be special cased for correct propagation of None value. Given type T and U. casting of T? to U? (? denotes optional) should always succeed. If the from-value is None, the None value the casted value (U?) should be None; otherwise, the from-value is casted to U. This is different from casting T? to U, which requires the from-value must not be None. """ optval = context.make_helper(builder, fromty, value=val) validbit = cgutils.as_bool_bit(builder, optval.valid) # Create uninitialized optional value outoptval = context.make_helper(builder, toty) with builder.if_else(validbit) as (is_valid, is_not_valid): with is_valid: # Cast internal value outoptval.valid = cgutils.true_bit outoptval.data = context.cast(builder, optval.data, fromty.type, toty.type) with is_not_valid: # Store None to result outoptval.valid = cgutils.false_bit outoptval.data = cgutils.get_null_value(outoptval.data.type) return outoptval._getvalue()
def _make_constant_bytes(context, builder, nbytes): bstr_ctor = cgutils.create_struct_proxy(bytes_type) bstr = bstr_ctor(context, builder) if isinstance(nbytes, int): nbytes = ir.Constant(bstr.nitems.type, nbytes) bstr.meminfo = context.nrt.meminfo_alloc(builder, nbytes) bstr.nitems = nbytes bstr.itemsize = ir.Constant(bstr.itemsize.type, 1) bstr.data = context.nrt.meminfo_data(builder, bstr.meminfo) bstr.parent = cgutils.get_null_value(bstr.parent.type) # bstr.shape and bstr.strides are not used bstr.shape = cgutils.get_null_value(bstr.shape.type) bstr.strides = cgutils.get_null_value(bstr.strides.type) return bstr
def box_lsttype(typ, val, c): context = c.context builder = c.builder # XXX deduplicate ctor = cgutils.create_struct_proxy(typ) lstruct = ctor(context, builder, value=val) # Returns the plain MemInfo boxed_meminfo = c.box( types.MemInfoPointer(types.voidptr), lstruct.meminfo, ) modname = c.context.insert_const_string( c.builder.module, 'numba.typed.typedlist', ) typedlist_mod = c.pyapi.import_module_noblock(modname) fmp_fn = c.pyapi.object_getattr_string(typedlist_mod, '_from_meminfo_ptr') lsttype_obj = c.pyapi.unserialize(c.pyapi.serialize_object(typ)) result_var = builder.alloca(c.pyapi.pyobj) builder.store(cgutils.get_null_value(c.pyapi.pyobj), result_var) with builder.if_then(cgutils.is_not_null(builder, lsttype_obj)): res = c.pyapi.call_function_objargs( fmp_fn, (boxed_meminfo, lsttype_obj), ) c.pyapi.decref(fmp_fn) c.pyapi.decref(typedlist_mod) c.pyapi.decref(boxed_meminfo) builder.store(res, result_var) return builder.load(result_var)
def call_function(self, builder, callee, resty, argtys, args): """ Call the Numba-compiled *callee*. """ # XXX better fix for callees that are not function values # (pointers to function; thus have no `.args` attribute) retty = self._get_return_argument(callee.function_type).pointee retvaltmp = cgutils.alloca_once(builder, retty) # initialize return value to zeros builder.store(cgutils.get_null_value(retty), retvaltmp) excinfoptr = cgutils.alloca_once(builder, ir.PointerType(excinfo_t), name="excinfo") arginfo = self._get_arg_packer(argtys) args = list(arginfo.as_arguments(builder, args)) realargs = [retvaltmp, excinfoptr] + args code = builder.call(callee, realargs) status = self._get_return_status(builder, code, builder.load(excinfoptr)) retval = builder.load(retvaltmp) out = self.context.get_returned_value(builder, resty, retval) return status, out
def unset_try_status(self, builder): try_state_ptr = self._get_try_state(builder) # Decrement try depth old = builder.load(try_state_ptr) new = builder.sub(old, old.type(1)) builder.store(new, try_state_ptr) # Needs to reset the exception state so that the exception handler # will run normally. excinfoptr = self._get_excinfo_argument(builder.function) null = cgutils.get_null_value(excinfoptr.type.pointee) builder.store(null, excinfoptr)
def alloca(self, name, ltype=None): """ Allocate a stack slot and initialize it to NULL. The default is to allocate a pyobject pointer. Use ``ltype`` to override. """ if ltype is None: ltype = self.context.get_value_type(types.pyobject) with self.builder.goto_block(self.entry_block): ptr = self.builder.alloca(ltype, name=name) self.builder.store(cgutils.get_null_value(ltype), ptr) return ptr
def delvar(self, name): """ Delete the variable slot with the given name. This will decref the corresponding Python object. """ # If this raises then the live variables analysis is wrong self._live_vars.remove(name) ptr = self._getvar(name) # initializes `name` if not already self.decref(self.builder.load(ptr)) # This is a safety guard against double decref's, but really # the IR should be correct and have only one Del per variable # and code path. self.builder.store(cgutils.get_null_value(ptr.type.pointee), ptr)
def call_function(self, builder, callee, resty, argtys, args, env=None): """Call the Numba-compiled *callee*.""" assert env is None retty = callee.args[0].type.pointee retvaltmp = cgutils.alloca_once(builder, retty) # initialize return value builder.store(cgutils.get_null_value(retty), retvaltmp) arginfo = self.context.get_arg_packer(argtys) args = arginfo.as_arguments(builder, args) realargs = [retvaltmp] + list(args) code = builder.call(callee, realargs) status = self._get_return_status(builder, code) retval = builder.load(retvaltmp) out = self.context.get_returned_value(builder, resty, retval) return status, out
def _define_nrt_incref(module, atomic_incr): """ Implement NRT_incref in the module """ fn_incref = module.get_or_insert_function(incref_decref_ty, name="NRT_incref") # Cannot inline this for refcount pruning to work fn_incref.attributes.add("noinline") builder = ir.IRBuilder(fn_incref.append_basic_block()) [ptr] = fn_incref.args is_null = builder.icmp_unsigned("==", ptr, cgutils.get_null_value(ptr.type)) with cgutils.if_unlikely(builder, is_null): builder.ret_void() if _debug_print: cgutils.printf(builder, "*** NRT_Incref %zu [%p]\n", builder.load(ptr), ptr) builder.call(atomic_incr, [builder.bitcast(ptr, atomic_incr.args[0].type)]) builder.ret_void()
def declare_env_global(self, module, envname): """Declare the Environment pointer as a global of the module. The pointer is initialized to NULL. It must be filled by the runtime with the actual address of the Env before the associated function can be executed. Parameters ---------- module : The LLVM Module envname : str The name of the global variable. """ if envname not in module.globals: gv = llvmir.GlobalVariable(module, cgutils.voidptr_t, name=envname) gv.linkage = 'common' gv.initializer = cgutils.get_null_value(gv.type.pointee) return module.globals[envname]
def ctor_impl(context, builder, sig, args): """ Generic constructor (__new__) for jitclasses. """ # Allocate the instance inst_typ = sig.return_type alloc_type = context.get_data_type(inst_typ.get_data_type()) alloc_size = context.get_abi_sizeof(alloc_type) meminfo = context.nrt.meminfo_alloc_dtor( builder, context.get_constant(types.uintp, alloc_size), imp_dtor(context, builder.module, inst_typ), ) data_pointer = context.nrt.meminfo_data(builder, meminfo) data_pointer = builder.bitcast(data_pointer, alloc_type.as_pointer()) # Nullify all data builder.store(cgutils.get_null_value(alloc_type), data_pointer) inst_struct = context.make_helper(builder, inst_typ) inst_struct.meminfo = meminfo inst_struct.data = data_pointer # Call the jitted __init__ # TODO: extract the following into a common util init_sig = (sig.return_type,) + sig.args init = inst_typ.jit_methods['__init__'] disp_type = types.Dispatcher(init) call = context.get_function(disp_type, types.void(*init_sig)) _add_linking_libs(context, call) realargs = [inst_struct._getvalue()] + list(args) call(builder, realargs) # Prepare return value ret = inst_struct._getvalue() return imputils.impl_ret_new_ref(context, builder, inst_typ, ret)
def codegen(context, builder, signature, args): # FIXME: mostly the same as jitclass ctor_impl() model = context.data_model_manager[inst_type.get_data_type()] alloc_type = model.get_value_type() alloc_size = context.get_abi_sizeof(alloc_type) meminfo = context.nrt.meminfo_alloc_dtor( builder, context.get_constant(types.uintp, alloc_size), imp_dtor(context, builder.module, inst_type), ) data_pointer = context.nrt.meminfo_data(builder, meminfo) data_pointer = builder.bitcast(data_pointer, alloc_type.as_pointer()) # Nullify all data builder.store(cgutils.get_null_value(alloc_type), data_pointer) inst_struct = context.make_helper(builder, inst_type) inst_struct.meminfo = meminfo return inst_struct._getvalue()
def _define_nrt_decref(module, atomic_decr): """ Implement NRT_decref in the module """ fn_decref = cgutils.get_or_insert_function(module, incref_decref_ty, "NRT_decref") # Cannot inline this for refcount pruning to work fn_decref.attributes.add('noinline') calldtor = ir.Function(module, ir.FunctionType(ir.VoidType(), [_pointer_type]), name="NRT_MemInfo_call_dtor") builder = ir.IRBuilder(fn_decref.append_basic_block()) [ptr] = fn_decref.args is_null = builder.icmp_unsigned("==", ptr, cgutils.get_null_value(ptr.type)) with cgutils.if_unlikely(builder, is_null): builder.ret_void() # For memory fence usage, see https://llvm.org/docs/Atomics.html # A release fence is used before the relevant write operation. # No-op on x86. On POWER, it lowers to lwsync. builder.fence("release") word_ptr = builder.bitcast(ptr, atomic_decr.args[0].type) if config.DEBUG_NRT: cgutils.printf(builder, "*** NRT_Decref %zu [%p]\n", builder.load(word_ptr), ptr) newrefct = builder.call(atomic_decr, [word_ptr]) refct_eq_0 = builder.icmp_unsigned("==", newrefct, ir.Constant(newrefct.type, 0)) with cgutils.if_unlikely(builder, refct_eq_0): # An acquire fence is used after the relevant read operation. # No-op on x86. On POWER, it lowers to lwsync. builder.fence("acquire") builder.call(calldtor, [ptr]) builder.ret_void()
def call_function(self, builder, callee, resty, argtys, args, attrs=None): """ Call the Numba-compiled *callee*. Parameters: ----------- attrs: LLVM style string or iterable of individual attributes, default is None which specifies no attributes. Examples: LLVM style string: "noinline fast" Equivalent iterable: ("noinline", "fast") """ # XXX better fix for callees that are not function values # (pointers to function; thus have no `.args` attribute) retty = self._get_return_argument(callee.function_type).pointee retvaltmp = cgutils.alloca_once(builder, retty) # initialize return value to zeros builder.store(cgutils.get_null_value(retty), retvaltmp) excinfoptr = cgutils.alloca_once(builder, ir.PointerType(excinfo_t), name="excinfo") arginfo = self._get_arg_packer(argtys) args = list(arginfo.as_arguments(builder, args)) realargs = [retvaltmp, excinfoptr] + args # deal with attrs, it's fine to specify a load in a string like # "noinline fast" as per LLVM or equally as an iterable of individual # attributes. if attrs is None: _attrs = () elif isinstance(attrs, Iterable) and not isinstance(attrs, str): _attrs = tuple(attrs) else: raise TypeError("attrs must be an iterable of strings or None") code = builder.call(callee, realargs, attrs=_attrs) status = self._get_return_status(builder, code, builder.load(excinfoptr)) retval = builder.load(retvaltmp) out = self.context.get_returned_value(builder, resty, retval) return status, out
def lower_expr(self, expr): if expr.op == 'binop': return self.lower_binop(expr, expr.fn, inplace=False) elif expr.op == 'inplace_binop': return self.lower_binop(expr, expr.fn, inplace=True) elif expr.op == 'unary': value = self.loadvar(expr.value.name) if expr.fn == operator.neg: res = self.pyapi.number_negative(value) elif expr.fn == operator.pos: res = self.pyapi.number_positive(value) elif expr.fn == operator.not_: res = self.pyapi.object_not(value) self.check_int_status(res) res = self.pyapi.bool_from_bool(res) elif expr.fn == operator.invert: res = self.pyapi.number_invert(value) else: raise NotImplementedError(expr) self.check_error(res) return res elif expr.op == 'call': argvals = [self.loadvar(a.name) for a in expr.args] fn = self.loadvar(expr.func.name) args = self.pyapi.tuple_pack(argvals) if expr.vararg: # Expand *args new_args = self.pyapi.number_add( args, self.loadvar(expr.vararg.name)) self.decref(args) args = new_args if not expr.kws: # No named arguments ret = self.pyapi.call(fn, args, None) else: # Named arguments keyvalues = [(k, self.loadvar(v.name)) for k, v in expr.kws] kws = self.pyapi.dict_pack(keyvalues) ret = self.pyapi.call(fn, args, kws) self.decref(kws) self.decref(args) self.check_error(ret) return ret elif expr.op == 'getattr': obj = self.loadvar(expr.value.name) res = self.pyapi.object_getattr(obj, self._freeze_string(expr.attr)) self.check_error(res) return res elif expr.op == 'build_tuple': items = [self.loadvar(it.name) for it in expr.items] res = self.pyapi.tuple_pack(items) self.check_error(res) return res elif expr.op == 'build_list': items = [self.loadvar(it.name) for it in expr.items] res = self.pyapi.list_pack(items) self.check_error(res) return res elif expr.op == 'build_map': res = self.pyapi.dict_new(expr.size) self.check_error(res) for k, v in expr.items: key = self.loadvar(k.name) value = self.loadvar(v.name) ok = self.pyapi.dict_setitem(res, key, value) self.check_int_status(ok) return res elif expr.op == 'build_set': items = [self.loadvar(it.name) for it in expr.items] res = self.pyapi.set_new() self.check_error(res) for it in items: ok = self.pyapi.set_add(res, it) self.check_int_status(ok) return res elif expr.op == 'getiter': obj = self.loadvar(expr.value.name) res = self.pyapi.object_getiter(obj) self.check_error(res) return res elif expr.op == 'iternext': iterobj = self.loadvar(expr.value.name) item = self.pyapi.iter_next(iterobj) is_valid = cgutils.is_not_null(self.builder, item) pair = self.pyapi.tuple_new(2) with self.builder.if_else(is_valid) as (then, otherwise): with then: self.pyapi.tuple_setitem(pair, 0, item) with otherwise: self.check_occurred() # Make the tuple valid by inserting None as dummy # iteration "result" (it will be ignored). self.pyapi.tuple_setitem(pair, 0, self.pyapi.make_none()) self.pyapi.tuple_setitem(pair, 1, self.pyapi.bool_from_bool(is_valid)) return pair elif expr.op == 'pair_first': pair = self.loadvar(expr.value.name) first = self.pyapi.tuple_getitem(pair, 0) self.incref(first) return first elif expr.op == 'pair_second': pair = self.loadvar(expr.value.name) second = self.pyapi.tuple_getitem(pair, 1) self.incref(second) return second elif expr.op == 'exhaust_iter': iterobj = self.loadvar(expr.value.name) tup = self.pyapi.sequence_tuple(iterobj) self.check_error(tup) # Check tuple size is as expected tup_size = self.pyapi.tuple_size(tup) expected_size = self.context.get_constant(types.intp, expr.count) has_wrong_size = self.builder.icmp(lc.ICMP_NE, tup_size, expected_size) with cgutils.if_unlikely(self.builder, has_wrong_size): self.return_exception(ValueError) return tup elif expr.op == 'getitem': value = self.loadvar(expr.value.name) index = self.loadvar(expr.index.name) res = self.pyapi.object_getitem(value, index) self.check_error(res) return res elif expr.op == 'static_getitem': value = self.loadvar(expr.value.name) index = self.context.get_constant(types.intp, expr.index) indexobj = self.pyapi.long_from_ssize_t(index) self.check_error(indexobj) res = self.pyapi.object_getitem(value, indexobj) self.decref(indexobj) self.check_error(res) return res elif expr.op == 'getslice': target = self.loadvar(expr.target.name) start = self.loadvar(expr.start.name) stop = self.loadvar(expr.stop.name) slicefn = self.get_builtin_obj("slice") sliceobj = self.pyapi.call_function_objargs(slicefn, (start, stop)) self.decref(slicefn) self.check_error(sliceobj) res = self.pyapi.object_getitem(target, sliceobj) self.check_error(res) return res elif expr.op == 'cast': val = self.loadvar(expr.value.name) self.incref(val) return val elif expr.op == 'phi': raise LoweringError("PHI not stripped") elif expr.op == 'null': # Make null value return cgutils.get_null_value(self.pyapi.pyobj) else: raise NotImplementedError(expr)
def real_imag_impl(context, builder, typ, value): res = cgutils.get_null_value(value.type) return impl_ret_untracked(context, builder, typ, res)