numba
numba copied to clipboard
llvmlite assertion when using deferred type as typed dict values
From: https://numba.discourse.group/t/cannot-gettypeinfo-on-a-type-that-is-unsized-error-on-jitclass-with-typed-dict-variable-that-has-deferred-type-as-value-type/1191
import numba as nb
from collections import OrderedDict
from numba import deferred_type, optional
from numba.experimental import jitclass
from numba import types
node_type = deferred_type()
@jitclass([
('parent', optional(node_type)),
('children', types.DictType(types.int32, node_type)),
])
class TreeNode(object):
def __init__(self, parent=None):
self.parent = parent
self.children = nb.typed.Dict.empty(types.int32, node_type)
node_type.define(TreeNode.class_type.instance_type)
t = TreeNode()
t2 = TreeNode(t2)
produces:
$ python repro.py
reducer_override for <class 'type'>
python: /opt/conda/conda-bld/llvmdev_1637166008009/work/lib/IR/DataLayout.cpp:713: llvm::Align llvm::DataLayout::getAlignment(llvm::Type*, bool) const: Assertion `Ty->isSized() && "Cannot getTypeInfo() on a type that is unsized!"' failed.
Aborted (core dumped)
This may be an llvmlite bug or a combination of Numba doing something bad and llvmlite not catching / handling it very well, but I'm just opening the issue with the initial reproducer to start tracking it.
Note: Edits made to the the reproducer so it can run to completion once the Numba issue is resolved (the initial revision tried to pass 1
as parent
, when it should have been a tree node type).
To do:
- [ ] Create an llvmlite reproducer / issue and investigate whether the assertion failure can easily be prevented.
Using a typed dict with a deferred type as the value type reproduces the issue:
from numba import deferred_type, njit, typed, void
from numba import types
node_type = deferred_type()
node_type.define(types.int32)
@njit(void())
def f():
typed.Dict.empty(types.int32, node_type)
produces the same assertion.
(partial) Python backtrace at segfault:
(gdb) py-bt
Traceback (most recent call first):
File "/home/gmarkall/numbadev/llvmlite/llvmlite/binding/ffi.py", line 151, in __call__
return self._cfn(*args, **kwargs)
File "/home/gmarkall/numbadev/llvmlite/llvmlite/binding/targets.py", line 154, in get_pointee_abi_size
size = ffi.lib.LLVMPY_ABISizeOfElementType(self, ty)
File "/home/gmarkall/numbadev/llvmlite/llvmlite/ir/types.py", line 53, in get_abi_size
return target_data.get_pointee_abi_size(llty)
File "/home/gmarkall/numbadev/numba/numba/core/base.py", line 1105, in get_abi_sizeof
return ty.get_abi_size(self.target_data)
File "/home/gmarkall/numbadev/numba/numba/typed/dictobject.py", line 240, in codegen
sz_val = context.get_abi_sizeof(ll_val)
File "/home/gmarkall/numbadev/numba/numba/core/base.py", line 1220, in wrapper
return fn(*args, **kwargs)
File "/home/gmarkall/numbadev/numba/numba/core/base.py", line 1190, in __call__
res = self._imp(self._context, builder, self._sig, args, loc=loc)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 1133, in _lower_call_normal
res = impl(self.builder, argvals, self.loc)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 891, in lower_call
res = self._lower_call_normal(fnty, expr, signature)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 1162, in lower_expr
res = self.lower_call(resty, expr)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 626, in lower_assign
return self.lower_expr(ty, value)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 439, in lower_inst
val = self.lower_assign(ty, inst)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 265, in lower_block
self.lower_inst(inst)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 251, in lower_function_body
self.lower_block(block)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 222, in lower_normal_function
entry_block_tail = self.lower_function_body()
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 168, in lower
self.lower_normal_function(self.fndesc)
File "/home/gmarkall/numbadev/numba/numba/core/typed_passes.py", line 394, in run_pass
lower.lower()
File "/home/gmarkall/numbadev/numba/numba/core/compiler_machinery.py", line 273, in check
mangled = func(compiler_state)
File "/home/gmarkall/numbadev/numba/numba/core/compiler_machinery.py", line 311, in _runPass
mutated |= check(pss.run_pass, internal_state)
File "/home/gmarkall/numbadev/numba/numba/core/compiler_lock.py", line 35, in _acquire_compile_lock
return func(*args, **kwargs)
File "/home/gmarkall/numbadev/numba/numba/core/compiler_machinery.py", line 356, in run
self._runPass(idx, pass_inst, state)
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 486, in _compile_core
pm.run(self.state)
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 520, in _compile_bytecode
return self._compile_core()
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 452, in compile_extra
return self._compile_bytecode()
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 716, in compile_extra
return pipeline.compile_extra(func)
File "/home/gmarkall/numbadev/numba/numba/core/dispatcher.py", line 152, in _compile_core
cres = compiler.compile_extra(self.targetdescr.typing_context,
File "/home/gmarkall/numbadev/numba/numba/core/dispatcher.py", line 139, in _compile_cached
retval = self._compile_core(args, return_type)
File "/home/gmarkall/numbadev/numba/numba/core/dispatcher.py", line 125, in compile
status, retval = self._compile_cached(args, return_type)
File "/home/gmarkall/numbadev/numba/numba/core/dispatcher.py", line 965, in compile
cres = self._compiler.compile(args, return_type)
File "/home/gmarkall/numbadev/numba/numba/core/dispatcher.py", line 363, in get_call_template
self.compile(tuple(args))
The problem is that the context doesn't ensure that the model for a deferred type has been defined prior to trying to get its actual size. The following hack:
diff --git a/numba/core/base.py b/numba/core/base.py
index 9622a3f09..54740d162 100644
--- a/numba/core/base.py
+++ b/numba/core/base.py
@@ -477,7 +477,10 @@ class BaseContext(object):
The return value is a llvmlite.ir.Type object, or None if the type
is an opaque pointer (???).
"""
- return self.data_model_manager[ty].get_data_type()
+ dmm = self.data_model_manager[ty]
+ if isinstance(dmm, datamodel.models.DeferredStructModel):
+ dmm._define()
+ return dmm.get_data_type()
def get_value_type(self, ty):
return self.data_model_manager[ty].get_value_type()
allows the above code to run to completion, but may not be the right place to insert the fix.
I'm running into the same issue but the fix doesn't work in this case: self-contained example
With the patch, my shell reports that the Python interpreter segfaults but without any message.
Upatched numba==0.56.2 gives:
Assertion failed: (Ty->isSized() && "Cannot getTypeInfo() on a type that is unsized!"), function getTypeSizeInBits, file /Users/ci/miniconda3/envs/numba-ci/conda-bld/llvmdev_1637166012004/work/include/llvm/IR/DataLayout.h, line 653.
xref #6077 (possible duplicate)
It could be a duplicate or a separate issue - this maybe depends on whether we think defining a deferred type should also ensure that the data model is aware of the definition, or whether we expect anything using the data model to make sure the definition is know to the data model at the appropriate time.
I just came across #6209 which is somewhat related to this.
This issue is marked as stale as it has had no activity in the past 30 days. Please close this issue if no further response or action is needed. Otherwise, please respond with any updates and confirm that this issue still needs to be addressed.
This shouldn't be stale - removing "needtriage" so it doesn't go stale again.
The problem is that the context doesn't ensure that the model for a deferred type has been defined prior to trying to get its actual size. The following hack:
diff --git a/numba/core/base.py b/numba/core/base.py index 9622a3f09..54740d162 100644 --- a/numba/core/base.py +++ b/numba/core/base.py @@ -477,7 +477,10 @@ class BaseContext(object): The return value is a llvmlite.ir.Type object, or None if the type is an opaque pointer (???). """ - return self.data_model_manager[ty].get_data_type() + dmm = self.data_model_manager[ty] + if isinstance(dmm, datamodel.models.DeferredStructModel): + dmm._define() + return dmm.get_data_type() def get_value_type(self, ty): return self.data_model_manager[ty].get_value_type()
allows the above code to run to completion, but may not be the right place to insert the fix.
import numba as nb
from collections import OrderedDict
from numba import deferred_type, optional
from numba.experimental import jitclass
from numba import types
node_type = deferred_type()
@jitclass([
('parent', optional(node_type)),
('children', types.DictType(types.int32, node_type)),
])
class TreeNode(object):
def __init__(self, parent=None):
self.parent = parent
self.children = nb.typed.Dict.empty(types.int32, node_type)
node_type.define(TreeNode.class_type.instance_type)
t = TreeNode()
t.children
Above code raises error:
AttributeError: 'DeferredType' object has no attribute 'name'
Above code raises error:
AttributeError: 'DeferredType' object has no attribute 'name'
Indeed, I can reproduce this - so my fix is either incomplete or incorrect :slightly_smiling_face:
Current failure happens on llvmlite/bindings/targets.py::TargetData.get_pointee_abi_size
. Below is the last five frames of the stacktrace:
[48] /Users/guilhermeleobas/git/numba/numba/core/base.py(1220)wrapper()
-> return fn(*args, **kwargs)
[49] /Users/guilhermeleobas/git/numba/numba/experimental/structref.py(305)codegen()
-> alloc_size = context.get_abi_sizeof(alloc_type)
[50] /Users/guilhermeleobas/git/numba/numba/core/base.py(1105)get_abi_sizeof()
-> return ty.get_abi_size(self.target_data)
[51] /Users/guilhermeleobas/miniconda3/envs/numba/lib/python3.9/site-packages/llvmlite/ir/types.py(53)get_abi_size()
-> return target_data.get_pointee_abi_size(llty)
[52] > /Users/guilhermeleobas/miniconda3/envs/numba/lib/python3.9/site-packages/llvmlite/binding/targets.py(156)get_pointee_abi_size()
-> size = ffi.lib.LLVMPY_ABISizeOfElementType(self, ty)
Assertion is triggered when Numba tries to get the size of the struct defined above:
301 def codegen(context, builder, signature, args):
302 # FIXME: mostly the same as jitclass ctor_impl()
303 model = context.data_model_manager[inst_type.get_data_type()]
304 alloc_type = model.get_value_type()
305 -> alloc_size = context.get_abi_sizeof(alloc_type)
306
307 meminfo = context.nrt.meminfo_alloc_dtor(
308 builder,
309 context.get_constant(types.uintp, alloc_size),
310 imp_dtor(context, builder.module, inst_type),
311 )
312 data_pointer = context.nrt.meminfo_data(builder, meminfo)
313 data_pointer = builder.bitcast(data_pointer, alloc_type.as_pointer())
314
315 # Nullify all data
316 builder.store(cgutils.get_null_value(alloc_type), data_pointer)
317
318 inst_struct = context.make_helper(builder, inst_type)
319 inst_struct.meminfo = meminfo
320
321 return inst_struct._getvalue()
(Pdb++) alloc_type
<<class 'llvmlite.ir.types.LiteralStructType'> {i64, {%"deferred.4702211712.value", i1}, {%"deferred.4702211712.value", i1}}>
Here is some code to apply @gmarkall's patch live:
from numba.core import datamodel
from numba.core.base import BaseContext
def get_data_type_patched(self, ty):
""" Patched get_data_type, as suggested here: https://github.com/numba/numba/issues/8404#issuecomment-1231473379
May not work if you're boxing for python interpreter, which triggers pickling
"""
dmm = self.data_model_manager[ty]
if isinstance(dmm, datamodel.models.DeferredStructModel):
dmm._define()
return dmm.get_data_type()
setattr(BaseContext, "get_data_type", get_data_type_patched)
I think as long as you are not boxing the jitclass for use in the interpreter, like in @guilhermeleobas's example, you won't trigger the pickling error.
I ran into some segfaults which seemed to be caused by using deferred type. So I don't recommend trying to use the patch anymore. In general, probably best to avoid using deferred types with jitclass as it seems quite buggy.