def lower_dist_arr_reduce(context, builder, sig, args): op_typ = args[1].type # store an int to specify data type typ_enum = _h5_typ_table[sig.args[0].dtype] typ_arg = cgutils.alloca_once_value( builder, lir.Constant(lir.IntType(32), typ_enum)) ndims = sig.args[0].ndim out = make_array(sig.args[0])(context, builder, args[0]) # store size vars array struct to pointer size_ptr = cgutils.alloca_once(builder, out.shape.type) builder.store(out.shape, size_ptr) size_arg = builder.bitcast(size_ptr, lir.IntType(64).as_pointer()) ndim_arg = cgutils.alloca_once_value( builder, lir.Constant(lir.IntType(32), sig.args[0].ndim)) call_args = [builder.bitcast(out.data, lir.IntType(8).as_pointer()), size_arg, builder.load(ndim_arg), args[1], builder.load(typ_arg)] # array, shape, ndim, extra last arg type for type enum arg_typs = [lir.IntType(8).as_pointer(), lir.IntType(64).as_pointer(), lir.IntType(32), op_typ, lir.IntType(32)] fnty = lir.FunctionType(lir.IntType(32), arg_typs) fn = builder.module.get_or_insert_function( fnty, name="hpat_dist_arr_reduce") builder.call(fn, call_args) res = out._getvalue() return impl_ret_borrowed(context, builder, sig.return_type, res)
def codegen(context, builder, sig, args): fnty = ir.FunctionType( ll_status, [ll_dict_type, ll_bytes, ll_hash, ll_bytes, ll_bytes], ) [d, key, hashval, val] = args [td, tkey, thashval, tval] = sig.args fn = builder.module.get_or_insert_function(fnty, name='numba_dict_insert') dm_key = context.data_model_manager[tkey] dm_val = context.data_model_manager[tval] data_key = dm_key.as_data(builder, key) data_val = dm_val.as_data(builder, val) ptr_key = cgutils.alloca_once_value(builder, data_key) ptr_val = cgutils.alloca_once_value(builder, data_val) # TODO: the ptr_oldval is not used. needed for refct ptr_oldval = cgutils.alloca_once(builder, data_val.type) dp = _dict_get_data(context, builder, td, d) status = builder.call( fn, [ dp, _as_bytes(builder, ptr_key), hashval, _as_bytes(builder, ptr_val), _as_bytes(builder, ptr_oldval), ], ) return status
def init_specific(self, context, builder, arrty, arr): zero = context.get_constant(types.intp, 0) self.index = cgutils.alloca_once_value(builder, zero) self.pointer = cgutils.alloca_once_value(builder, arr.data) # We can't trust strides[-1] to always contain the right # step value, see # http://docs.scipy.org/doc/numpy-dev/release.html#npy-relaxed-strides-checking self.stride = arr.itemsize
def array_nonzero(context, builder, sig, args): aryty = sig.args[0] # Return type is a N-tuple of 1D C-contiguous arrays retty = sig.return_type outaryty = retty.dtype ndim = aryty.ndim nouts = retty.count ary = make_array(aryty)(context, builder, args[0]) shape = cgutils.unpack_tuple(builder, ary.shape) strides = cgutils.unpack_tuple(builder, ary.strides) data = ary.data layout = aryty.layout # First count the number of non-zero elements zero = context.get_constant(types.intp, 0) one = context.get_constant(types.intp, 1) count = cgutils.alloca_once_value(builder, zero) with cgutils.loop_nest(builder, shape, zero.type) as indices: ptr = cgutils.get_item_pointer2(builder, data, shape, strides, layout, indices) val = load_item(context, builder, aryty, ptr) nz = context.is_true(builder, aryty.dtype, val) with builder.if_then(nz): builder.store(builder.add(builder.load(count), one), count) # Then allocate output arrays of the right size out_shape = (builder.load(count), ) outs = [ _empty_nd_impl(context, builder, outaryty, out_shape)._getvalue() for i in range(nouts) ] outarys = [make_array(outaryty)(context, builder, out) for out in outs] out_datas = [out.data for out in outarys] # And fill them up index = cgutils.alloca_once_value(builder, zero) with cgutils.loop_nest(builder, shape, zero.type) as indices: ptr = cgutils.get_item_pointer2(builder, data, shape, strides, layout, indices) val = load_item(context, builder, aryty, ptr) nz = context.is_true(builder, aryty.dtype, val) with builder.if_then(nz): # Store element indices in output arrays if not indices: # For a 0-d array, store 0 in the unique output array indices = (zero, ) cur = builder.load(index) for i in range(nouts): ptr = cgutils.get_item_pointer2(builder, out_datas[i], out_shape, (), 'C', [cur]) store_item(context, builder, outaryty, indices[i], ptr) builder.store(builder.add(cur, one), index) tup = context.make_tuple(builder, sig.return_type, outs) return impl_ret_new_ref(context, builder, sig.return_type, tup)
def array_nonzero(context, builder, sig, args): aryty = sig.args[0] # Return type is a N-tuple of 1D C-contiguous arrays retty = sig.return_type outaryty = retty.dtype ndim = aryty.ndim nouts = retty.count ary = make_array(aryty)(context, builder, args[0]) shape = cgutils.unpack_tuple(builder, ary.shape) strides = cgutils.unpack_tuple(builder, ary.strides) data = ary.data layout = aryty.layout # First count the number of non-zero elements zero = context.get_constant(types.intp, 0) one = context.get_constant(types.intp, 1) count = cgutils.alloca_once_value(builder, zero) with cgutils.loop_nest(builder, shape, zero.type) as indices: ptr = cgutils.get_item_pointer2(builder, data, shape, strides, layout, indices) val = load_item(context, builder, aryty, ptr) nz = context.is_true(builder, aryty.dtype, val) with builder.if_then(nz): builder.store(builder.add(builder.load(count), one), count) # Then allocate output arrays of the right size out_shape = (builder.load(count),) outs = [_empty_nd_impl(context, builder, outaryty, out_shape)._getvalue() for i in range(nouts)] outarys = [make_array(outaryty)(context, builder, out) for out in outs] out_datas = [out.data for out in outarys] # And fill them up index = cgutils.alloca_once_value(builder, zero) with cgutils.loop_nest(builder, shape, zero.type) as indices: ptr = cgutils.get_item_pointer2(builder, data, shape, strides, layout, indices) val = load_item(context, builder, aryty, ptr) nz = context.is_true(builder, aryty.dtype, val) with builder.if_then(nz): # Store element indices in output arrays if not indices: # For a 0-d array, store 0 in the unique output array indices = (zero,) cur = builder.load(index) for i in range(nouts): ptr = cgutils.get_item_pointer2(builder, out_datas[i], out_shape, (), 'C', [cur]) store_item(context, builder, outaryty, indices[i], ptr) builder.store(builder.add(cur, one), index) tup = context.make_tuple(builder, sig.return_type, outs) return impl_ret_new_ref(context, builder, sig.return_type, tup)
def to_native_optional(self, obj, typ): """ Convert object *obj* to a native optional structure. """ noneval = self.context.make_optional_none(self.builder, typ.type) is_not_none = self.builder.icmp(lc.ICMP_NE, obj, self.borrow_none()) retptr = cgutils.alloca_once(self.builder, noneval.type) errptr = cgutils.alloca_once_value(self.builder, cgutils.false_bit) with cgutils.ifelse(self.builder, is_not_none) as (then, orelse): with then: native = self.to_native_value(obj, typ.type) just = self.context.make_optional_value(self.builder, typ.type, native.value) self.builder.store(just, retptr) self.builder.store(native.is_error, errptr) with orelse: self.builder.store(ir.Constant(noneval.type, ir.Undefined), retptr) self.builder.store(noneval, retptr) if native.cleanup is not None: def cleanup(): with cgutils.ifthen(self.builder, is_not_none): native.cleanup() else: cleanup = None ret = self.builder.load(retptr) return NativeValue(ret, is_error=self.builder.load(errptr), cleanup=cleanup)
def downsize(self, nitems): """ When removing from the set, ensure it is properly sized for the given number of used entries. """ context = self._context builder = self._builder intp_t = nitems.type one = ir.Constant(intp_t, 1) two = ir.Constant(intp_t, 2) minsize = ir.Constant(intp_t, MINSIZE) payload = self.payload # Ensure entries >= max(2 * used, MINSIZE) min_entries = builder.shl(nitems, one) min_entries = builder.select( builder.icmp_unsigned('>=', min_entries, minsize), min_entries, minsize) # Shrink only if size >= 4 * min_entries && size > MINSIZE max_size = builder.shl(min_entries, two) size = builder.add(payload.mask, one) need_resize = builder.and_(builder.icmp_unsigned('<=', max_size, size), builder.icmp_unsigned('<', minsize, size)) with builder.if_then(need_resize, likely=False): # Find out next suitable size new_size_p = cgutils.alloca_once_value(builder, size) bb_body = builder.append_basic_block("calcsize.body") bb_end = builder.append_basic_block("calcsize.end") builder.branch(bb_body) with builder.goto_block(bb_body): # Divide by 2 (ensuring size remains a power of two) new_size = builder.load(new_size_p) new_size = builder.lshr(new_size, one) # Keep current size if new size would be < min_entries is_too_small = builder.icmp_unsigned('>', min_entries, new_size) with builder.if_then(is_too_small): builder.branch(bb_end) builder.store(new_size, new_size_p) builder.branch(bb_body) builder.position_at_end(bb_end) # Ensure new_size >= MINSIZE new_size = builder.load(new_size_p) # At this point, new_size should be < size if the factors # above were chosen carefully! if DEBUG_ALLOCS: context.printf( builder, "downsize to %zd items: current size = %zd, " "min entries = %zd, new size = %zd\n", nitems, size, min_entries, new_size) self._resize(payload, new_size, "cannot shrink set")
def choose_alloc_size(cls, context, builder, nitems): """ Choose a suitable number of entries for the given number of items. """ intp_t = nitems.type one = ir.Constant(intp_t, 1) minsize = ir.Constant(intp_t, MINSIZE) # Ensure number of entries >= 2 * used min_entries = builder.shl(nitems, one) # Find out first suitable power of 2, starting from MINSIZE size_p = cgutils.alloca_once_value(builder, minsize) bb_body = builder.append_basic_block("calcsize.body") bb_end = builder.append_basic_block("calcsize.end") builder.branch(bb_body) with builder.goto_block(bb_body): size = builder.load(size_p) is_large_enough = builder.icmp_unsigned('>=', size, min_entries) with builder.if_then(is_large_enough, likely=False): builder.branch(bb_end) next_size = builder.shl(size, one) builder.store(next_size, size_p) builder.branch(bb_body) builder.position_at_end(bb_end) return builder.load(size_p)
def isdisjoint(self, other): context = self._context builder = self._builder payload = self.payload other_payload = other.payload res = cgutils.alloca_once_value(builder, cgutils.true_bit) def check(smaller, larger): # Loop over the smaller of the two, and search in the larger with smaller._iterate() as loop: entry = loop.entry found, _ = larger._lookup(entry.key, entry.hash) with builder.if_then(found): builder.store(cgutils.false_bit, res) loop.do_break() with builder.if_else( builder.icmp_unsigned('>', payload.used, other_payload.used)) as (if_larger, otherwise): with if_larger: # len(self) > len(other) check(other_payload, payload) with otherwise: # len(self) <= len(other) check(payload, other_payload) return builder.load(res)
def issubset(self, other, strict=False): context = self._context builder = self._builder payload = self.payload other_payload = other.payload cmp_op = '<' if strict else '<=' res = cgutils.alloca_once_value(builder, cgutils.true_bit) with builder.if_else( builder.icmp_unsigned(cmp_op, payload.used, other_payload.used)) as (if_smaller, if_larger): with if_larger: # self larger than other => self cannot possibly a subset builder.store(cgutils.false_bit, res) with if_smaller: # check whether each key of self is in other with payload._iterate() as loop: entry = loop.entry found, _ = other_payload._lookup(entry.key, entry.hash) with builder.if_then(builder.not_(found)): builder.store(cgutils.false_bit, res) loop.do_break() return builder.load(res)
def codegen(context, builder, sig, args): out_str_arr, in_str_arr = args in_string_array = context.make_helper(builder, string_array_type, in_str_arr) out_string_array = context.make_helper(builder, string_array_type, out_str_arr) n = in_string_array.num_items zero = context.get_constant(offset_typ, 0) curr_offset_ptr = cgutils.alloca_once_value(builder, zero) # XXX: assuming last offset is already set by allocate_string_array # for i in range(n) # if not isna(): # out_offset[curr] = offset[i] with cgutils.for_range(builder, n) as loop: isna = lower_is_na(context, builder, in_string_array.null_bitmap, loop.index) with cgutils.if_likely(builder, builder.not_(isna)): in_val = builder.load( builder.gep(in_string_array.offsets, [loop.index])) curr_offset = builder.load(curr_offset_ptr) builder.store( in_val, builder.gep(out_string_array.offsets, [curr_offset])) builder.store( builder.add( curr_offset, lir.Constant(context.get_data_type(offset_typ), 1)), curr_offset_ptr) return context.get_dummy_value()
def lower_dist_quantile(context, builder, sig, args): # store an int to specify data type typ_enum = _h5_typ_table[sig.args[0].dtype] typ_arg = cgutils.alloca_once_value(builder, lir.Constant(lir.IntType(32), typ_enum)) assert sig.args[0].ndim == 1 arr = make_array(sig.args[0])(context, builder, args[0]) local_size = builder.extract_value(arr.shape, 0) if len(args) == 3: total_size = args[2] else: # sequential case total_size = local_size call_args = [builder.bitcast(arr.data, lir.IntType(8).as_pointer()), local_size, total_size, args[1], builder.load(typ_arg)] # array, size, total_size, quantile, type enum arg_typs = [lir.IntType(8).as_pointer(), lir.IntType(64), lir.IntType(64), lir.DoubleType(), lir.IntType(32)] fnty = lir.FunctionType(lir.DoubleType(), arg_typs) fn = builder.module.get_or_insert_function(fnty, name="quantile_parallel") return builder.call(fn, call_args)
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 lower_get_sendrecv_counts(context, builder, sig, args): # prepare buffer args pointer_to_cbuffer_typ = lir.IntType(8).as_pointer().as_pointer() send_counts = cgutils.alloca_once(builder, lir.IntType(8).as_pointer()) recv_counts = cgutils.alloca_once(builder, lir.IntType(8).as_pointer()) send_disp = cgutils.alloca_once(builder, lir.IntType(8).as_pointer()) recv_disp = cgutils.alloca_once(builder, lir.IntType(8).as_pointer()) # prepare key array args key_arr = make_array(sig.args[0])(context, builder, args[0]) # XXX: assuming key arr is 1D assert key_arr.shape.type.count == 1 arr_len = builder.extract_value(key_arr.shape, 0) # TODO: extend to other key types assert sig.args[0].dtype == types.intp key_typ_enum = _h5_typ_table[sig.args[0].dtype] key_typ_arg = builder.load(cgutils.alloca_once_value(builder, lir.Constant(lir.IntType(32), key_typ_enum))) key_arr_data = builder.bitcast(key_arr.data, lir.IntType(8).as_pointer()) call_args = [send_counts, recv_counts, send_disp, recv_disp, arr_len, key_typ_arg, key_arr_data] fnty = lir.FunctionType(lir.IntType(64), [pointer_to_cbuffer_typ] * 4 + [lir.IntType(64), lir.IntType(32), lir.IntType(8).as_pointer()]) fn = builder.module.get_or_insert_function(fnty, name="get_join_sendrecv_counts") total_size = builder.call(fn, call_args) items = [builder.load(send_counts), builder.load(recv_counts), builder.load(send_disp), builder.load(recv_disp), total_size] out_tuple_typ = types.Tuple([c_buffer_type, c_buffer_type, c_buffer_type, c_buffer_type, types.intp]) return context.make_tuple(builder, out_tuple_typ, items)
def lower_dist_reduce(context, builder, sig, args): val_typ = args[0].type op_typ = args[1].type target_typ = sig.args[0] if isinstance(target_typ, IndexValueType): target_typ = target_typ.val_typ supported_typs = [types.int32, types.float32, types.float64] import sys if not sys.platform.startswith('win'): # long is 4 byte on Windows supported_typs.append(types.int64) if target_typ not in supported_typs: # pragma: no cover raise TypeError("argmin/argmax not supported for type {}".format( target_typ)) in_ptr = cgutils.alloca_once(builder, val_typ) out_ptr = cgutils.alloca_once(builder, val_typ) builder.store(args[0], in_ptr) # cast to char * in_ptr = builder.bitcast(in_ptr, lir.IntType(8).as_pointer()) out_ptr = builder.bitcast(out_ptr, lir.IntType(8).as_pointer()) typ_enum = _h5_typ_table[target_typ] typ_arg = cgutils.alloca_once_value(builder, lir.Constant(lir.IntType(32), typ_enum)) fnty = lir.FunctionType(lir.VoidType(), [lir.IntType(8).as_pointer(), lir.IntType(8).as_pointer(), op_typ, lir.IntType(32)]) fn = builder.module.get_or_insert_function(fnty, name="hpat_dist_reduce") builder.call(fn, [in_ptr, out_ptr, args[1], builder.load(typ_arg)]) # cast back to value type out_ptr = builder.bitcast(out_ptr, val_typ.as_pointer()) return builder.load(out_ptr)
def from_list(cls, context, builder, iter_type, list_val): list_inst = ListInstance(context, builder, iter_type.container, list_val) self = cls(context, builder, iter_type, None) index = context.get_constant(types.intp, 0) self._iter.index = cgutils.alloca_once_value(builder, index) self._iter.meminfo = list_inst.meminfo return self
def issubset(self, other, strict=False): context = self._context builder = self._builder payload = self.payload other_payload = other.payload cmp_op = '<' if strict else '<=' res = cgutils.alloca_once_value(builder, cgutils.true_bit) with builder.if_else( builder.icmp_unsigned(cmp_op, payload.used, other_payload.used) ) as (if_smaller, if_larger): with if_larger: # self larger than other => self cannot possibly a subset builder.store(cgutils.false_bit, res) with if_smaller: # check whether each key of self is in other with payload._iterate() as loop: entry = loop.entry found, _ = other_payload._lookup(entry.key, entry.hash) with builder.if_then(builder.not_(found)): builder.store(cgutils.false_bit, res) loop.do_break() return builder.load(res)
def init_specific(self, context, builder, arrty, arr): zero = context.get_constant(types.intp, 0) data = arr.data ndim = arrty.ndim shapes = cgutils.unpack_tuple(builder, arr.shape, ndim) indices = cgutils.alloca_once(builder, zero.type, size=context.get_constant(types.intp, arrty.ndim)) pointers = cgutils.alloca_once(builder, data.type, size=context.get_constant(types.intp, arrty.ndim)) strides = cgutils.unpack_tuple(builder, arr.strides, ndim) exhausted = cgutils.alloca_once_value(builder, cgutils.false_byte) # Initialize indices and pointers with their start values. for dim in range(ndim): idxptr = cgutils.gep(builder, indices, dim) ptrptr = cgutils.gep(builder, pointers, dim) builder.store(data, ptrptr) builder.store(zero, idxptr) # 0-sized dimensions really indicate an empty array, # but we have to catch that condition early to avoid # a bug inside the iteration logic (see issue #846). dim_size = shapes[dim] dim_is_empty = builder.icmp(lc.ICMP_EQ, dim_size, zero) with cgutils.if_unlikely(builder, dim_is_empty): builder.store(cgutils.true_byte, exhausted) self.indices = indices self.pointers = pointers self.exhausted = exhausted
def from_set(cls, context, builder, iter_type, set_val): set_inst = SetInstance(context, builder, iter_type.container, set_val) self = cls(context, builder, iter_type, None) index = context.get_constant(types.intp, 0) self._iter.index = cgutils.alloca_once_value(builder, index) self._iter.meminfo = set_inst.meminfo return self
def h5_read(context, builder, sig, args): # extra last arg type for type enum arg_typs = [h5file_lir_type, lir.IntType(32), lir.IntType(64).as_pointer(), lir.IntType(64).as_pointer(), lir.IntType(64), lir.IntType(8).as_pointer(), lir.IntType(32)] fnty = lir.FunctionType(lir.IntType(32), arg_typs) fn = builder.module.get_or_insert_function(fnty, name="hpat_h5_read") out = make_array(sig.args[5])(context, builder, args[5]) # store size vars array struct to pointer count_ptr = cgutils.alloca_once(builder, args[2].type) builder.store(args[2], count_ptr) size_ptr = cgutils.alloca_once(builder, args[3].type) builder.store(args[3], size_ptr) # store an int to specify data type typ_enum = _numba_to_c_type_map[sig.args[5].dtype] typ_arg = cgutils.alloca_once_value( builder, lir.Constant(lir.IntType(32), typ_enum)) call_args = [args[0], args[1], builder.bitcast(count_ptr, lir.IntType(64).as_pointer()), builder.bitcast(size_ptr, lir.IntType( 64).as_pointer()), args[4], builder.bitcast(out.data, lir.IntType(8).as_pointer()), builder.load(typ_arg)] return builder.call(fn, call_args)
def lower_dist_isend(context, builder, sig, args): # store an int to specify data type typ_enum = hpat.pio_lower._h5_typ_table[sig.args[0].dtype] typ_arg = cgutils.alloca_once_value( builder, lir.Constant(lir.IntType(32), typ_enum)) out = make_array(sig.args[0])(context, builder, args[0]) call_args = [ builder.bitcast(out.data, lir.IntType(8).as_pointer()), args[1], builder.load(typ_arg), args[2], args[3], args[4] ] # array, size, extra arg type for type enum # pe, tag, cond arg_typs = [ lir.IntType(8).as_pointer(), lir.IntType(32), lir.IntType(32), lir.IntType(32), lir.IntType(32), lir.IntType(1) ] fnty = lir.FunctionType(lir.IntType(32), arg_typs) fn = builder.module.get_or_insert_function(fnty, name="hpat_dist_isend") return builder.call(fn, call_args)
def get_next_int(context, builder, state_ptr, nbits): """ Get the next integer with width *nbits*. """ c32 = ir.Constant(nbits.type, 32) def get_shifted_int(nbits): shift = builder.sub(c32, nbits) y = get_next_int32(context, builder, state_ptr) return builder.lshr(y, builder.zext(shift, y.type)) ret = cgutils.alloca_once_value(builder, ir.Constant(int64_t, 0)) is_32b = builder.icmp_unsigned('<=', nbits, c32) with cgutils.ifelse(builder, is_32b) as (ifsmall, iflarge): with ifsmall: low = get_shifted_int(nbits) builder.store(builder.zext(low, int64_t), ret) with iflarge: # XXX This assumes nbits <= 64 low = get_next_int32(context, builder, state_ptr) high = get_shifted_int(builder.sub(nbits, c32)) total = builder.add( builder.zext(low, int64_t), builder.shl(builder.zext(high, int64_t), ir.Constant(int64_t, 32))) builder.store(total, ret) return builder.load(ret)
def codegen(context, builder, sig, args): fnty = ir.FunctionType( ll_status, [ll_list_type, ll_bytes], ) [l, item] = args [tl, titem] = sig.args fn = builder.module.get_or_insert_function(fnty, name='numba_list_append') dm_item = context.data_model_manager[titem] data_item = dm_item.as_data(builder, item) ptr_item = cgutils.alloca_once_value(builder, data_item) lp = _container_get_data(context, builder, tl, l) status = builder.call( fn, [ lp, _as_bytes(builder, ptr_item), ], ) return status
def from_list(cls, context, builder, iter_type, list_val): list_inst = ListInstance(context, builder, iter_type.list_type, list_val) self = cls(context, builder, iter_type, None) index = context.get_constant(types.intp, 0) self._iter.index = cgutils.alloca_once_value(builder, index) self._iter.meminfo = list_inst.meminfo return self
def lower_dist_allgather(context, builder, sig, args): arr_typ = sig.args[0] val_typ = sig.args[1] assert val_typ == arr_typ.dtype # type enum arg assert val_typ in _h5_typ_table, "invalid allgather type" typ_enum = _h5_typ_table[val_typ] typ_arg = context.get_constant(types.int32, typ_enum) # size arg is 1 for now size_arg = context.get_constant(types.int32, 1) val_ptr = cgutils.alloca_once_value(builder, args[1]) out = make_array(sig.args[0])(context, builder, args[0]) call_args = [builder.bitcast(out.data, lir.IntType(8).as_pointer()), size_arg, val_ptr, typ_arg] fnty = lir.FunctionType(lir.VoidType(), [lir.IntType(8).as_pointer(), lir.IntType(32), val_ptr.type, lir.IntType(32)]) fn = builder.module.get_or_insert_function(fnty, name="allgather") builder.call(fn, call_args) return context.get_dummy_value()
def iternext_zip(context, builder, sig, args, result): [zip_type] = sig.args [zipobj] = args zipobj = context.make_helper(builder, zip_type, value=zipobj) if len(zipobj) == 0: # zip() is an empty iterator result.set_exhausted() return p_ret_tup = cgutils.alloca_once(builder, context.get_value_type(zip_type.yield_type)) p_is_valid = cgutils.alloca_once_value(builder, value=cgutils.true_bit) for i, (iterobj, srcty) in enumerate(zip(zipobj, zip_type.source_types)): is_valid = builder.load(p_is_valid) # Avoid calling the remaining iternext if a iterator has been exhausted with builder.if_then(is_valid): srcres = call_iternext(context, builder, srcty, iterobj) is_valid = builder.and_(is_valid, srcres.is_valid()) builder.store(is_valid, p_is_valid) val = srcres.yielded_value() ptr = cgutils.gep_inbounds(builder, p_ret_tup, 0, i) builder.store(val, ptr) is_valid = builder.load(p_is_valid) result.set_valid(is_valid) with builder.if_then(is_valid): result.yield_(builder.load(p_ret_tup))
def init_specific(self, context, builder, arrty, arr): zero = context.get_constant(types.intp, 0) data = arr.data ndim = arrty.ndim shapes = cgutils.unpack_tuple(builder, arr.shape, ndim) indices = cgutils.alloca_once(builder, zero.type, size=context.get_constant( types.intp, arrty.ndim)) pointers = cgutils.alloca_once(builder, data.type, size=context.get_constant( types.intp, arrty.ndim)) strides = cgutils.unpack_tuple(builder, arr.strides, ndim) exhausted = cgutils.alloca_once_value(builder, cgutils.false_byte) # Initialize indices and pointers with their start values. for dim in range(ndim): idxptr = cgutils.gep(builder, indices, dim) ptrptr = cgutils.gep(builder, pointers, dim) builder.store(data, ptrptr) builder.store(zero, idxptr) # 0-sized dimensions really indicate an empty array, # but we have to catch that condition early to avoid # a bug inside the iteration logic (see issue #846). dim_size = shapes[dim] dim_is_empty = builder.icmp(lc.ICMP_EQ, dim_size, zero) with cgutils.if_unlikely(builder, dim_is_empty): builder.store(cgutils.true_byte, exhausted) self.indices = indices self.pointers = pointers self.exhausted = exhausted
def get_next_int(context, builder, state_ptr, nbits): """ Get the next integer with width *nbits*. """ c32 = ir.Constant(nbits.type, 32) def get_shifted_int(nbits): shift = builder.sub(c32, nbits) y = get_next_int32(context, builder, state_ptr) return builder.lshr(y, builder.zext(shift, y.type)) ret = cgutils.alloca_once_value(builder, ir.Constant(int64_t, 0)) is_32b = builder.icmp_unsigned('<=', nbits, c32) with builder.if_else(is_32b) as (ifsmall, iflarge): with ifsmall: low = get_shifted_int(nbits) builder.store(builder.zext(low, int64_t), ret) with iflarge: # XXX This assumes nbits <= 64 low = get_next_int32(context, builder, state_ptr) high = get_shifted_int(builder.sub(nbits, c32)) total = builder.add( builder.zext(low, int64_t), builder.shl(builder.zext(high, int64_t), ir.Constant(int64_t, 32))) builder.store(total, ret) return builder.load(ret)
def isdisjoint(self, other): context = self._context builder = self._builder payload = self.payload other_payload = other.payload res = cgutils.alloca_once_value(builder, cgutils.true_bit) def check(smaller, larger): # Loop over the smaller of the two, and search in the larger with smaller._iterate() as loop: entry = loop.entry found, _ = larger._lookup(entry.key, entry.hash) with builder.if_then(found): builder.store(cgutils.false_bit, res) loop.do_break() with builder.if_else( builder.icmp_unsigned('>', payload.used, other_payload.used) ) as (if_larger, otherwise): with if_larger: # len(self) > len(other) check(other_payload, payload) with otherwise: # len(self) <= len(other) check(payload, other_payload) return builder.load(res)
def downsize(self, nitems): """ When removing from the set, ensure it is properly sized for the given number of used entries. """ context = self._context builder = self._builder intp_t = nitems.type one = ir.Constant(intp_t, 1) two = ir.Constant(intp_t, 2) minsize = ir.Constant(intp_t, MINSIZE) payload = self.payload # Ensure entries >= max(2 * used, MINSIZE) min_entries = builder.shl(nitems, one) min_entries = builder.select(builder.icmp_unsigned('>=', min_entries, minsize), min_entries, minsize) # Shrink only if size >= 4 * min_entries && size > MINSIZE max_size = builder.shl(min_entries, two) size = builder.add(payload.mask, one) need_resize = builder.and_( builder.icmp_unsigned('<=', max_size, size), builder.icmp_unsigned('<', minsize, size)) with builder.if_then(need_resize, likely=False): # Find out next suitable size new_size_p = cgutils.alloca_once_value(builder, size) bb_body = builder.append_basic_block("calcsize.body") bb_end = builder.append_basic_block("calcsize.end") builder.branch(bb_body) with builder.goto_block(bb_body): # Divide by 2 (ensuring size remains a power of two) new_size = builder.load(new_size_p) new_size = builder.lshr(new_size, one) # Keep current size if new size would be < min_entries is_too_small = builder.icmp_unsigned('>', min_entries, new_size) with builder.if_then(is_too_small): builder.branch(bb_end) builder.store(new_size, new_size_p) builder.branch(bb_body) builder.position_at_end(bb_end) # Ensure new_size >= MINSIZE new_size = builder.load(new_size_p) # At this point, new_size should be < size if the factors # above were chosen carefully! if DEBUG_ALLOCS: context.printf(builder, "downsize to %zd items: current size = %zd, " "min entries = %zd, new size = %zd\n", nitems, size, min_entries, new_size) self._resize(payload, new_size, "cannot shrink set")
def alloca_buffer(self): """ Return a pointer to a stack-allocated, zero-initialized Py_buffer. """ # Treat the buffer as an opaque array of bytes ptr = cgutils.alloca_once_value(self.builder, lc.Constant.null(self.py_buffer_t)) return ptr
def codegen(context, builder, sig, args): fnty = ir.FunctionType( ll_ssize_t, [ll_dict_type, ll_bytes, ll_hash, ll_bytes], ) [td, tkey, thashval] = sig.args [d, key, hashval] = args fn = builder.module.get_or_insert_function(fnty, name='numba_dict_lookup') dm_key = context.data_model_manager[tkey] dm_val = context.data_model_manager[td.value_type] data_key = dm_key.as_data(builder, key) ptr_key = cgutils.alloca_once_value(builder, data_key) ll_val = context.get_data_type(td.value_type) ptr_val = cgutils.alloca_once(builder, ll_val) dp = _dict_get_data(context, builder, td, d) ix = builder.call( fn, [ dp, _as_bytes(builder, ptr_key), hashval, _as_bytes(builder, ptr_val), ], ) # Load value if output is available found = builder.icmp_signed('>=', ix, ix.type(int(DKIX.EMPTY))) out = context.make_optional_none(builder, td.value_type) pout = cgutils.alloca_once_value(builder, out) with builder.if_then(found): val = dm_val.load_from_data_pointer(builder, ptr_val) context.nrt.incref(builder, td.value_type, val) loaded = context.make_optional_value(builder, td.value_type, val) builder.store(loaded, pout) out = builder.load(pout) return context.make_tuple(builder, resty, [ix, out])
def _copy_payload(self, src_payload): """ Raw-copy the given payload into self. """ context = self._context builder = self._builder ok = cgutils.alloca_once_value(builder, cgutils.true_bit) intp_t = context.get_value_type(types.intp) zero = ir.Constant(intp_t, 0) one = ir.Constant(intp_t, 1) payload_type = context.get_data_type(types.SetPayload(self._ty)) payload_size = context.get_abi_sizeof(payload_type) entry_size = self._entrysize # Account for the fact that the payload struct already contains an entry payload_size -= entry_size mask = src_payload.mask nentries = builder.add(one, mask) # Total allocation size = <payload header size> + nentries * entry_size # (note there can't be any overflow since we're reusing an existing # payload's parameters) allocsize = builder.add( ir.Constant(intp_t, payload_size), builder.mul(ir.Constant(intp_t, entry_size), nentries)) with builder.if_then(builder.load(ok), likely=True): meminfo = context.nrt.meminfo_new_varsize(builder, size=allocsize) alloc_ok = cgutils.is_null(builder, meminfo) with builder.if_else(cgutils.is_null(builder, meminfo), likely=False) as (if_error, if_ok): with if_error: builder.store(cgutils.false_bit, ok) with if_ok: self._set.meminfo = meminfo payload = self.payload payload.used = src_payload.used payload.fill = src_payload.fill payload.finger = zero payload.mask = mask cgutils.raw_memcpy(builder, payload.entries, src_payload.entries, nentries, entry_size) if DEBUG_ALLOCS: context.printf( builder, "allocated %zd bytes for set at %p: mask = %zd\n", allocsize, payload.ptr, mask) return builder.load(ok)
def impl_string_array_single(context, builder, sig, args): typ = sig.return_type string_array = cgutils.create_struct_proxy(typ)(context, builder) if not sig.args: # return empty string array if no args return string_array._getvalue() string_list = ListInstance(context, builder, sig.args[0], args[0]) # get total size of string buffer fnty = lir.FunctionType(lir.IntType(64), [lir.IntType(8).as_pointer()]) fn_len = builder.module.get_or_insert_function(fnty, name="get_str_len") zero = context.get_constant(types.intp, 0) total_size = cgutils.alloca_once_value(builder, zero) string_array.size = string_list.size # loop through all strings and get length with cgutils.for_range(builder, string_list.size) as loop: str_value = string_list.getitem(loop.index) str_len = builder.call(fn_len, [str_value]) builder.store(builder.add(builder.load(total_size), str_len), total_size) # allocate string array fnty = lir.FunctionType(lir.VoidType(), [ lir.IntType(8).as_pointer().as_pointer(), lir.IntType(8).as_pointer().as_pointer(), lir.IntType(64), lir.IntType(64) ]) fn_alloc = builder.module.get_or_insert_function( fnty, name="allocate_string_array") builder.call(fn_alloc, [ string_array._get_ptr_by_name('offsets'), string_array._get_ptr_by_name('data'), string_list.size, builder.load(total_size) ]) # set string array values fnty = lir.FunctionType(lir.VoidType(), [ lir.IntType(8).as_pointer(), lir.IntType(8).as_pointer(), lir.IntType(8).as_pointer(), lir.IntType(64) ]) fn_setitem = builder.module.get_or_insert_function( fnty, name="setitem_string_array") with cgutils.for_range(builder, string_list.size) as loop: str_value = string_list.getitem(loop.index) builder.call( fn_setitem, [string_array.offsets, string_array.data, str_value, loop.index]) return string_array._getvalue()
def init_specific(self, context, builder, arrty, arr): zero = context.get_constant(types.intp, 0) self.index = cgutils.alloca_once_value(builder, zero) self.pointer = cgutils.alloca_once_value(builder, arr.data) # We can't trust strides[-1] to always contain the right # step value, see # http://docs.scipy.org/doc/numpy-dev/release.html#npy-relaxed-strides-checking self.stride = arr.itemsize if kind == 'ndenumerate': # Zero-initialize the indices array. indices = cgutils.alloca_once( builder, zero.type, size=context.get_constant(types.intp, arrty.ndim)) for dim in range(arrty.ndim): idxptr = cgutils.gep(builder, indices, dim) builder.store(zero, idxptr) self.indices = indices
def _copy_payload(self, src_payload): """ Raw-copy the given payload into self. """ context = self._context builder = self._builder ok = cgutils.alloca_once_value(builder, cgutils.true_bit) intp_t = context.get_value_type(types.intp) zero = ir.Constant(intp_t, 0) one = ir.Constant(intp_t, 1) payload_type = context.get_data_type(types.SetPayload(self._ty)) payload_size = context.get_abi_sizeof(payload_type) entry_size = self._entrysize # Account for the fact that the payload struct already contains an entry payload_size -= entry_size mask = src_payload.mask nentries = builder.add(one, mask) # Total allocation size = <payload header size> + nentries * entry_size # (note there can't be any overflow since we're reusing an existing # payload's parameters) allocsize = builder.add(ir.Constant(intp_t, payload_size), builder.mul(ir.Constant(intp_t, entry_size), nentries)) with builder.if_then(builder.load(ok), likely=True): meminfo = context.nrt.meminfo_new_varsize(builder, size=allocsize) alloc_ok = cgutils.is_null(builder, meminfo) with builder.if_else(cgutils.is_null(builder, meminfo), likely=False) as (if_error, if_ok): with if_error: builder.store(cgutils.false_bit, ok) with if_ok: self._set.meminfo = meminfo payload = self.payload payload.used = src_payload.used payload.fill = src_payload.fill payload.finger = zero payload.mask = mask cgutils.raw_memcpy(builder, payload.entries, src_payload.entries, nentries, entry_size) if DEBUG_ALLOCS: context.printf(builder, "allocated %zd bytes for set at %p: mask = %zd\n", allocsize, payload.ptr, mask) return builder.load(ok)
def init_specific(self, context, builder, arrty, arr): zero = context.get_constant(types.intp, 0) self.index = cgutils.alloca_once_value(builder, zero) self.pointer = cgutils.alloca_once_value(builder, arr.data) # We can't trust strides[-1] to always contain the right # step value, see # http://docs.scipy.org/doc/numpy-dev/release.html#npy-relaxed-strides-checking self.stride = arr.itemsize if kind == 'ndenumerate': # Zero-initialize the indices array. indices = cgutils.alloca_once(builder, zero.type, size=context.get_constant( types.intp, arrty.ndim)) for dim in range(arrty.ndim): idxptr = cgutils.gep(builder, indices, dim) builder.store(zero, idxptr) self.indices = indices
def getiter_array(context, builder, sig, args): [arrayty] = sig.args [array] = args iterobj = make_arrayiter_cls(sig.return_type)(context, builder) zero = context.get_constant(types.intp, 0) indexptr = cgutils.alloca_once_value(builder, zero) iterobj.index = indexptr iterobj.array = array return iterobj._getvalue()
def make_array_flatiter(context, builder, arrty, arr): flatitercls = make_array_flat_cls(types.NumpyFlatType(arrty)) flatiter = flatitercls(context, builder) arrayptr = cgutils.alloca_once_value(builder, arr) flatiter.array = arrayptr arrcls = context.make_array(arrty) arr = arrcls(context, builder, ref=arrayptr) flatiter.init_specific(context, builder, arrty, arr) return flatiter._getvalue()
def const_print_impl(context, builder, sig, args): ty, = sig.args pyval = ty.value # This is ensured by lowering assert isinstance(pyval, str) vprint = nvvmutils.declare_vprint(builder.module) rawfmt = "%s" fmt = context.insert_string_const_addrspace(builder, rawfmt) val = context.insert_string_const_addrspace(builder, pyval) valptr = cgutils.alloca_once_value(builder, val) builder.call(vprint, (fmt, builder.bitcast(valptr, voidptr))) return context.get_dummy_value()
def make_array_ndenumerate(context, builder, sig, args): arrty, = sig.args arr, = args nditercls = make_array_ndenumerate_cls(types.NumpyNdEnumerateType(arrty)) nditer = nditercls(context, builder) arrayptr = cgutils.alloca_once_value(builder, arr) nditer.array = arrayptr arrcls = context.make_array(arrty) arr = arrcls(context, builder, ref=arrayptr) nditer.init_specific(context, builder, arrty, arr) return nditer._getvalue()
def nrt_adapt_ndarray_to_python(self, aryty, ary, dtypeptr): assert self.context.enable_nrt, "NRT required" intty = ir.IntType(32) fnty = Type.function(self.pyobj, [self.voidptr, intty, intty, self.pyobj]) fn = self._get_function(fnty, name="NRT_adapt_ndarray_to_python") fn.args[0].add_attribute(lc.ATTR_NO_CAPTURE) ndim = self.context.get_constant(types.int32, aryty.ndim) writable = self.context.get_constant(types.int32, int(aryty.mutable)) aryptr = cgutils.alloca_once_value(self.builder, ary) return self.builder.call(fn, [self.builder.bitcast(aryptr, self.voidptr), ndim, writable, dtypeptr])
def allocate_ex(cls, context, builder, list_type, nitems): """ Allocate a ListInstance with its storage. Return a (ok, instance) tuple where *ok* is a LLVM boolean and *instance* is a ListInstance object (the object's contents are only valid when *ok* is true). """ intp_t = context.get_value_type(types.intp) if isinstance(nitems, int): nitems = ir.Constant(intp_t, nitems) payload_type = context.get_data_type(types.ListPayload(list_type)) payload_size = context.get_abi_sizeof(payload_type) itemsize = get_itemsize(context, list_type) # Account for the fact that the payload struct contains one entry payload_size -= itemsize ok = cgutils.alloca_once_value(builder, cgutils.true_bit) self = cls(context, builder, list_type, None) # Total allocation size = <payload header size> + nitems * itemsize allocsize, ovf = cgutils.muladd_with_overflow(builder, nitems, ir.Constant(intp_t, itemsize), ir.Constant(intp_t, payload_size)) with builder.if_then(ovf, likely=False): builder.store(cgutils.false_bit, ok) with builder.if_then(builder.load(ok), likely=True): meminfo = context.nrt.meminfo_new_varsize_dtor( builder, size=allocsize, dtor=self.get_dtor()) with builder.if_else(cgutils.is_null(builder, meminfo), likely=False) as (if_error, if_ok): with if_error: builder.store(cgutils.false_bit, ok) with if_ok: self._list.meminfo = meminfo self._list.parent = context.get_constant_null(types.pyobject) self._payload.allocated = nitems self._payload.size = ir.Constant(intp_t, 0) # for safety self._payload.dirty = cgutils.false_bit # Zero the allocated region self.zfill(self.size.type(0), nitems) return builder.load(ok), self
def upsize(self, nitems): """ When adding to the set, ensure it is properly sized for the given number of used entries. """ context = self._context builder = self._builder intp_t = nitems.type one = ir.Constant(intp_t, 1) two = ir.Constant(intp_t, 2) payload = self.payload # Ensure number of entries >= 2 * used min_entries = builder.shl(nitems, one) size = builder.add(payload.mask, one) need_resize = builder.icmp_unsigned('>=', min_entries, size) with builder.if_then(need_resize, likely=False): # Find out next suitable size new_size_p = cgutils.alloca_once_value(builder, size) bb_body = builder.append_basic_block("calcsize.body") bb_end = builder.append_basic_block("calcsize.end") builder.branch(bb_body) with builder.goto_block(bb_body): # Multiply by 4 (ensuring size remains a power of two) new_size = builder.load(new_size_p) new_size = builder.shl(new_size, two) builder.store(new_size, new_size_p) is_too_small = builder.icmp_unsigned('>=', min_entries, new_size) builder.cbranch(is_too_small, bb_body, bb_end) builder.position_at_end(bb_end) new_size = builder.load(new_size_p) if DEBUG_ALLOCS: context.printf(builder, "upsize to %zd items: current size = %zd, " "min entries = %zd, new size = %zd\n", nitems, size, min_entries, new_size) self._resize(payload, new_size, "cannot grow set")
def build_set(context, builder, set_type, items): """ Build a set of the given type, containing the given items. """ nitems = len(items) inst = SetInstance.allocate(context, builder, set_type, nitems) # Populate set. Inlining the insertion code for each item would be very # costly, instead we create a LLVM array and iterate over it. array = cgutils.pack_array(builder, items) array_ptr = cgutils.alloca_once_value(builder, array) count = context.get_constant(types.intp, nitems) with cgutils.for_range(builder, count) as loop: item = builder.load(cgutils.gep(builder, array_ptr, 0, loop.index)) inst.add(item) return impl_ret_new_ref(context, builder, set_type, inst.value)
def clamp_index(self, idx): """ Clamp the index in [0, size]. """ builder = self._builder idxptr = cgutils.alloca_once_value(builder, idx) zero = ir.Constant(idx.type, 0) size = self.size underflow = self._builder.icmp_signed('<', idx, zero) with builder.if_then(underflow, likely=False): builder.store(zero, idxptr) overflow = self._builder.icmp_signed('>=', idx, size) with builder.if_then(overflow, likely=False): builder.store(size, idxptr) return builder.load(idxptr)
def init_specific(self, context, builder, arrty, arr): zero = context.get_constant(types.intp, 0) one = context.get_constant(types.intp, 1) data = arr.data ndim = arrty.ndim shapes = cgutils.unpack_tuple(builder, arr.shape, ndim) indices = cgutils.alloca_once(builder, zero.type, size=context.get_constant(types.intp, arrty.ndim)) pointers = cgutils.alloca_once(builder, data.type, size=context.get_constant(types.intp, arrty.ndim)) strides = cgutils.unpack_tuple(builder, arr.strides, ndim) empty = cgutils.alloca_once_value(builder, cgutils.false_byte) # Initialize each dimension with the next index and pointer # values. For the last (inner) dimension, this is 0 and the # start pointer, for the other dimensions, this is 1 and the # pointer to the next subarray after start. for dim in range(ndim): idxptr = cgutils.gep(builder, indices, dim) ptrptr = cgutils.gep(builder, pointers, dim) if dim == ndim - 1: builder.store(zero, idxptr) builder.store(data, ptrptr) else: p = cgutils.pointer_add(builder, data, strides[dim]) builder.store(p, ptrptr) builder.store(one, idxptr) # 0-sized dimensions really indicate an empty array, # but we have to catch that condition early to avoid # a bug inside the iteration logic (see issue #846). dim_size = shapes[dim] dim_is_empty = builder.icmp(lc.ICMP_EQ, dim_size, zero) with cgutils.if_unlikely(builder, dim_is_empty): builder.store(cgutils.true_byte, empty) self.indices = indices self.pointers = pointers self.empty = empty
def list_eq(context, builder, sig, args): aty, bty = sig.args a = ListInstance(context, builder, aty, args[0]) b = ListInstance(context, builder, bty, args[1]) a_size = a.size same_size = builder.icmp_signed("==", a_size, b.size) res = cgutils.alloca_once_value(builder, same_size) with builder.if_then(same_size): with cgutils.for_range(builder, a_size) as loop: v = a.getitem(loop.index) w = b.getitem(loop.index) itemres = context.generic_compare(builder, "==", (aty.dtype, bty.dtype), (v, w)) with builder.if_then(builder.not_(itemres)): # Exit early builder.store(cgutils.false_bit, res) loop.do_break() return builder.load(res)
def init_specific(self, context, builder, shapes): zero = context.get_constant(types.intp, 0) indices = cgutils.alloca_once(builder, zero.type, size=context.get_constant(types.intp, ndim)) exhausted = cgutils.alloca_once_value(builder, cgutils.false_byte) for dim in range(ndim): idxptr = cgutils.gep(builder, indices, dim) builder.store(zero, idxptr) # 0-sized dimensions really indicate an empty array, # but we have to catch that condition early to avoid # a bug inside the iteration logic. dim_size = shapes[dim] dim_is_empty = builder.icmp(lc.ICMP_EQ, dim_size, zero) with cgutils.if_unlikely(builder, dim_is_empty): builder.store(cgutils.true_byte, exhausted) self.indices = indices self.exhausted = exhausted self.shape = cgutils.pack_array(builder, shapes)
def getiter_unicode(context, builder, sig, args): [ty] = sig.args [data] = args iterobj = context.make_helper(builder, sig.return_type) # set the index to zero zero = context.get_constant(types.uintp, 0) indexptr = cgutils.alloca_once_value(builder, zero) iterobj.index = indexptr # wire in the unicode type data iterobj.data = data # incref as needed if context.enable_nrt: context.nrt.incref(builder, ty, data) res = iterobj._getvalue() return impl_ret_new_ref(context, builder, sig.return_type, res)