This is an archive of the discontinued LLVM Phabricator instance.

[GuardUtils] Add asserts about loop varying widenable conditions
ClosedPublic

Authored by anna on Apr 6 2023, 5:52 PM.

Details

Summary

We have now seen two miscompiles because of widening widenable
conditions at incorrect IR points and thereby changing a branch's loop
invariant condition to a loop-varying one (see PR60234 and PR61963).

This patch adds asserts in common guard utilities that we use for
widening to proactively catch these bugs in future.
Note that these asserts will not fire if we were to sink a widenable
condition from out of a loop into a loop (that's also incorrect for the
same reason as above).

Tested this without the fix for PR60234 (guard widening miscompile) and
confirmed the assert fires.

Diff Detail

Event Timeline

anna created this revision.Apr 6 2023, 5:52 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 6 2023, 5:52 PM
anna requested review of this revision.Apr 6 2023, 5:52 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 6 2023, 5:52 PM
anna added a comment.Apr 6 2023, 5:54 PM

Note that this does not fire for LoopPredication currently since we also incorrectly sunk the widenable call into the loop (thereby already making it loop-varying branch before we even used the utility widenWidenableBranch.
So, this can be landed orthogonal to D147662.

I like the general idea, but using L->isLoopInvariant might be too strict, because it can return false for things that are in fact invariant (just computed inside the loop). Can we find a better API for it?

llvm/lib/Transforms/Utils/GuardUtils.cpp
126

This might be too strict. L->isLoopInvariant is only checking placement of instruction. Imagine you have invariant a and b and WidenableBr->getCondition() = add a, b. It is still invariant, no matter if the actual add instruction is placed inside of loop or not. Or, for example, condition is a load marked as !invariant_load; it's still same value no matter where we move it.

If we had SCEV, the good approximation for what we want is isAvailableAtLoopEntry; we need something similar with instructions.

I mean, we *can* go this strict way, but be ready that it will fail when there is no real bug.

anna added inline comments.Apr 10 2023, 8:16 AM
llvm/lib/Transforms/Utils/GuardUtils.cpp
126

This is true, but it would be a pass-ordering issue. LICM should have hoisted such IR out.
We can strengthen against pass-ordering, but to what extent? In short, failing the assert shows we have one of 2 problems: either an actual bug or a missed performance opportunity, both of which needs investigation :)
I think we can use canHoistOrSinkInstruction API from LICM if we want to have lesser false positives, but that API needs AA to make sure we are hoisting correctly in presence of AA.

mkazantsev accepted this revision.Apr 11 2023, 2:21 AM

Okay... Let's give it a try, but please add WARNING to commit message. If we'll end up finding such situation, we'll need to do something about it. The person who finds this should know this is, kind of, expected.

This revision is now accepted and ready to land.Apr 11 2023, 2:21 AM
anna updated this revision to Diff 512401.Apr 11 2023, 5:06 AM

addressed review comment (and also added the message in the code above assert). Rebase.

anna updated this revision to Diff 512439.Apr 11 2023, 7:01 AM

fix test failure in test3 in GuardWidening::loop_invariant_widenable_condition.ll.
Through GW itself, we are placing an invariant condition within the loop.
Strengthened against this in API introduced isLoopInvariantWidenableCond.

anna added a comment.Apr 12 2023, 7:57 AM

Hitting this downstream. Assert maybe too strict. Will revert for now and investigate.
During offline discussion with Artur, he had an idea to change the API completely from widening a *branch* to widening a *widenable condition* (i.e. rather than change one use of a widenable condition, we should change all uses). Also, this way, we will never run into change a loop-invariant condition to a loop-varying one (since we're widening at the widenable condition which has to be outside the loop for it to be loop-invariant in the first place).