This crash was pretty common while compiling Rust for iOS (armv7). Reason - SjLj preparation step was lowering aggregate arguments as ExtractValue + InsertValue. ExtractValue has assertion which checks that there is some data in value, which is not true in case of empty (no fields) structures. Rust uses them quite extensively so this patch skips ExtractValue + InsertValue in case of empty struct or empty array.
Details
Diff Detail
Event Timeline
This is crying out for a testcase. Can you supply one?
lib/CodeGen/SjLjEHPrepare.cpp | ||
---|---|---|
255 | This comment should go inside of the 'if (ST || AT) {' block. Also, no need to mention specific languages. Something like this: An aggregate type with no fields (e.g., an empty struct) cannot have | |
258 | I'm worried about doing this. The purpose of this function is to lower the arguments so that we don't have to handle them with special code. If we do this, we are now allowing the empty aggregate to be live outside of the entry block. Actually, this whole code seems suspect to me. One way around both the original problem and your problem would be to use another instruction that would satisfy both. This one is off the top of my head: define @foo([i8, i32] %a) { %new_a = select i1 true, [i8, i32] %a, [i8, i32] null ... } Could you convert this block of code to do that and see if it still works? |
Added a test case and used 'select' as Bill proposed.
Still need advice if it is possible to unify both aggregate type branches into one, as I'm not sure if there is a combination of codegen/optimization flags which might not eliminate loading from null.
If there is a combination of flags that will result in a load from 'null', then it's a bug and will need to be fixed. The best way to guard against something like this is to create tests to make sure that the expected ASM is emitted.
I would modify the test you added here so that it checks for the expected ARM ASM once it runs through llc. Also, expand the testcase a bit, adding optimizations, etc. You won't be able to check all combinations of flags, nor should you need to. But at least check at every llc '-O' level.
test/CodeGen/Thumb/sjljehprepare-lower-empty-struct.ll | ||
---|---|---|
1 ↗ | (On Diff #11076) | Please put this in test/CodeGen/ARM. It's not thumb-specific. |
5 ↗ | (On Diff #11076) | I don't think you need the 'target *' lines here. |
7 ↗ | (On Diff #11076) | The arguments aren't used in the code. Could you modify the testcase so that they are used outside of the entry basic block? |
lib/CodeGen/SjLjEHPrepare.cpp | ||
---|---|---|
277 | You don't need to do this. Just use the 'null' value directly. :-) |
I think there's some misunderstanding of what I'd like to see here. (I confused things by using 'null' for the example.) This is the type of code I was thinking of:
def void @foo({} %c) { %fake_c = select i1 true, {} %c, {} undef ; subsequent uses of %fake_c in the code.
This should be valid for all types, whether they are aggregates or not. So you can replace the whole of that code with just this.
Fixed too wide line.
Yep, I think that a complete unification should be done in a separate commit
This comment should go inside of the 'if (ST || AT) {' block. Also, no need to mention specific languages. Something like this:
An aggregate type with no fields (e.g., an empty struct) cannot have
extract/insert instructions applied to it. In this case, there is no need to
// transfer anything.