Assign integer expression to struct type scalar
Opened this issue · 15 comments
def test_struct_scalar_twice():
hcl.init()
dt8='int8'
dt32=hcl.Int(32)
def kernel():
stype = hcl.Struct({"x": dt8, "y": dt8})
xy = hcl.scalar(0x12, "foo", dtype=stype).v
# this also generates the same error test_struct_sideeffect
stype2 = hcl.Struct({"a": dt8, "b": dt8})
ab = hcl.scalar(xy.x, "ab", dtype=stype2).v # <-- replace xy.x with a constant and it builds
r = hcl.compute((2,), lambda i: 0, dtype=dt32)
r[0] = xy.y
r[1] = ab.a
return r
s = hcl.create_schedule([], kernel)
hcl_res = hcl.asarray(np.zeros((2,), dtype=np.int32), dtype=dt32)
f = hcl.build(s)
The code above generates an LLVM segfault:
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var
LLVM_SYMBOLIZER_PATH
to point to it):
/home/jcasas/dprive/heterocl_mlir/hcl-dialect-prototype/build/tools/hcl/python_packages/hcl_core/hcl_mlir/_mlir_libs/libHCLMLIRAggregateCAPI.so.15(+0x11b2584)[0x149f604d4584]
/home/jcasas/dprive/heterocl_mlir/hcl-dialect-prototype/build/tools/hcl/python_packages/hcl_core/hcl_mlir/_mlir_libs/libHCLMLIRAggregateCAPI.so.15(+0x11afce4)[0x149f604d1ce4]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x143c0)[0x149f6c78e3c0]
/home/jcasas/dprive/heterocl_mlir/hcl-dialect-prototype/build/tools/hcl/python_packages/hcl_core/hcl_mlir/_mlir_libs/libHCLMLIRAggregateCAPI.so.15(+0xf0b776)[0x149f6022d776]
/home/jcasas/dprive/heterocl_mlir/hcl-dialect-prototype/build/tools/hcl/python_packages/hcl_core/hcl_mlir/_mlir_libs/libHCLMLIRAggregateCAPI.so.15(+0xf53ae0)[0x149f60275ae0]
...
Not sure but this may be related to the comment about 'single expr' initialization not supported yet (#133 (comment))
Yes it's related to #133 , general expr to initialize scalar with struct type is WIP
Segmentation fault is caused by an unrealized_conversion_cast
operation from i8
to !hcl.struct<i8, i8>
.
- We should support initializing struct with general expression. However, I think we should restrict the bitwidth of the expression to be the same as the struct type. Otherwise there's ambiguity of how the struct fields should be initialized.
- We should throw error instead of warning for
unrealized_conversion_cast
, since it's not supported in execution engine anyway.
@jcasas00 What is the expected behavior when we cast i8
to !hcl.struct<i8, i8>
? Should we split i8
to i4
i4
first and extend them to i8
or extend i8
to i16
first then split?
What is the expected behavior when we cast i8 to !hcl.struct<i8, i8>
I suggest we generate an error (not warning) instead.
I think the most reasonable behavior would be the latter one -- extend the source data to the target type's size and then assign.
Rethinking this ... as long as an explicit cast works (i.e., hcl.scalar(hcl.cast("int16", ), ...)), then generating an error is fine. This way the cast is forced to be explicit.
This issue should be fixed by ddcb3d4.
I agree that the cast should be explicit. The current integer to struct checks two things:
- The integer bitwidth is the same as the bitwidth sum from the struct type
- All fields of struct are integers, i.e. no automatic bitcast.
I've tested it with this test case:
def test_struct_scalar_twice():
hcl.init()
dt4='int4'
dt8='int8'
dt32=hcl.Int(32)
def kernel():
stype = hcl.Struct({"x": dt8, "y": dt8})
xy = hcl.scalar(0x12, "foo", dtype=stype).v
stype2 = hcl.Struct({"a": dt4, "b": dt4})
ab = hcl.scalar(xy.x, "ab", dtype=stype2).v
r = hcl.compute((2,), lambda i: 0, dtype=dt32)
r[0] = xy.y
r[1] = ab.a
return r
s = hcl.create_schedule([], kernel)
hcl_res = hcl.asarray(np.zeros((2,), dtype=np.int32), dtype=dt32)
f = hcl.build(s)
f(hcl_res)
assert hcl_res.asnumpy()[0] == 0
assert hcl_res.asnumpy()[1] == 2
More test cases will be added.
Regarding "All fields of struct are integers, i.e. no automatic bitcast." , I think this may be too restrictive.
I'm interpreting this as saying we can't assign to a struct that has a field that is also a struct. For example:
t1 = hcl.Struct({"x": hcl.UInt(8), "y": hcl.UInt(8)})
t2 = hcl.Struct({"z": hcl.UInt(8), "t": t1})
I think as long as the value being assigned has the right bit width (e.g., 24 for a scalar assignment to type t2), the system should be able to assign the fields even if they are non-integers since the bit widths should match.
That said -- I recall we discussed with Sean before about supporting structs in structs but I don't think it was fully implemented. It would be a good enhancement to have to make data abstractions simpler to manage.
Misclicked ... didn't mean to close
I agree, I think we should support nested structs. What I meant by "no automatic bitcast" is that we don't do integer slice -> float/fixed bitcast.
This change seems to have caused a segfault in some other areas that used to work ...
If the following code is added:
with hcl.if_(xy.y > 0): # add this "if" condition to the test case above ...
r[0] = xy.y
r[1] = ab.a
I see the following error:
warnings.warn(self.message, category=self.category)
error: 'affine.load' op operation destroyed but still has uses
LLVM ERROR: operation destroyed but still has uses
The AST visitor removed the condition operation while it still has uses:
https://github.com/cornell-zhang/hcl-dialect-prototype/blob/ddcb3d42ba4e3dcb0153245d55ba86b2913c1981/include/hcl/Bindings/Python/hcl/build_ir.py#L2685-L2687
Actually I don't think the new changes broke it, this is the same issue as #141. We need to revamp the logical operations.
This issue is caused by struct op visitors. When their operands are removed but struct ops stayed, MLIR raises an "operation destroyed but still has uses" error. I have added remove and profile mode for struct op visitor with this commit: 7906b26.
Thanks. Looks like this one works now.
Removing the urgent tag on this as the primary issue has been resolved.