This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Update predicate when canonicalizing comparisons in canonicalizeClampLike.
ClosedPublic

Authored by rickyz on Feb 13 2022, 10:34 PM.

Details

Summary

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.

Fixes https://github.com/llvm/llvm-project/issues/53252.

Diff Detail

Event Timeline

rickyz created this revision.Feb 13 2022, 10:34 PM
rickyz requested review of this revision.Feb 13 2022, 10:34 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 13 2022, 10:34 PM
rickyz updated this revision to Diff 408334.Feb 13 2022, 11:14 PM

Add assert on predicate type.

Tests/alive2 only cover the ICMP_UGT case, but not the 3 other affected.

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.

spatel added inline comments.Feb 17 2022, 11:11 AM
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.

rickyz updated this revision to Diff 409844.Feb 17 2022, 7:34 PM
rickyz marked an inline comment as done.

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.

fhahn added a subscriber: fhahn.Apr 10 2022, 2:09 AM

Reverse ping. It would be great to squash this miscompile-bug.

Herald added a project: Restricted Project. · View Herald TranscriptApr 10 2022, 2:09 AM
RKSimon added a subscriber: RKSimon.

@rickyz Are you still looking at this, otherwise would you mind if somebody commandeered the patches to get the bug fixed soon please?

rickyz marked an inline comment as done.EditedApr 26 2022, 1:46 AM

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.

rickyz updated this revision to Diff 425152.Apr 26 2022, 1:51 AM
rickyz marked an inline comment as done.

Rebase, add assert on Pred1.

RKSimon added inline comments.Apr 26 2022, 2:17 AM
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)

rickyz updated this revision to Diff 425160.Apr 26 2022, 2:26 AM

Remove FIXME comment from incorrect merge.

rickyz updated this revision to Diff 425162.Apr 26 2022, 2:28 AM
rickyz edited the summary of this revision. (Show Details)

Update commit message to clarify conditions for the bug.

Forgot to pass --verbatim last time, sorry for the noise!

spatel accepted this revision.Apr 26 2022, 9:42 AM

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.

This revision is now accepted and ready to land.Apr 26 2022, 9:42 AM

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!

This revision was landed with ongoing or failed builds.Apr 26 2022, 2:36 PM
This revision was automatically updated to reflect the committed changes.