You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Incorrect result copying array-typed field of structured array in 0.55.0rc1 #7693
This was first discussed in discourse using 0.53.1 with a different manifestation. Shout-out to @HPLegion for suggesting trying the latest release candidate.
I can reproduce this - unfortunately we've gone from getting a lowering error in earlier versions:
numba.core.errors.LoweringError: Failed in nopython mode pipeline (step: nopython mode backend)
Can only insert i16 at [0] in [3 x i16]: got i8*
File "repro.py", line 18:
def copy(index, src, dest):
<source elided>
dest['user1'] = src[index]['user']
dest['array1'] = src[index]['array']
^
During: lowering "dest['array1'] = $26binary_subscr.11" at repro.py (18)
to silently doing the wrong thing - I think of this as a regression, and we should have a look at fixing it for 0.55. I think this will probably have been enabled by #7359 (cc @luk-f-a in case he has any thoughts).
Traceback (most recent call last):
File "/home/gmarkall/numbadev/issues/7693/ex1.py", line 14, in <module>
ex1(dest[0])
File "/home/gmarkall/numbadev/numba/numba/core/dispatcher.py", line 487, in _compile_for_args
raise e
File "/home/gmarkall/numbadev/numba/numba/core/dispatcher.py", line 420, in _compile_for_args
return_val = self.compile(tuple(argtypes))
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 125, in compile
status, retval = self._compile_cached(args, return_type)
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 152, in _compile_core
cres = compiler.compile_extra(self.targetdescr.typing_context,
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 693, in compile_extra
return pipeline.compile_extra(func)
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 429, in compile_extra
return self._compile_bytecode()
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 497, in _compile_bytecode
return self._compile_core()
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 472, in _compile_core
raise e
File "/home/gmarkall/numbadev/numba/numba/core/compiler.py", line 463, in _compile_core
pm.run(self.state)
File "/home/gmarkall/numbadev/numba/numba/core/compiler_machinery.py", line 349, in run
raise e
File "/home/gmarkall/numbadev/numba/numba/core/compiler_machinery.py", line 341, in run
self._runPass(idx, pass_inst, 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 296, in _runPass
mutated |= check(pss.run_pass, internal_state)
File "/home/gmarkall/numbadev/numba/numba/core/compiler_machinery.py", line 269, in check
mangled = func(compiler_state)
File "/home/gmarkall/numbadev/numba/numba/core/typed_passes.py", line 396, in run_pass
lower.lower()
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 183, in lower
self.lower_normal_function(self.fndesc)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 237, in lower_normal_function
entry_block_tail = self.lower_function_body()
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 266, in lower_function_body
self.lower_block(block)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 280, in lower_block
self.lower_inst(inst)
File "/home/gmarkall/numbadev/numba/numba/core/lowering.py", line 483, in lower_inst
return impl(self.builder, (target, inst.index, value))
File "/home/gmarkall/numbadev/numba/numba/core/base.py", line 1201, in __call__
res = self._imp(self._context, builder, self._sig, args, loc=loc)
File "/home/gmarkall/numbadev/numba/numba/core/base.py", line 1231, in wrapper
return fn(*args, **kwargs)
File "/home/gmarkall/numbadev/numba/numba/np/arrayobj.py", line 2640, in record_static_setitem_str
return impl(builder, (rec, val))
File "/home/gmarkall/numbadev/numba/numba/core/base.py", line 641, in wrapped
return impl(self, builder, sig, args, attr)
File "/home/gmarkall/numbadev/numba/numba/core/imputils.py", line 166, in res
return real_impl(context, builder, sig, args, attr)
File "/home/gmarkall/numbadev/numba/numba/np/arrayobj.py", line 2604, in record_setattr
val = context.cast(builder, val, valty, elemty)
File "/home/gmarkall/numbadev/numba/numba/core/base.py", line 713, in cast
return impl(self, builder, fromty, toty, val)
File "/home/gmarkall/numbadev/numba/numba/np/arrayobj.py", line 5410, in array_to_array
assert fromty.mutable != toty.mutable or toty.layout == 'A'
AssertionError
Modifying the example above so that the prior value of dest_nb is shown suggests that 4 bytes of garbage is written (as opposed to nothing being written and us seeing some old garbage values)
@stuartarchibald@gmarkall from I can see in the numba IR, the variables are correcly typed as nestedarray. The NestedArrayModel inherits from the ArrayModel so it makes sense that it's being used.
When I made the original changes I noticed that nestedarray were using the array model but in the examples I looked at, the members like nitems, shape, strides, etc were being inserted in hard-coded way like this store <2 x i64> <i64 3, i64 2>, <2 x i64>* %1, align 8, and only the data member was being copied from the nestedarray into the array. This happens in an example like this
defset1(dest):
# dest is a record, not a record array, the field array1 is a nested arrayreturndest['array1']
In this case I can see the data of dest being copied into the data of %retptr, while the other fields of %retptr are filled directly.
the whole LLVM struct of src[index]['array'] (ie nitem, strides, data, etc, etc) is being copied intodest['array1']`. Could this be an issue in the lowering of staticsetitem? Maybe it needs to be modified to treat nestedarray different from array. I'll look into this later today.
As an aside to help me understand the code, do you know what the "fe" and "be" mean in fe_type and be_type? Like in the example below
As an aside to help me understand the code, do you know what the "fe" and "be" mean in fe_type and be_type? Like in the example below
FE is frontend - the Numba types like numba.types.uint32, etc., and BE is backend - LLVM types like llvmlite.ir.types.IntType(32) - the data models provide the mapping between the frontend types and the backend types.
The logic is fine for assignment of scalars into scalar fields, but with arrays into arrays this happens:
the dptr variable has value %".74" = bitcast i8* %".73" to {i8*, i8*, i64, i64, i16*, [1 x i64], [1 x i64]}*. This a problem because %".73" is defined as %".73" = getelementptr inbounds [16 x i8], [16 x i8]* %"arg.dest", i32 0, i32 8.
So %".73" is the pointer to the data of the nestedarray, but %".74" is the wrong bitcast, to the structure that represents the full data model, not just the data
I have a very ugly poc that extracts the data of the source and stores it in the record field. #7712
I'm pretty sure that the resulting LLVM IR is not good, and I probably didn't follow established patterns in numba of how to do this. For example, is it ok to call builder.store directly from record_setattr or should I have done that via context?
On the plus side it works, so at least I know it is a step in the right direction.
Activity
gmarkall commentedon Dec 29, 2021
I can reproduce this - unfortunately we've gone from getting a lowering error in earlier versions:
to silently doing the wrong thing - I think of this as a regression, and we should have a look at fixing it for 0.55. I think this will probably have been enabled by #7359 (cc @luk-f-a in case he has any thoughts).
luk-f-a commentedon Jan 4, 2022
I think it was probably enabled by #7359. It's interesting that these work fine
and this one throws a typing error
BUT
does not throw an error. Probably related to the difference in typing between
arrayandnestedarray.I will have a look today.
luk-f-a commentedon Jan 4, 2022
small update: looking at the numba ir I didn't spot any obvious problems. there's a
which looks fine. I'll have to look into what LLVM IR this generates.
nelson2005 commentedon Jan 5, 2022
Thanks for working on this,
gmarkall commentedon Jan 5, 2022
Related issue I found whilst experimenting:
gives:
gmarkall commentedon Jan 5, 2022
With:
(which I think is a reasonable change) the
AssertionErroris no longer hit, but the answer is still not correct:gives:
gmarkall commentedon Jan 5, 2022
Modifying the example above so that the prior value of
dest_nbis shown suggests that 4 bytes of garbage is written (as opposed to nothing being written and us seeing some old garbage values)Also this sometimes segfaults:
This is handy, as valgrind should help to pinpoint where we're doing some bad memory accesses.
gmarkall commentedon Jan 5, 2022
As suggested by @stuartarchibald, somewhere the data of the record is likely to be getting interpreted as an array struct.
stuartarchibald commentedon Jan 5, 2022
Is this what's happening:
i.e. record which ends up as a block of bytes being read as an array model?
luk-f-a commentedon Jan 7, 2022
@stuartarchibald @gmarkall from I can see in the numba IR, the variables are correcly typed as
nestedarray. TheNestedArrayModelinherits from theArrayModelso it makes sense that it's being used.When I made the original changes I noticed that nestedarray were using the array model but in the examples I looked at, the members like nitems, shape, strides, etc were being inserted in hard-coded way like this
store <2 x i64> <i64 3, i64 2>, <2 x i64>* %1, align 8, and only thedatamember was being copied from the nestedarray into the array. This happens in an example like thisIn this case I can see the
dataofdestbeing copied into thedataof%retptr, while the other fields of%retptrare filled directly.However, in a case like this
the whole LLVM struct of src[index]['array']
(ie nitem, strides, data, etc, etc) is being copied intodest['array1']`. Could this be an issue in the lowering of staticsetitem? Maybe it needs to be modified to treat nestedarray different from array. I'll look into this later today.As an aside to help me understand the code, do you know what the "fe" and "be" mean in
fe_typeandbe_type? Like in the example belowgmarkall commentedon Jan 7, 2022
FE is frontend - the Numba types like
numba.types.uint32, etc., and BE is backend - LLVM types likellvmlite.ir.types.IntType(32)- the data models provide the mapping between the frontend types and the backend types.luk-f-a commentedon Jan 7, 2022
update: I've tracked the issue to this function
numba/numba/np/arrayobj.py
Lines 2589 to 2606 in bf480b9
The logic is fine for assignment of scalars into scalar fields, but with arrays into arrays this happens:
the
dptrvariable has value%".74" = bitcast i8* %".73" to {i8*, i8*, i64, i64, i16*, [1 x i64], [1 x i64]}*. This a problem because%".73"is defined as%".73" = getelementptr inbounds [16 x i8], [16 x i8]* %"arg.dest", i32 0, i32 8.So
%".73"is the pointer to the data of the nestedarray, but%".74"is the wrong bitcast, to the structure that represents the full data model, not just the dataI have a very ugly poc that extracts the data of the source and stores it in the record field. #7712
I'm pretty sure that the resulting LLVM IR is not good, and I probably didn't follow established patterns in numba of how to do this. For example, is it ok to call
builder.storedirectly fromrecord_setattror should I have done that viacontext?On the plus side it works, so at least I know it is a step in the right direction.
Add tests for Issue numba#7693