I’m trying to create a numba compatible Decimal math library (it’s still in the it’s very early days). I’m currently experimenting with different approaches, but I was recommended to use a StructModel
to represent the core of my dtype. Currently, I’m following the Interval Example, however I’m having some problems with getting my code to work.
Here is the calling code:
from numba import njit
@njit
def uses_decimal():
d = Decimal(True, "123", 12, 1)
return d
uses_decimal()
I’m getting the following exception when I try to use it:
RuntimeError: Failed in nopython mode pipeline (step: native lowering)
Instruction does not dominate all uses!
%.141 = load i8*, i8** %.58, align 8
ret i8* %.141
I’ve also attached my current implementation below. Does anyone know why I’m getting this LLVM error?
from contextlib import ExitStack
from numba import types
from numba.core import cgutils
from numba.extending import (
models,
register_model,
typeof_impl,
as_numba_type,
type_callable,
make_attribute_wrapper,
lower_builtin,
box,
unbox,
NativeValue,
)
class Decimal:
def __init__(self, sign, base, exponent, special_code):
self.sign = sign
self.base = base
self.exponent = exponent
self.special_code = special_code
class DecimalType(types.Type):
def __init__(self):
super(DecimalType, self).__init__(name="Decimal")
decimal_type = DecimalType()
@typeof_impl.register(Decimal)
def typeof_index(val, c):
return decimal_type
as_numba_type.register(Decimal, decimal_type)
@type_callable(Decimal)
def type_decimal(context):
def typer(sign, base, exponent, special_code):
sign_is_bool = isinstance(sign, types.Boolean)
base_is_string = isinstance(base, types.UnicodeType)
exponent_is_int = isinstance(exponent, types.Integer)
special_code_is_int = isinstance(special_code, types.Integer)
if sign_is_bool and base_is_string and exponent_is_int and special_code_is_int:
return decimal_type
return typer
@register_model(DecimalType)
class DecimalModel(models.StructModel):
def __init__(self, dmm, fe_type):
members = [
("sign", types.boolean),
("base", types.unicode_type),
("exponent", types.int64),
("special_code", types.int64),
]
models.StructModel.__init__(self, dmm, fe_type, members)
make_attribute_wrapper(DecimalType, "sign", "sign")
make_attribute_wrapper(DecimalType, "base", "base")
make_attribute_wrapper(DecimalType, "exponent", "exponent")
make_attribute_wrapper(DecimalType, "special_code", "special_code")
@lower_builtin(Decimal, types.boolean, types.unicode_type, types.int64, types.int64)
def impl_decimal(context, builder, sig, args):
typ = sig.return_type
sign, base, exponent, special_code = args
decimal = cgutils.create_struct_proxy(typ)(context, builder)
decimal.sign = sign
decimal.base = base
decimal.exponent = exponent
decimal.special_code = special_code
return decimal._getvalue()
@unbox(DecimalType)
def unbox_decimal(typ, obj, c):
is_error_ptr = cgutils.alloca_once_value(c.builder, cgutils.false_bit)
decimal = cgutils.create_struct_proxy(typ)(c.context, c.builder)
with ExitStack() as stack:
sign_obj = c.pyapi.object_getattr_string(obj, "sign")
with cgutils.early_exit_if_null(c.builder, stack, sign_obj):
c.builder.store(cgutils.true_bit, is_error_ptr)
sign_native = c.unbox(types.boolean, sign_obj)
c.pyapi.decref(sign_obj)
with cgutils.early_exit_if(c.builder, stack, sign_native.is_error):
c.builder.store(cgutils.true_bit, is_error_ptr)
base_obj = c.pyapi.object_getattr_string(obj, "base")
with cgutils.early_exit_if_null(c.builder, stack, base_obj):
c.builder.store(cgutils.true_bit, is_error_ptr)
base_native = c.unbox(types.unicode_type, base_obj)
c.pyapi.decref(base_obj)
with cgutils.early_exit_if(c.builder, stack, base_native.is_error):
c.builder.store(cgutils.true_bit, is_error_ptr)
exponent_obj = c.pyapi.object_getattr_string(obj, "exponent")
with cgutils.early_exit_if_null(c.builder, stack, exponent_obj):
c.builder.store(cgutils.true_bit, is_error_ptr)
exponent_native = c.unbox(types.int64, exponent_obj)
c.pyapi.decref(exponent_obj)
with cgutils.early_exit_if(c.builder, stack, exponent_native.is_error):
c.builder.store(cgutils.true_bit, is_error_ptr)
special_code_obj = c.pyapi.object_getattr_string(obj, "special_code")
with cgutils.early_exit_if_null(c.builder, stack, special_code_obj):
c.builder.store(cgutils.true_bit, is_error_ptr)
special_code_native = c.unbox(types.int64, special_code_obj)
c.pyapi.decref(special_code_obj)
with cgutils.early_exit_if(c.builder, stack, special_code_native.is_error):
c.builder.store(cgutils.true_bit, is_error_ptr)
decimal.sign = sign_native.value
decimal.base = base_native.value
decimal.exponent = exponent_native.value
decimal.special_code = special_code_native.value
return NativeValue(decimal._getvalue(), is_error=c.builder.load(is_error_ptr))
@box(DecimalType)
def box_decimal(typ, val, c):
ret_ptr = cgutils.alloca_once(c.builder, c.pyapi.pyobj)
fail_obj = c.pyapi.get_null_object()
with ExitStack() as stack:
decimal = cgutils.create_struct_proxy(typ)(c.context, c.builder, value=val)
sign_obj = c.box(types.boolean, decimal.sign)
with cgutils.early_exit_if_null(c.builder, stack, sign_obj):
c.builder.store(fail_obj, ret_ptr)
base_obj = c.box(types.unicode_type, decimal.base)
with cgutils.early_exit_if_null(c.builder, stack, base_obj):
c.pyapi.decref(sign_obj)
c.builder.store(fail_obj, ret_ptr)
exponent_obj = c.box(types.int64, decimal.exponent)
with cgutils.early_exit_if_null(c.builder, stack, exponent_obj):
c.pyapi.decref(sign_obj)
c.pyapi.decref(base_obj)
c.builder.store(fail_obj, ret_ptr)
special_code_obj = c.box(types.int64, decimal.special_code)
with cgutils.early_exit_if_null(c.builder, stack, special_code_obj):
c.pyapi.decref(sign_obj)
c.pyapi.decref(base_obj)
c.pyapi.decref(exponent_obj)
c.builder.store(fail_obj, ret_ptr)
class_obj = c.pyapi.unserialize(c.pyapi.serialize_object(Decimal))
with cgutils.early_exit_if_null(c.builder, stack, class_obj):
c.pyapi.decref(sign_obj)
c.pyapi.decref(base_obj)
c.pyapi.decref(exponent_obj)
c.pyapi.decref(special_code_obj)
c.builder.store(fail_obj, ret_ptr)
# NOTE: The result of this call is not checked as the cleanup
# has to occur regardless of whether it is successful. If it
# fails `res` is set to NULL and a Python exception is set.
res = c.pyapi.call_function_objargs(class_obj, (sign_obj, base_obj, exponent_obj, special_code_obj))
c.pyapi.decref(sign_obj)
c.pyapi.decref(base_obj)
c.pyapi.decref(exponent_obj)
c.pyapi.decref(special_code_obj)
c.pyapi.decref(class_obj)
c.builder.store(res, ret_ptr)
return c.builder.load(ret_ptr)