Previously -fast-isel getelementptr would constant-fold non-constant i8
load/stores.
Details
Diff Detail
- Build Status
Buildable 7220 Build 7220: arc lint + arc unit
Event Timeline
The underlying problem is that after it folds the dynamic index into the address base register, the code at the bottom of WebAssemblyFastISel::computeAddress just sticks the getelementptr base into the address base register, accidentally overwriting the previous value. A more precise fix would be to have that code at the bottom of WebAssemblyFastISel::computeAddress return false in the case where there's already a (non-zero) base register.
For extra caution, it may be possible to have Address::setReg assert that the base is zero before overwriting it.
lib/Target/WebAssembly/WebAssemblyFastISel.cpp | ||
---|---|---|
279 | Is this continue now redundant? |
That's probably worth doing, the problem is that the line that does this problematic folding already checks Addr.getReg() == 0, so it doesn't actually fix the problem.
The real underlying cause is that we think we're folding Add instructions, but we haven't actually checked that the Use is an Add yet. Moving the unscaled add after the check makes that a correct assumption, and everything seems happy about it.
Note that adding the return false made the lit tests pass, and only failed on the wasm waterfall. I added a test case (@store_i8_with_array_alloca_gep) to cover it.
This also only fails with the -elf triple, because we store the stack pointer in memory instead of in a global, making allocas loads instead of get_globals.
For extra caution, it may be possible to have Address::setReg assert that the base is zero before overwriting it.
Probably a good idea going forward. Added.
@sunfish PTAL
lib/Target/WebAssembly/WebAssemblyFastISel.cpp | ||
---|---|---|
279 | Yep, thanks |
test/CodeGen/WebAssembly/offset-fastisel.ll | ||
---|---|---|
1 | Add -fast-isel-abort=1 |
The if (S == 1 && Addr.isRegBase() && Addr.getReg() == 0) case doesn't depend on the use being an Add.
Note that adding the return false made the lit tests pass, and only failed on the wasm waterfall. I added a test case (@store_i8_with_array_alloca_gep) to cover it.
Ah, ok. Looking at that testcase, the problem is that the case Instruction::Alloca: code has another instance as the same bug as before; it also needs to return false if there's already a base register, before assigning a new base.
The comment // An unscaled add of a register. Set it as the new base. confused me then, especially because immediately following we are dealing with Adds.
Thinking about it more deeply, this is is basically when we have getelementptr i8, i8* %pointer, i32 %someExpression, and this lowers that to the wasm equivalent of (i32.add %pointer %someExpression)?
Note that adding the return false made the lit tests pass, and only failed on the wasm waterfall. I added a test case (@store_i8_with_array_alloca_gep) to cover it.
Ah, ok. Looking at that testcase, the problem is that the case Instruction::Alloca: code has another instance as the same bug as before; it also needs to return false if there's already a base register, before assigning a new base.
Ah, makes sense. Added asserts and an isSet function that should guard against these sorts of thing.
Is this continue now redundant?