canonicalizeClampLike canonicalizes the ule/ugt comparisons to ult/uge,
respectively. However, it does not update the variable holding the
comparison predicate type after doing this. Later code fails to handle
the non-canonical predicate type (specifically, the swap of
ThresholdLowIncl and ThresholdHighExcl when Pred0 has been canonicalized
from ugt to uge). This leads to the miscompile reported in PR53252. Fix
this by updating the comparison predicate after canonicalizing.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Unit Tests
Time | Test | |
---|---|---|
60,030 ms | x64 debian > MLIR.Examples/standalone::test.toy | |
60,020 ms | x64 debian > libFuzzer.libFuzzer::large.test |
Event Timeline
I added test coverage of the other cases in D119689 (the parent to this revision).
fwiw, I also verified the fixed transformation with a SAT solver: https://gist.github.com/rickyz/dd9e7c5b73ccf176a80752adf5a0b3f6 (note: doesn't fully model LLVM semantics). It would be cool if these transformations were implemented in some sort of DSL that allowed automatically generating checkers! It looks like bugs like this one are already being found by alive2 + some fuzzing though.
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | ||
---|---|---|
1381 | Why set this if it is never used below here? | |
llvm/test/Transforms/InstCombine/canonicalize-clamp-like-pattern-between-negative-and-positive-thresholds.ll | ||
214–215 | I don't think this variation adds real coverage for the fix. 'uge' will be canonicalized to 'ugt' before we reach the buggy code, so it's the same as the previous test. Do we need to swap the select operands to exercise the predicate cases further? It's not clear to me from just looking at the code or the existing tests how to hit the bug. |
Rebase/respond to comment.
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | ||
---|---|---|
1381 | This isn't used, but I did it to signal that from this point forward, the predicate has been canonicalized to SLT. I see this as defense in depth against future bugs like this one (kind of like how some C programmers like to set pointer variables to NULL after freeing them). I can see how this could mislead a reader to believe that the variable is used afterwards though. I'll remove it, since it seems like there's some preference against this. | |
llvm/test/Transforms/InstCombine/canonicalize-clamp-like-pattern-between-negative-and-positive-thresholds.ll | ||
214–215 | Ah, I misinterpreted the comment as asking for tests of different predicates regardless of whether they hit the switch cases in this code (i.e. to provide coverage should canonicalization change at some point). Thinking about this more, I think I also lean towards deleting these tests. I don't know of a good way to exercise the UGE and ULE cases in the switch on Pred0 - like you mentioned, comparisons against a constant are canonicalized to strict comparisons in canonicalizeCmpWithConstant. The only cases I can think of where this canonicalization will not happen are tautological boundary cases like icmp ule i32 %x, -1 and icmp uge i32 %x, 0 which will instead be simplified to true before we get to canonicalizing the select. |
@lebedev.ri mentioned that there may be 4 different patterns to test, but I'm not seeing how that happens. Suggestions?
It seems like we should be able to swap the select operands to make Pred0 become "ule", but then what other conditions are needed to trigger the bug?
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | ||
---|---|---|
1381 | I'm not opposed to the change, but if you add it, then there should be an assert below. That way, it is clear why we did the update. |
@rickyz Are you still looking at this, otherwise would you mind if somebody commandeered the patches to get the bug fixed soon please?
Apologies for my long-delayed response!
It seems that I was confused in my previous comments about the difficulty of exercising the non-strict equality cases - swapping the select arguments works as expected. D119689 should now contain negative tests for each possible value of Pred0. The two non-strict equality tests pass even without this change, since the bug is only triggered when Pred0 = UGT.
(edit: corrected the last sentence from ULT to UGT)
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | ||
---|---|---|
1381 | Good idea, done. |
llvm/test/Transforms/InstCombine/canonicalize-clamp-like-pattern-between-negative-and-positive-thresholds.ll | ||
---|---|---|
189 | Remove the FIXME (but please keep the reference to PR53252) |
Update commit message to clarify conditions for the bug.
Forgot to pass --verbatim last time, sorry for the noise!
I didn't step through to see why the ult/uge variant doesn't trigger the bug, but there's a test now, so LGTM.
Thanks! If it helps, here is an attempt to explain why the bug is only triggered by UGT:
Pred0 = UGT
Pred0 is canonicalized to UGE here: https://github.com/llvm/llvm-project/blob/5805cfb90127195a9e8aa09716e989f286d0e22b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp#L1307-L1316. Before this change, Pred0 would still equal UGT.
https://github.com/llvm/llvm-project/blob/5805cfb90127195a9e8aa09716e989f286d0e22b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp#L1389-L1390 is supposed to flip the thresholds when the (post-canonicalization) Pred0 is UGE.
The bug does not occur for any other values of Pred0 because:
UGE is canonicalized to UGE, and correctly flips the thresholds
ULT is canonicalized to ULT, which does not require flipping the thresholds
ULE is canonicalized to ULT, which does not require flipping the thresholds
Thanks for the review - I am not an LLVM committer, so can you please land this and D119689? Thanks!
Why set this if it is never used below here?