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 = _container_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 int_divmod_signed(context, builder, ty, x, y): """ Reference Objects/intobject.c xdivy = x / y; xmody = (long)(x - (unsigned long)xdivy * y); /* If the signs of x and y differ, and the remainder is non-0, * C89 doesn't define whether xdivy is now the floor or the * ceiling of the infinitely precise quotient. We want the floor, * and we have it iff the remainder's sign matches y's. */ if (xmody && ((y ^ xmody) < 0) /* i.e. and signs differ */) { xmody += y; --xdivy; assert(xmody && ((y ^ xmody) >= 0)); } *p_xdivy = xdivy; *p_xmody = xmody; """ assert x.type == y.type ZERO = y.type(0) ONE = y.type(1) # NOTE: On x86 at least, dividing the lowest representable integer # (e.g. 0x80000000 for int32) by -1 causes a SIFGPE (division overflow), # causing the process to crash. # We return 0, 0 instead (more or less like Numpy). resdiv = cgutils.alloca_once_value(builder, ZERO) resmod = cgutils.alloca_once_value(builder, ZERO) is_overflow = builder.and_( builder.icmp_signed("==", x, x.type(ty.minval)), builder.icmp_signed("==", y, y.type(-1)), ) with builder.if_then(builder.not_(is_overflow), likely=True): # Note LLVM will optimize this to a single divmod instruction, # if available on the target CPU (e.g. x86). xdivy = builder.sdiv(x, y) xmody = builder.srem(x, y) y_xor_xmody_ltz = builder.icmp_signed("<", builder.xor(y, xmody), ZERO) xmody_istrue = builder.icmp_signed("!=", xmody, ZERO) cond = builder.and_(xmody_istrue, y_xor_xmody_ltz) with builder.if_else(cond) as (if_different_signs, if_same_signs): with if_same_signs: builder.store(xdivy, resdiv) builder.store(xmody, resmod) with if_different_signs: builder.store(builder.sub(xdivy, ONE), resdiv) builder.store(builder.add(xmody, y), resmod) return builder.load(resdiv), builder.load(resmod)
def codegen_get_voidptr(context, builder, ty_var, var_val): dm_key = context.data_model_manager[ty_var] data_val = dm_key.as_data(builder, var_val) ptr_var = cgutils.alloca_once_value(builder, data_val) val_as_voidptr = _as_bytes(builder, ptr_var) return val_as_voidptr
def codegen(context, builder, signature, args): data, = args rawptr = cgutils.alloca_once_value(builder, value=data) struct = builder.load(builder.gep(rawptr, [int32_t(0)])) return builder.load(builder.gep( struct, [int32_t(0), int32_t(1)]))
def box_generator_struct(self, lower, gen_struct): """ Box the raw *gen_struct* as a Python object. """ gen_ptr = cgutils.alloca_once_value(lower.builder, gen_struct) return lower.pyapi.from_native_generator(gen_ptr, self.gentype, lower.envarg)
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 unbox_numpy_random_generator(typ, obj, c): """ Here we're creating a NumPyRandomGeneratorType StructModel with following fields: * ('bit_generator', _bit_gen_type): The unboxed BitGenerator associated with this Generator object instance. * ('parent', types.pyobject): Pointer to the original Generator PyObject. * ('meminfo', types.MemInfoPointer(types.voidptr)): The information about the memory stored at the pointer (to the original Generator PyObject). This is useful for keeping track of reference counts within the Python runtime. Helps prevent cases where deletion happens in Python runtime without NRT being awareness of it. """ is_error_ptr = cgutils.alloca_once_value(c.builder, cgutils.false_bit) with ExitStack() as stack: struct_ptr = cgutils.create_struct_proxy(typ)(c.context, c.builder) bit_gen_inst = c.pyapi.object_getattr_string(obj, 'bit_generator') with early_exit_if_null(c.builder, stack, bit_gen_inst): c.builder.store(cgutils.true_bit, is_error_ptr) unboxed = c.unbox(_bit_gen_type, bit_gen_inst).value struct_ptr.bit_generator = unboxed struct_ptr.parent = obj NULL = cgutils.voidptr_t(None) struct_ptr.meminfo = c.pyapi.nrt_meminfo_new_from_pyobject( NULL, # there's no data obj, # the python object, the call to nrt_meminfo_new_from_pyobject # will py_incref ) c.pyapi.decref(bit_gen_inst) return NativeValue(struct_ptr._getvalue(), is_error=c.builder.load(is_error_ptr))
def unbox_unicodecharseq(typ, obj, c): lty = c.context.get_value_type(typ) ok, buffer, size, kind, is_ascii, hashv = \ c.pyapi.string_as_string_size_and_kind(obj) # If conversion is ok, copy the buffer to the output storage. with cgutils.if_likely(c.builder, ok): # Check if the returned string size fits in the charseq storage_size = ir.Constant(size.type, typ.count) size_fits = c.builder.icmp_unsigned("<=", size, storage_size) # Allow truncation of string size = c.builder.select(size_fits, size, storage_size) # Initialize output to zero bytes null_string = ir.Constant(lty, None) outspace = cgutils.alloca_once_value(c.builder, null_string) # We don't need to set the NULL-terminator because the storage # is already zero-filled. cgutils.memcpy(c.builder, c.builder.bitcast(outspace, buffer.type), buffer, size) ret = c.builder.load(outspace) return NativeValue(ret, is_error=c.builder.not_(ok))
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 print_varargs(context, builder, sig, args): """This function is a generic 'print' wrapper for arbitrary types. It dispatches to the appropriate 'print' implementations above depending on the detected real types in the signature.""" vprint = nvvmutils.declare_vprint(builder.module) formats = [] values = [] for i, (argtype, argval) in enumerate(zip(sig.args, args)): argfmt, argvals = print_item(argtype, context, builder, argval) formats.append(argfmt) values.extend(argvals) rawfmt = " ".join(formats) + "\n" if len(args) > 32: msg = ('CUDA print() cannot print more than 32 items. ' 'The raw format string will be emitted by the kernel instead.') warn(msg, NumbaWarning) rawfmt = rawfmt.replace('%', '%%') fmt = context.insert_string_const_addrspace(builder, rawfmt) array = cgutils.make_anonymous_struct(builder, values) arrayptr = cgutils.alloca_once_value(builder, array) vprint = nvvmutils.declare_vprint(builder.module) builder.call(vprint, (fmt, builder.bitcast(arrayptr, voidptr))) return context.get_dummy_value()
def box_list(typ, val, c): """ Convert native list *val* to a list object. """ list = listobj.ListInstance(c.context, c.builder, typ, val) obj = list.parent res = cgutils.alloca_once_value(c.builder, obj) with c.builder.if_else(cgutils.is_not_null(c.builder, obj)) as (has_parent, otherwise): with has_parent: # List is actually reflected => return the original object # (note not all list instances whose *type* is reflected are # actually reflected; see numba.tests.test_lists for an example) c.pyapi.incref(obj) with otherwise: # Build a new Python list nitems = list.size obj = c.pyapi.list_new(nitems) with c.builder.if_then(cgutils.is_not_null(c.builder, obj), likely=True): with cgutils.for_range(c.builder, nitems) as loop: item = list.getitem(loop.index) list.incref_value(item) itemobj = c.box(typ.dtype, item) c.pyapi.list_setitem(obj, loop.index, itemobj) c.builder.store(obj, res) # Steal NRT ref c.context.nrt.decref(c.builder, typ, val) return c.builder.load(res)
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 _numba_to_c_type_map, "invalid allgather type" typ_enum = _numba_to_c_type_map[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 box_set(typ, val, c): """ Convert native set *val* to a set object. """ inst = setobj.SetInstance(c.context, c.builder, typ, val) obj = inst.parent res = cgutils.alloca_once_value(c.builder, obj) with c.builder.if_else(cgutils.is_not_null(c.builder, obj)) as (has_parent, otherwise): with has_parent: # Set is actually reflected => return the original object # (note not all set instances whose *type* is reflected are # actually reflected; see numba.tests.test_sets for an example) c.pyapi.incref(obj) with otherwise: # Build a new Python list and then create a set from that payload = inst.payload ok, listobj = _native_set_to_python_list(typ, payload, c) with c.builder.if_then(ok, likely=True): obj = c.pyapi.set_new(listobj) c.pyapi.decref(listobj) c.builder.store(obj, res) # Steal NRT ref c.context.nrt.decref(c.builder, typ, val) return c.builder.load(res)
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 unbox_optional(typ, obj, c): """ Convert object *obj* to a native optional structure. """ noneval = c.context.make_optional_none(c.builder, typ.type) is_not_none = c.builder.icmp_signed('!=', obj, c.pyapi.borrow_none()) retptr = cgutils.alloca_once(c.builder, noneval.type) errptr = cgutils.alloca_once_value(c.builder, cgutils.false_bit) with c.builder.if_else(is_not_none) as (then, orelse): with then: native = c.unbox(typ.type, obj) just = c.context.make_optional_value(c.builder, typ.type, native.value) c.builder.store(just, retptr) c.builder.store(native.is_error, errptr) with orelse: c.builder.store(noneval, retptr) if native.cleanup is not None: def cleanup(): with c.builder.if_then(is_not_none): native.cleanup() else: cleanup = None ret = c.builder.load(retptr) return NativeValue(ret, is_error=c.builder.load(errptr), cleanup=cleanup)
def codegen(context, builder, sig, args): fnty = ir.FunctionType(ll_status, [ll_list_type, ll_ssize_t, ll_bytes],) [tl, tindex] = sig.args [l, index] = args fn = builder.module.get_or_insert_function( fnty, name="numba_list_{}".format(op) ) dm_item = context.data_model_manager[tl.item_type] ll_item = context.get_data_type(tl.item_type) ptr_item = cgutils.alloca_once(builder, ll_item) lp = _container_get_data(context, builder, tl, l) status = builder.call(fn, [lp, index, _as_bytes(builder, ptr_item),],) # Load item if output is available found = builder.icmp_signed(">=", status, status.type(int(ListStatus.LIST_OK))) out = context.make_optional_none( builder, tl.item_type if IS_NOT_NONE else types.int64 ) pout = cgutils.alloca_once_value(builder, out) with builder.if_then(found): if IS_NOT_NONE: item = dm_item.load_from_data_pointer(builder, ptr_item) context.nrt.incref(builder, tl.item_type, item) loaded = context.make_optional_value(builder, tl.item_type, item) builder.store(loaded, pout) out = builder.load(pout) return context.make_tuple(builder, resty, [status, out])
def unbox_set(typ, obj, c): """ Convert set *obj* to a native set. If set was previously unboxed, we reuse the existing native set to ensure consistency. """ size = c.pyapi.set_size(obj) errorptr = cgutils.alloca_once_value(c.builder, cgutils.false_bit) setptr = cgutils.alloca_once(c.builder, c.context.get_value_type(typ)) # See if the set was previously unboxed, if so, re-use the meminfo. ptr = c.pyapi.object_get_private_data(obj) with c.builder.if_else(cgutils.is_not_null(c.builder, ptr)) \ as (has_meminfo, otherwise): with has_meminfo: # Set was previously unboxed => reuse meminfo inst = setobj.SetInstance.from_meminfo(c.context, c.builder, typ, ptr) if typ.reflected: inst.parent = obj c.builder.store(inst.value, setptr) with otherwise: _python_set_to_native(typ, obj, c, size, setptr, errorptr) def cleanup(): # Clean up the associated pointer, as the meminfo is now invalid. c.pyapi.object_reset_private_data(obj) return NativeValue(c.builder.load(setptr), is_error=c.builder.load(errorptr), cleanup=cleanup)
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 _python_set_to_native(typ, obj, c, size, setptr, errorptr): """ Construct a new native set from a Python set. """ # Allocate a new native set ok, inst = setobj.SetInstance.allocate_ex(c.context, c.builder, typ, size) with c.builder.if_else(ok, likely=True) as (if_ok, if_not_ok): with if_ok: # Traverse Python set and unbox objects into native set typobjptr = cgutils.alloca_once_value( c.builder, ir.Constant(c.pyapi.pyobj, None)) with c.pyapi.set_iterate(obj) as loop: itemobj = loop.value # Mandate that objects all have the same exact type typobj = c.pyapi.get_type(itemobj) expected_typobj = c.builder.load(typobjptr) with c.builder.if_else(cgutils.is_null(c.builder, expected_typobj), likely=False) as (if_first, if_not_first): with if_first: # First iteration => store item type c.builder.store(typobj, typobjptr) with if_not_first: # Otherwise, check item type type_mismatch = c.builder.icmp_signed( '!=', typobj, expected_typobj) with c.builder.if_then(type_mismatch, likely=False): c.builder.store(cgutils.true_bit, errorptr) c.pyapi.err_set_string( "PyExc_TypeError", "can't unbox heterogeneous set") loop.do_break() # XXX we don't call native cleanup for each set element, # since that would require keeping track # of which unboxings have been successful. native = c.unbox(typ.dtype, itemobj) with c.builder.if_then(native.is_error, likely=False): c.builder.store(cgutils.true_bit, errorptr) inst.add_pyapi(c.pyapi, native.value, do_resize=False) if typ.reflected: inst.parent = obj # Associate meminfo pointer with the Python object for later reuse. with c.builder.if_then(c.builder.not_(c.builder.load(errorptr)), likely=False): c.pyapi.object_set_private_data(obj, inst.meminfo) inst.set_dirty(False) c.builder.store(inst.value, setptr) with if_not_ok: c.builder.store(cgutils.true_bit, errorptr) # If an error occurred, drop the whole native set with c.builder.if_then(c.builder.load(errorptr)): c.context.nrt.decref(c.builder, typ, inst.value)
def StructureNumbaPointerType_getattr_impl(context, builder, sig, struct, attr): typ = sig.dtype model = datamodel.default_manager.lookup(typ) index = model.get_field_position(attr) rawptr = cgutils.alloca_once_value(builder, value=struct) struct = builder.load(builder.gep(rawptr, [int32_t(0)])) return builder.load(builder.gep(struct, [int32_t(0), int32_t(index)]))
def from_list(cls, context, builder, iter_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.parent = list_val self._iter.size = cls._size_of_list(context, builder, self._list_ty, self._iter.parent) return self
def codegen(context, builder, signature, args): data, index = args rawptr = cgutils.alloca_once_value(builder, value=data) buf = builder.load(builder.gep(rawptr, [int32_t(0)])) ptr = builder.load(builder.gep(buf, [int32_t(0), int32_t(0)])) res = builder.load(builder.gep(ptr, [index])) return res
def box_numpy_random_generator(typ, val, c): inst = c.context.make_helper(c.builder, typ, val) obj = inst.parent res = cgutils.alloca_once_value(c.builder, obj) c.pyapi.incref(obj) # Steal NRT ref c.context.nrt.decref(c.builder, typ, val) return c.builder.load(res)
def StructureNumbaPointerType_setattr_impl(context, builder, sig, args, attr): typ = sig.args[0].dtype struct, value = args model = datamodel.default_manager.lookup(typ) index = model.get_field_position(attr) rawptr = cgutils.alloca_once_value(builder, value=struct) ptr = builder.load(rawptr) buf = builder.load(builder.gep(ptr, [int32_t(0), int32_t(index)])) builder.store(value, buf.operands[0])
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 = cgutils.get_or_insert_function(builder.module, fnty, '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) cgutils.memset_padding(builder, ptr_key) ll_val = context.get_data_type(td.value_type) ptr_val = cgutils.alloca_once(builder, ll_val) dp = _container_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 codegen(context, builder, signature, args): zero = int32_t(0) data, index, value = args rawptr = cgutils.alloca_once_value(builder, value=data) ptr = builder.load(rawptr) arr = builder.load(builder.gep(ptr, [zero, zero])) builder.store(value, builder.gep(arr, [index]))
def box_optional(typ, val, c): optval = c.context.make_helper(c.builder, typ, val) ret = cgutils.alloca_once_value(c.builder, c.pyapi.borrow_none()) with c.builder.if_else(optval.valid) as (then, otherwise): with then: validres = c.box(typ.type, optval.data) c.builder.store(validres, ret) with otherwise: c.builder.store(c.pyapi.make_none(), ret) return c.builder.load(ret)
def codegen(context, builder, signature, args): zero = int32_t(0) data, index, value = args rawptr = cgutils.alloca_once_value(builder, value=data) ptr = builder.load(rawptr) buf = builder.load(builder.gep(ptr, [zero, zero])) value = truncate_or_extend(builder, nb_value, eltype, value, buf.type.pointee) builder.store(value, builder.gep(buf, [index]))
def unbox_tuple(typ, obj, c): """ Convert tuple *obj* to a native array (if homogeneous) or structure. """ n = len(typ) values = [] cleanups = [] lty = c.context.get_value_type(typ) is_error_ptr = cgutils.alloca_once_value(c.builder, cgutils.false_bit) value_ptr = cgutils.alloca_once(c.builder, lty) # Issue #1638: need to check the tuple size actual_size = c.pyapi.tuple_size(obj) size_matches = c.builder.icmp_unsigned( "==", actual_size, ir.Constant(actual_size.type, n) ) with c.builder.if_then(c.builder.not_(size_matches), likely=False): c.pyapi.err_format( "PyExc_ValueError", "size mismatch for tuple, expected %d element(s) but got %%zd" % (n,), actual_size, ) c.builder.store(cgutils.true_bit, is_error_ptr) # We unbox the items even if not `size_matches`, to avoid issues with # the generated IR (instruction doesn't dominate all uses) for i, eltype in enumerate(typ): elem = c.pyapi.tuple_getitem(obj, i) native = c.unbox(eltype, elem) values.append(native.value) with c.builder.if_then(native.is_error, likely=False): c.builder.store(cgutils.true_bit, is_error_ptr) if native.cleanup is not None: cleanups.append(native.cleanup) value = c.context.make_tuple(c.builder, typ, values) c.builder.store(value, value_ptr) if cleanups: with c.builder.if_then(size_matches, likely=True): def cleanup(): for func in reversed(cleanups): func() else: cleanup = None return NativeValue( c.builder.load(value_ptr), cleanup=cleanup, is_error=c.builder.load(is_error_ptr), )
def box_charseq(typ, val, c): rawptr = cgutils.alloca_once_value(c.builder, value=val) strptr = c.builder.bitcast(rawptr, c.pyapi.cstring) fullsize = c.context.get_constant(types.intp, typ.count) zero = fullsize.type(0) one = fullsize.type(1) count = cgutils.alloca_once_value(c.builder, zero) # Find the length of the string, mimicking Numpy's behaviour: # search for the last non-null byte in the underlying storage # (e.g. b'A\0\0B\0\0\0' will return the logical string b'A\0\0B') with cgutils.loop_nest(c.builder, [fullsize], fullsize.type) as [idx]: # Get char at idx ch = c.builder.load(c.builder.gep(strptr, [idx])) # If the char is a non-null-byte, store the next index as count with c.builder.if_then(cgutils.is_not_null(c.builder, ch)): c.builder.store(c.builder.add(idx, one), count) strlen = c.builder.load(count) return c.pyapi.bytes_from_string_and_size(strptr, strlen)