def builtin_lookup(self, mod, name): """ Args ---- mod: The __builtins__ dictionary or module, as looked up in a module's globals. name: str The object to lookup """ fromdict = self.pyapi.dict_getitem(mod, self._freeze_string(name)) self.incref(fromdict) # fromdict is borrowed bbifdict = self.builder.basic_block with cgutils.if_unlikely(self.builder, self.is_null(fromdict)): # This happen if we are using the __main__ module frommod = self.pyapi.object_getattr(mod, self._freeze_string(name)) with cgutils.if_unlikely(self.builder, self.is_null(frommod)): self.pyapi.raise_missing_global_error(name) self.return_exception_raised() bbifmod = self.builder.basic_block builtin = self.builder.phi(self.pyapi.pyobj) builtin.add_incoming(fromdict, bbifdict) builtin.add_incoming(frommod, bbifmod) return builtin
def _unbox_native_field(typ, obj, field_name: str, c): ret_ptr = cgutils.alloca_once(c.builder, c.context.get_value_type(typ)) is_error_ptr = cgutils.alloca_once_value(c.builder, cgutils.false_bit) fail_obj = c.context.get_constant_null(typ) with local_return(c.builder) as ret: fail_blk = c.builder.append_basic_block("fail") with c.builder.goto_block(fail_blk): c.builder.store(cgutils.true_bit, is_error_ptr) c.builder.store(fail_obj, ret_ptr) ret() field_obj = c.pyapi.object_getattr_string(obj, field_name) with cgutils.if_unlikely(c.builder, cgutils.is_null(c.builder, field_obj)): c.builder.branch(fail_blk) field_native = c.unbox(typ, field_obj) c.pyapi.decref(field_obj) with cgutils.if_unlikely(c.builder, field_native.is_error): c.builder.branch(fail_blk) c.builder.store(cgutils.false_bit, is_error_ptr) c.builder.store(field_native.value, ret_ptr) return NativeValue(c.builder.load(ret_ptr), is_error=c.builder.load(is_error_ptr))
def iternext_zip(context, builder, sig, args, result): genty, = sig.args gen, = args impl = context.get_generator_impl(genty) status, retval = impl(context, builder, sig, args) context.add_linking_libs(getattr(impl, 'libs', ())) with cgutils.if_likely(builder, status.is_ok): result.set_valid(True) result.yield_(retval) with cgutils.if_unlikely(builder, status.is_stop_iteration): result.set_exhausted() with cgutils.if_unlikely(builder, builder.and_(status.is_error, builder.not_(status.is_stop_iteration))): context.call_conv.return_status_propagate(builder, status)
def lower_finalize_func_body(self, builder, genptr): """ Lower the body of the generator's finalizer: decref all live state variables. """ pyapi = self.context.get_python_api(builder) resume_index_ptr = self.get_resume_index_ptr(builder, genptr) resume_index = builder.load(resume_index_ptr) # If resume_index is 0, next() was never called # If resume_index is -1, generator terminated cleanly # (note function arguments are saved in state variables, # so they don't need a separate cleanup step) need_cleanup = builder.icmp_signed('>', resume_index, Constant.int(resume_index.type, 0)) with cgutils.if_unlikely(builder, need_cleanup): # Decref all live vars (some may be NULL) gen_state_ptr = self.get_state_ptr(builder, genptr) for state_index in range(len(self.gentype.state_types)): state_slot = cgutils.gep_inbounds(builder, gen_state_ptr, 0, state_index) ty = self.gentype.state_types[state_index] val = self.context.unpack_value(builder, ty, state_slot) pyapi.decref(val) builder.ret_void()
def check_error(self, obj): """ Return if *obj* is NULL. """ with cgutils.if_unlikely(self.builder, self.is_null(obj)): self.return_exception_raised() return obj
def check_int_status(self, num, ok_value=0): """ Raise an exception if *num* is smaller than *ok_value*. """ ok = lc.Constant.int(num.type, ok_value) pred = self.builder.icmp(lc.ICMP_SLT, num, ok) with cgutils.if_unlikely(self.builder, pred): self.return_exception_raised()
def check_occurred(self): """ Return if an exception occurred. """ err_occurred = cgutils.is_not_null(self.builder, self.pyapi.err_occurred()) with cgutils.if_unlikely(self.builder, err_occurred): self.return_exception_raised()
def build_wrapper(self, api, builder, closure, args, kws): nargs = len(self.fndesc.argtypes) objs = [api.alloca_obj() for _ in range(nargs)] parseok = api.unpack_tuple(args, self.fndesc.qualname, nargs, nargs, *objs) pred = builder.icmp(lc.ICMP_EQ, parseok, Constant.null(parseok.type)) with cgutils.if_unlikely(builder, pred): builder.ret(api.get_null_object()) # Block that returns after erroneous argument unboxing/cleanup endblk = builder.append_basic_block("arg.end") with builder.goto_block(endblk): builder.ret(api.get_null_object()) # Get the Environment object env_manager = self.get_env(api, builder) cleanup_manager = _ArgManager(self.context, builder, api, env_manager, endblk, nargs) # Compute the arguments to the compiled Numba function. innerargs = [] for obj, ty in zip(objs, self.fndesc.argtypes): if isinstance(ty, types.Omitted): # It's an omitted value => ignore dummy Python object innerargs.append(None) else: val = cleanup_manager.add_arg(builder.load(obj), ty) innerargs.append(val) if self.release_gil: cleanup_manager = _GilManager(builder, api, cleanup_manager) status, retval = self.context.call_conv.call_function( builder, self.func, self.fndesc.restype, self.fndesc.argtypes, innerargs) # Do clean up self.debug_print(builder, "# callwrapper: emit_cleanup") cleanup_manager.emit_cleanup() self.debug_print(builder, "# callwrapper: emit_cleanup end") # Determine return status with builder.if_then(status.is_ok, likely=True): # Ok => return boxed Python value with builder.if_then(status.is_none): api.return_none() retty = self._simplified_return_type() obj = api.from_native_return(retty, retval, env_manager) builder.ret(obj) # Error out self.context.call_conv.raise_error(builder, api, status) builder.ret(api.get_null_object())
def getrandbits_impl(context, builder, sig, args): (nbits, ) = args too_large = builder.icmp_unsigned(">=", nbits, const_int(65)) too_small = builder.icmp_unsigned("==", nbits, const_int(0)) with cgutils.if_unlikely(builder, builder.or_(too_large, too_small)): msg = "getrandbits() limited to 64 bits" context.call_conv.return_user_exc(builder, OverflowError, (msg, )) state_ptr = get_state_ptr(context, builder, "py") res = get_next_int(context, builder, state_ptr, nbits, False) return impl_ret_untracked(context, builder, sig.return_type, res)
def call_internal(self, builder, fndesc, sig, args): """ Given the function descriptor of an internally compiled function, emit a call to that function with the given arguments. """ status, res = self.call_internal_no_propagate(builder, fndesc, sig, args) with cgutils.if_unlikely(builder, status.is_error): self.call_conv.return_status_propagate(builder, status) res = imputils.fix_returning_optional(self, builder, sig, status, res) return res
def loadvar(self, name): """ Load the llvm value of the variable named *name*. """ # If this raises then the live variables analysis is wrong assert name in self._live_vars, name ptr = self.varmap[name] val = self.builder.load(ptr) with cgutils.if_unlikely(self.builder, self.is_null(val)): self.pyapi.raise_missing_name_error(name) self.return_exception_raised() return val
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 unbox_COO(typ: COOType, obj: COO, c) -> NativeValue: ret_ptr = cgutils.alloca_once(c.builder, c.context.get_value_type(typ)) is_error_ptr = cgutils.alloca_once_value(c.builder, cgutils.false_bit) fail_obj = c.context.get_constant_null(typ) with local_return(c.builder) as ret: fail_blk = c.builder.append_basic_block("fail") with c.builder.goto_block(fail_blk): c.builder.store(cgutils.true_bit, is_error_ptr) c.builder.store(fail_obj, ret_ptr) ret() data = _unbox_native_field(typ.data_type, obj, "data", c) with cgutils.if_unlikely(c.builder, data.is_error): c.builder.branch(fail_blk) coords = _unbox_native_field(typ.coords_type, obj, "coords", c) with cgutils.if_unlikely(c.builder, coords.is_error): c.builder.branch(fail_blk) shape = _unbox_native_field(typ.shape_type, obj, "shape", c) with cgutils.if_unlikely(c.builder, shape.is_error): c.builder.branch(fail_blk) fill_value = _unbox_native_field(typ.fill_value_type, obj, "fill_value", c) with cgutils.if_unlikely(c.builder, fill_value.is_error): c.builder.branch(fail_blk) coo = cgutils.create_struct_proxy(typ)(c.context, c.builder) coo.coords = coords.value coo.data = data.value coo.shape = shape.value coo.fill_value = fill_value.value c.builder.store(cgutils.false_bit, is_error_ptr) c.builder.store(coo._getvalue(), ret_ptr) return NativeValue(c.builder.load(ret_ptr), is_error=c.builder.load(is_error_ptr))
def string_type_to_const(context, builder, fromty, toty, val): # calling str() since the const value can be non-str like tuple const (CSV) cstr = context.insert_const_string(builder.module, str(toty.literal_value)) # check to make sure Const value matches stored string # call str == cstr fnty = lir.FunctionType(lir.IntType(1), [lir.IntType(8).as_pointer(), lir.IntType(8).as_pointer()]) fn = cgutils.get_or_insert_function(builder.module, fnty, name="str_equal_cstr") match = builder.call(fn, [val, cstr]) with cgutils.if_unlikely(builder, builder.not_(match)): # Raise RuntimeError about the assumption violation usermsg = "constant string assumption violated" errmsg = "{}: expecting {}".format(usermsg, toty.literal_value) context.call_conv.return_user_exc(builder, RuntimeError, (errmsg,)) return impl_ret_untracked(context, builder, toty, cstr)
def imp(context, builder, sig, args): func = context.declare_function(builder.module, fndesc) # env=None assumes this is a nopython function status, retval = context.call_conv.call_function( builder, func, fndesc.restype, fndesc.argtypes, args) with cgutils.if_unlikely(builder, status.is_error): context.call_conv.return_status_propagate(builder, status) assert sig.return_type == fndesc.restype # Reconstruct optional return type retval = fix_returning_optional(context, builder, sig, status, retval) # If the data representations don't match up if retval.type != context.get_value_type(sig.return_type): msg = "function returned {0} but expect {1}" raise TypeError(msg.format(retval.type, sig.return_type)) return impl_ret_new_ref(context, builder, fndesc.restype, retval)
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 call_internal(self, builder, fndesc, sig, args): """ Given the function descriptor of an internally compiled function, emit a call to that function with the given arguments. """ # Add call to the generated function llvm_mod = builder.module fn = self.declare_function(llvm_mod, fndesc) status, res = self.call_conv.call_function(builder, fn, sig.return_type, sig.args, args) with cgutils.if_unlikely(builder, status.is_error): self.call_conv.return_status_propagate(builder, status) res = imputils.fix_returning_optional(self, builder, sig, status, res) return res
def add_arg(self, obj, ty): """ Unbox argument and emit code that handles any error during unboxing. Args are cleaned up in reverse order of the parameter list, and cleanup begins as soon as unboxing of any argument fails. E.g. failure on arg2 will result in control flow going through: arg2.err -> arg1.err -> arg0.err -> arg.end (returns) """ # Unbox argument native = self.api.to_native_value(ty, obj) # If an error occurred, go to the cleanup block for # the previous argument with cgutils.if_unlikely(self.builder, native.is_error): self.builder.branch(self.nextblk) # Define the cleanup function for the argument def cleanup_arg(): # Native value reflection self.api.reflect_native_value(ty, native.value, self.env_manager) # Native value cleanup if native.cleanup is not None: native.cleanup() # NRT cleanup # (happens after the native value cleanup as the latter # may need the native value) if self.context.enable_nrt: self.context.nrt.decref(self.builder, ty, native.value) self.cleanups.append(cleanup_arg) # Write the on-error cleanup block for this argument cleanupblk = self.builder.append_basic_block("arg%d.err" % self.arg_count) with self.builder.goto_block(cleanupblk): cleanup_arg() # Go to next cleanup block self.builder.branch(self.nextblk) self.nextblk = cleanupblk self.arg_count += 1 return native.value
def from_range_state(cls, context, builder, state): """ Create a RangeIter initialized from the given RangeState *state*. """ self = cls(context, builder) start = state.start stop = state.stop step = state.step startptr = cgutils.alloca_once(builder, start.type) builder.store(start, startptr) countptr = cgutils.alloca_once(builder, start.type) self.iter = startptr self.stop = stop self.step = step self.count = countptr diff = builder.sub(stop, start) zero = context.get_constant(int_type, 0) one = context.get_constant(int_type, 1) pos_diff = builder.icmp(lc.ICMP_SGT, diff, zero) pos_step = builder.icmp(lc.ICMP_SGT, step, zero) sign_differs = builder.xor(pos_diff, pos_step) zero_step = builder.icmp(lc.ICMP_EQ, step, zero) with cgutils.if_unlikely(builder, zero_step): # step shouldn't be zero context.call_conv.return_user_exc( builder, ValueError, ("range() arg 3 must not be zero", )) with builder.if_else(sign_differs) as (then, orelse): with then: builder.store(zero, self.count) with orelse: rem = builder.srem(diff, step) rem = builder.select(pos_diff, rem, builder.neg(rem)) uneven = builder.icmp(lc.ICMP_SGT, rem, zero) newcount = builder.add(builder.sdiv(diff, step), builder.select(uneven, one, zero)) builder.store(newcount, self.count) return self
def unbox_complex(typ, obj, c): # First unbox to complex128, since that's what CPython gives us c128 = c.context.make_complex(c.builder, types.complex128) ok = c.pyapi.complex_adaptor(obj, c128._getpointer()) failed = cgutils.is_false(c.builder, ok) with cgutils.if_unlikely(c.builder, failed): c.pyapi.err_set_string("PyExc_TypeError", "conversion to %s failed" % (typ,)) if typ == types.complex64: # Downcast to complex64 if necessary cplx = c.context.make_complex(c.builder, typ) cplx.real = c.context.cast(c.builder, c128.real, types.float64, types.float32) cplx.imag = c.context.cast(c.builder, c128.imag, types.float64, types.float32) else: assert typ == types.complex128 cplx = c128 return NativeValue(cplx._getvalue(), is_error=failed)
def call_unresolved(self, builder, name, sig, args): """ Insert a function call to an unresolved symbol with the given *name*. Note: this is used for recursive call. In the mutual recursion case:: @njit def foo(): ... # calls bar() @njit def bar(): ... # calls foo() foo() When foo() is called, the compilation of bar() is fully completed (codegen'ed and loaded) before foo() is. Since MCJIT's eager compilation doesn't allow loading modules with declare-only functions (which is needed for foo() in bar()), the call_unresolved injects a global variable that the "linker" can update even after the module is loaded by MCJIT. The linker would allocate space for the global variable before the bar() module is loaded. When later foo() module is defined, it will update bar()'s reference to foo(). The legacy lazy JIT and the new ORC JIT would allow a declare-only function be used in a module as long as it is defined by the time of its first use. """ # Insert an unresolved reference to the function being called. codegen = self.codegen() fnty = self.call_conv.get_function_type(sig.return_type, sig.args) fn = codegen.insert_unresolved_ref(builder, fnty, name) # Normal call sequence status, res = self.call_conv.call_function(builder, fn, sig.return_type, sig.args, args) with cgutils.if_unlikely(builder, status.is_error): self.call_conv.return_status_propagate(builder, status) res = imputils.fix_returning_optional(self, builder, sig, status, res) return res
def lower_global(self, name, value): """ 1) Check global scope dictionary. 2) Check __builtins__. 2a) is it a dictionary (for non __main__ module) 2b) is it a module (for __main__ module) """ moddict = self.get_module_dict() obj = self.pyapi.dict_getitem(moddict, self._freeze_string(name)) self.incref(obj) # obj is borrowed try: if value in _unsupported_builtins: raise ForbiddenConstruct("builtins %s() is not supported" % name, loc=self.loc) except TypeError: # `value` is unhashable, ignore pass if hasattr(builtins, name): obj_is_null = self.is_null(obj) bbelse = self.builder.basic_block with self.builder.if_then(obj_is_null): mod = self.pyapi.dict_getitem( moddict, self._freeze_string("__builtins__")) builtin = self.builtin_lookup(mod, name) bbif = self.builder.basic_block retval = self.builder.phi(self.pyapi.pyobj) retval.add_incoming(obj, bbelse) retval.add_incoming(builtin, bbif) else: retval = obj with cgutils.if_unlikely(self.builder, self.is_null(retval)): self.pyapi.raise_missing_global_error(name) self.return_exception_raised() return retval
def get_next_int32(context, builder, state_ptr): """ Get the next int32 generated by the PRNG at *state_ptr*. """ idxptr = get_index_ptr(builder, state_ptr) idx = builder.load(idxptr) need_reshuffle = builder.icmp_unsigned(">=", idx, N_const) with cgutils.if_unlikely(builder, need_reshuffle): fn = get_rnd_shuffle(builder) builder.call(fn, (state_ptr, )) builder.store(const_int(0), idxptr) idx = builder.load(idxptr) array_ptr = get_array_ptr(builder, state_ptr) y = builder.load(cgutils.gep_inbounds(builder, array_ptr, 0, idx)) idx = builder.add(idx, const_int(1)) builder.store(idx, idxptr) # Tempering y = builder.xor(y, builder.lshr(y, const_int(11))) y = builder.xor( y, builder.and_(builder.shl(y, const_int(7)), const_int(0x9D2C5680))) y = builder.xor( y, builder.and_(builder.shl(y, const_int(15)), const_int(0xEFC60000))) y = builder.xor(y, builder.lshr(y, const_int(18))) return y
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 _randrange_impl(context, builder, start, stop, step, state): state_ptr = get_state_ptr(context, builder, state) ty = stop.type zero = ir.Constant(ty, 0) one = ir.Constant(ty, 1) nptr = cgutils.alloca_once(builder, ty, name="n") # n = stop - start builder.store(builder.sub(stop, start), nptr) with builder.if_then(builder.icmp_signed("<", step, zero)): # n = (n + step + 1) // step w = builder.add(builder.add(builder.load(nptr), step), one) n = builder.sdiv(w, step) builder.store(n, nptr) with builder.if_then(builder.icmp_signed(">", step, one)): # n = (n + step - 1) // step w = builder.sub(builder.add(builder.load(nptr), step), one) n = builder.sdiv(w, step) builder.store(n, nptr) n = builder.load(nptr) with cgutils.if_unlikely(builder, builder.icmp_signed("<=", n, zero)): # n <= 0 msg = "empty range for randrange()" context.call_conv.return_user_exc(builder, ValueError, (msg, )) fnty = ir.FunctionType(ty, [ty, cgutils.true_bit.type]) fn = builder.function.module.get_or_insert_function( fnty, "llvm.ctlz.%s" % ty) # Since the upper bound is exclusive, we need to subtract one before # calculating the number of bits. This leads to a special case when # n == 1; there's only one possible result, so we don't need bits from # the PRNG. This case is handled separately towards the end of this # function. CPython's implementation is simpler and just runs another # iteration of the while loop when the resulting number is too large # instead of subtracting one, to avoid needing to handle a special # case. Thus, we only perform this subtraction for the NumPy case. nm1 = builder.sub(n, one) if state == "np" else n nbits = builder.trunc(builder.call(fn, [nm1, cgutils.true_bit]), int32_t) nbits = builder.sub(ir.Constant(int32_t, ty.width), nbits) rptr = cgutils.alloca_once(builder, ty, name="r") def get_num(): bbwhile = builder.append_basic_block("while") bbend = builder.append_basic_block("while.end") builder.branch(bbwhile) builder.position_at_end(bbwhile) r = get_next_int(context, builder, state_ptr, nbits, state == "np") r = builder.trunc(r, ty) too_large = builder.icmp_signed(">=", r, n) builder.cbranch(too_large, bbwhile, bbend) builder.position_at_end(bbend) builder.store(r, rptr) if state == "np": # Handle n == 1 case, per previous comment. with builder.if_else(builder.icmp_signed("==", n, one)) as (is_one, is_not_one): with is_one: builder.store(zero, rptr) with is_not_one: get_num() else: get_num() return builder.add(start, builder.mul(builder.load(rptr), step))
def make_wrapper(fname, atypes, rtype, cres, target: TargetInfo, verbose=False): """Make wrapper function to numba compile result. The compilation result contains a function with prototype:: <status> <function name>(<rtype>** result, <arguments>) The make_wrapper adds a wrapper function to compilation result library with the following prototype:: <rtype> <fname>(<arguments>) or, if <rtype>.return_as_first_argument == True, then void <fname>(<rtype>* <arguments>) Parameters ---------- fname : str Function name. atypes : tuple A tuple of argument Numba types. rtype : numba.Type The return Numba type cres : CompileResult Numba compilation result. verbose: bool When True, insert printf statements for debugging errors. For CUDA target this feature is disabled. """ fndesc = cres.fndesc module = cres.library.create_ir_module(fndesc.unique_name) context = cres.target_context ll_argtypes = [context.get_value_type(ty) for ty in atypes] ll_return_type = context.get_value_type(rtype) return_as_first_argument = getattr(rtype, 'return_as_first_argument', False) assert isinstance(return_as_first_argument, bool) if return_as_first_argument: wrapty = ir.FunctionType(ir.VoidType(), [ll_return_type] + ll_argtypes) wrapfn = ir.Function(module, wrapty, fname) builder = ir.IRBuilder(wrapfn.append_basic_block('entry')) fnty = context.call_conv.get_function_type(rtype, atypes) fn = ir.Function(builder.module, fnty, cres.fndesc.llvm_func_name) status, out = context.call_conv.call_function(builder, fn, rtype, atypes, wrapfn.args[1:]) with cgutils.if_unlikely(builder, status.is_error): if verbose and target.is_cpu: cgutils.printf(builder, f"rbc: {fname} failed with status code %i\n", status.code) externals.stdio._cg_fflush(builder) builder.ret_void() builder.store(builder.load(out), wrapfn.args[0]) builder.ret_void() else: wrapty = ir.FunctionType(ll_return_type, ll_argtypes) wrapfn = ir.Function(module, wrapty, fname) builder = ir.IRBuilder(wrapfn.append_basic_block('entry')) fnty = context.call_conv.get_function_type(rtype, atypes) fn = ir.Function(builder.module, fnty, cres.fndesc.llvm_func_name) status, out = context.call_conv.call_function(builder, fn, rtype, atypes, wrapfn.args) if verbose and target.is_cpu: with cgutils.if_unlikely(builder, status.is_error): cgutils.printf(builder, f"rbc: {fname} failed with status code %i\n", status.code) externals.stdio._cg_fflush(builder) builder.ret(out) cres.library.add_ir_module(module)
def _build_array(context, builder, array_ty, input_types, inputs): """Utility function to handle allocation of an implicit output array given the target context, builder, output array type, and a list of _ArrayHelper instances. """ # First, strip optional types, ufunc loops are typed on concrete types input_types = [ x.type if isinstance(x, types.Optional) else x for x in input_types ] intp_ty = context.get_value_type(types.intp) def make_intp_const(val): return context.get_constant(types.intp, val) ZERO = make_intp_const(0) ONE = make_intp_const(1) src_shape = cgutils.alloca_once(builder, intp_ty, array_ty.ndim, "src_shape") dest_ndim = make_intp_const(array_ty.ndim) dest_shape = cgutils.alloca_once(builder, intp_ty, array_ty.ndim, "dest_shape") dest_shape_addrs = tuple( cgutils.gep_inbounds(builder, dest_shape, index) for index in range(array_ty.ndim)) # Initialize the destination shape with all ones. for dest_shape_addr in dest_shape_addrs: builder.store(ONE, dest_shape_addr) # For each argument, try to broadcast onto the destination shape, # mutating along any axis where the argument shape is not one and # the destination shape is one. for arg_number, arg in enumerate(inputs): if not hasattr(arg, "ndim"): # Skip scalar arguments continue arg_ndim = make_intp_const(arg.ndim) for index in range(arg.ndim): builder.store(arg.shape[index], cgutils.gep_inbounds(builder, src_shape, index)) arg_result = context.compile_internal( builder, _broadcast_onto, _broadcast_onto_sig, [arg_ndim, src_shape, dest_ndim, dest_shape]) with cgutils.if_unlikely(builder, builder.icmp(lc.ICMP_SLT, arg_result, ONE)): msg = "unable to broadcast argument %d to output array" % ( arg_number, ) loc = errors.loc_info.get('loc', None) if loc is not None: msg += '\nFile "%s", line %d, ' % (loc.filename, loc.line) context.call_conv.return_user_exc(builder, ValueError, (msg, )) real_array_ty = array_ty.as_array dest_shape_tup = tuple( builder.load(dest_shape_addr) for dest_shape_addr in dest_shape_addrs) array_val = arrayobj._empty_nd_impl(context, builder, real_array_ty, dest_shape_tup) # Get the best argument to call __array_wrap__ on array_wrapper_index = select_array_wrapper(input_types) array_wrapper_ty = input_types[array_wrapper_index] try: # __array_wrap__(source wrapped array, out array) -> out wrapped array array_wrap = context.get_function( '__array_wrap__', array_ty(array_wrapper_ty, real_array_ty)) except NotImplementedError: # If it's the same priority as a regular array, assume we # should use the allocated array unchanged. if array_wrapper_ty.array_priority != types.Array.array_priority: raise out_val = array_val._getvalue() else: wrap_args = (inputs[array_wrapper_index].return_val, array_val._getvalue()) out_val = array_wrap(builder, wrap_args) ndim = array_ty.ndim shape = cgutils.unpack_tuple(builder, array_val.shape, ndim) strides = cgutils.unpack_tuple(builder, array_val.strides, ndim) return _ArrayHelper(context, builder, shape, strides, array_val.data, array_ty.layout, array_ty.dtype, ndim, out_val)
def lower_expr(self, resty, expr): if expr.op == 'binop': return self.lower_binop(resty, expr, expr.fn) elif expr.op == 'inplace_binop': lty = self.typeof(expr.lhs.name) if lty.mutable: return self.lower_binop(resty, expr, expr.fn) else: # inplace operators on non-mutable types reuse the same # definition as the corresponding copying operators.) return self.lower_binop(resty, expr, expr.immutable_fn) elif expr.op == 'unary': val = self.loadvar(expr.value.name) typ = self.typeof(expr.value.name) func_ty = self.context.typing_context.resolve_value_type(expr.fn) # Get function signature = self.fndesc.calltypes[expr] impl = self.context.get_function(func_ty, signature) # Convert argument to match val = self.context.cast(self.builder, val, typ, signature.args[0]) res = impl(self.builder, [val]) res = self.context.cast(self.builder, res, signature.return_type, resty) return res elif expr.op == 'call': res = self.lower_call(resty, expr) return res elif expr.op == 'pair_first': val = self.loadvar(expr.value.name) ty = self.typeof(expr.value.name) res = self.context.pair_first(self.builder, val, ty) self.incref(resty, res) return res elif expr.op == 'pair_second': val = self.loadvar(expr.value.name) ty = self.typeof(expr.value.name) res = self.context.pair_second(self.builder, val, ty) self.incref(resty, res) return res elif expr.op in ('getiter', 'iternext'): val = self.loadvar(expr.value.name) ty = self.typeof(expr.value.name) signature = self.fndesc.calltypes[expr] impl = self.context.get_function(expr.op, signature) [fty] = signature.args castval = self.context.cast(self.builder, val, ty, fty) res = impl(self.builder, (castval, )) res = self.context.cast(self.builder, res, signature.return_type, resty) return res elif expr.op == 'exhaust_iter': val = self.loadvar(expr.value.name) ty = self.typeof(expr.value.name) # Unpack optional if isinstance(ty, types.Optional): val = self.context.cast(self.builder, val, ty, ty.type) ty = ty.type # If we have a tuple, we needn't do anything # (and we can't iterate over the heterogeneous ones). if isinstance(ty, types.BaseTuple): assert ty == resty self.incref(ty, val) return val itemty = ty.iterator_type.yield_type tup = self.context.get_constant_undef(resty) pairty = types.Pair(itemty, types.boolean) getiter_sig = typing.signature(ty.iterator_type, ty) getiter_impl = self.context.get_function('getiter', getiter_sig) iternext_sig = typing.signature(pairty, ty.iterator_type) iternext_impl = self.context.get_function('iternext', iternext_sig) iterobj = getiter_impl(self.builder, (val, )) # We call iternext() as many times as desired (`expr.count`). for i in range(expr.count): pair = iternext_impl(self.builder, (iterobj, )) is_valid = self.context.pair_second(self.builder, pair, pairty) with cgutils.if_unlikely(self.builder, self.builder.not_(is_valid)): self.return_exception(ValueError, loc=self.loc) item = self.context.pair_first(self.builder, pair, pairty) tup = self.builder.insert_value(tup, item, i) # Call iternext() once more to check that the iterator # is exhausted. pair = iternext_impl(self.builder, (iterobj, )) is_valid = self.context.pair_second(self.builder, pair, pairty) with cgutils.if_unlikely(self.builder, is_valid): self.return_exception(ValueError, loc=self.loc) self.decref(ty.iterator_type, iterobj) return tup elif expr.op == "getattr": val = self.loadvar(expr.value.name) ty = self.typeof(expr.value.name) if isinstance(resty, types.BoundFunction): # if we are getting out a method, assume we have typed this # properly and just build a bound function object casted = self.context.cast(self.builder, val, ty, resty.this) res = self.context.get_bound_function(self.builder, casted, resty.this) self.incref(resty, res) return res else: impl = self.context.get_getattr(ty, expr.attr) attrty = self.context.typing_context.resolve_getattr( ty, expr.attr) if impl is None: # ignore the attribute return self.context.get_dummy_value() else: res = impl(self.context, self.builder, ty, val, expr.attr) # Cast the attribute type to the expected output type res = self.context.cast(self.builder, res, attrty, resty) return res elif expr.op == "static_getitem": signature = typing.signature( resty, self.typeof(expr.value.name), _lit_or_omitted(expr.index), ) try: # Both get_function() and the returned implementation can # raise NotImplementedError if the types aren't supported impl = self.context.get_function("static_getitem", signature) return impl(self.builder, (self.loadvar(expr.value.name), expr.index)) except NotImplementedError: if expr.index_var is None: raise # Fall back on the generic getitem() implementation # for this type. signature = self.fndesc.calltypes[expr] return self.lower_getitem(resty, expr, expr.value, expr.index_var, signature) elif expr.op == "typed_getitem": signature = typing.signature( resty, self.typeof(expr.value.name), self.typeof(expr.index.name), ) impl = self.context.get_function("typed_getitem", signature) return impl( self.builder, (self.loadvar(expr.value.name), self.loadvar(expr.index.name))) elif expr.op == "getitem": signature = self.fndesc.calltypes[expr] return self.lower_getitem(resty, expr, expr.value, expr.index, signature) elif expr.op == "build_tuple": itemvals = [self.loadvar(i.name) for i in expr.items] itemtys = [self.typeof(i.name) for i in expr.items] castvals = [ self.context.cast(self.builder, val, fromty, toty) for val, toty, fromty in zip(itemvals, resty, itemtys) ] tup = self.context.make_tuple(self.builder, resty, castvals) self.incref(resty, tup) return tup elif expr.op == "build_list": itemvals = [self.loadvar(i.name) for i in expr.items] itemtys = [self.typeof(i.name) for i in expr.items] castvals = [ self.context.cast(self.builder, val, fromty, resty.dtype) for val, fromty in zip(itemvals, itemtys) ] return self.context.build_list(self.builder, resty, castvals) elif expr.op == "build_set": # Insert in reverse order, as Python does items = expr.items[::-1] itemvals = [self.loadvar(i.name) for i in items] itemtys = [self.typeof(i.name) for i in items] castvals = [ self.context.cast(self.builder, val, fromty, resty.dtype) for val, fromty in zip(itemvals, itemtys) ] return self.context.build_set(self.builder, resty, castvals) elif expr.op == "build_map": items = expr.items keys, values = [], [] key_types, value_types = [], [] for k, v in items: key = self.loadvar(k.name) keytype = self.typeof(k.name) val = self.loadvar(v.name) valtype = self.typeof(v.name) keys.append(key) values.append(val) key_types.append(keytype) value_types.append(valtype) return self.context.build_map(self.builder, resty, list(zip(key_types, value_types)), list(zip(keys, values))) elif expr.op == "cast": val = self.loadvar(expr.value.name) ty = self.typeof(expr.value.name) castval = self.context.cast(self.builder, val, ty, resty) self.incref(resty, castval) return castval elif expr.op in self.context.special_ops: res = self.context.special_ops[expr.op](self, expr) return res raise NotImplementedError(expr)
def build_wrapper(self, api, builder, closure, args, kws): nargs = len(self.fndesc.argtypes) objs = [api.alloca_obj() for _ in range(nargs)] parseok = api.unpack_tuple(args, self.fndesc.qualname, nargs, nargs, *objs) pred = builder.icmp(lc.ICMP_EQ, parseok, Constant.null(parseok.type)) with cgutils.if_unlikely(builder, pred): builder.ret(api.get_null_object()) # Block that returns after erroneous argument unboxing/cleanup endblk = builder.append_basic_block("arg.end") with builder.goto_block(endblk): builder.ret(api.get_null_object()) # Get the Environment object env_manager = self.get_env(api, builder) cleanup_manager = _ArgManager(self.context, builder, api, env_manager, endblk, nargs) # Compute the arguments to the compiled Numba function. innerargs = [] for obj, ty in zip(objs, self.fndesc.argtypes): if isinstance(ty, types.Omitted): # It's an omitted value => ignore dummy Python object innerargs.append(None) else: val = cleanup_manager.add_arg(builder.load(obj), ty) innerargs.append(val) if self.release_gil: cleanup_manager = _GilManager(builder, api, cleanup_manager) # We elect to not inline the top level user function into the call # wrapper, this incurs an overhead of a function call, however, it # increases optimisation stability in that the optimised user function # is what will actually be run and it is this function that all the # inspection tools "see". Further, this makes optimisation "stable" in # that calling the user function from e.g. C or from this wrapper will # result in the same code executing, were inlining permitted this may # not be the case as the inline could trigger additional optimisation # as the function goes into the wrapper, this resulting in the executing # instruction stream being different from that of the instruction stream # present in the user function. status, retval = self.context.call_conv.call_function( builder, self.func, self.fndesc.restype, self.fndesc.argtypes, innerargs, attrs=('noinline', )) # Do clean up self.debug_print(builder, "# callwrapper: emit_cleanup") cleanup_manager.emit_cleanup() self.debug_print(builder, "# callwrapper: emit_cleanup end") # Determine return status with builder.if_then(status.is_ok, likely=True): # Ok => return boxed Python value with builder.if_then(status.is_none): api.return_none() retty = self._simplified_return_type() obj = api.from_native_return(retty, retval, env_manager) builder.ret(obj) # Error out self.context.call_conv.raise_error(builder, api, status) builder.ret(api.get_null_object())
def box_COO(typ: COOType, val: "some LLVM thing", c) -> COO: ret_ptr = cgutils.alloca_once(c.builder, c.pyapi.pyobj) fail_obj = c.pyapi.get_null_object() coo = cgutils.create_struct_proxy(typ)(c.context, c.builder, value=val) with local_return(c.builder) as ret: data_obj = c.box(typ.data_type, coo.data) with cgutils.if_unlikely(c.builder, cgutils.is_null(c.builder, data_obj)): c.builder.store(fail_obj, ret_ptr) ret() coords_obj = c.box(typ.coords_type, coo.coords) with cgutils.if_unlikely(c.builder, cgutils.is_null(c.builder, coords_obj)): c.pyapi.decref(data_obj) c.builder.store(fail_obj, ret_ptr) ret() shape_obj = c.box(typ.shape_type, coo.shape) with cgutils.if_unlikely(c.builder, cgutils.is_null(c.builder, shape_obj)): c.pyapi.decref(coords_obj) c.pyapi.decref(data_obj) c.builder.store(fail_obj, ret_ptr) ret() fill_value_obj = c.box(typ.fill_value_type, coo.fill_value) with cgutils.if_unlikely(c.builder, cgutils.is_null(c.builder, fill_value_obj)): c.pyapi.decref(shape_obj) c.pyapi.decref(coords_obj) c.pyapi.decref(data_obj) c.builder.store(fail_obj, ret_ptr) ret() class_obj = c.pyapi.unserialize(c.pyapi.serialize_object(COO)) with cgutils.if_unlikely(c.builder, cgutils.is_null(c.builder, class_obj)): c.pyapi.decref(shape_obj) c.pyapi.decref(coords_obj) c.pyapi.decref(data_obj) c.pyapi.decref(fill_value_obj) c.builder.store(fail_obj, ret_ptr) ret() args = c.pyapi.tuple_pack([coords_obj, data_obj, shape_obj]) c.pyapi.decref(shape_obj) c.pyapi.decref(coords_obj) c.pyapi.decref(data_obj) with cgutils.if_unlikely(c.builder, cgutils.is_null(c.builder, args)): c.pyapi.decref(fill_value_obj) c.pyapi.decref(class_obj) c.builder.store(fail_obj, ret_ptr) ret() kwargs = c.pyapi.dict_pack([("fill_value", fill_value_obj)]) c.pyapi.decref(fill_value_obj) with cgutils.if_unlikely(c.builder, cgutils.is_null(c.builder, kwargs)): c.pyapi.decref(class_obj) c.builder.store(fail_obj, ret_ptr) ret() c.builder.store(c.pyapi.call(class_obj, args, kwargs), ret_ptr) c.pyapi.decref(class_obj) c.pyapi.decref(args) c.pyapi.decref(kwargs) return c.builder.load(ret_ptr)