-
Notifications
You must be signed in to change notification settings - Fork 10.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GuardWidening] Miscompile due to widening into loop-invariant WC #60234
Labels
Comments
In fact, widening is currently creating widened condition at the branch point, but it is only legal to do so in WC's location. |
xortator
added a commit
that referenced
this issue
Jan 23, 2023
#60234 explains how widening of a branch by loop-invariant condition is causing a miscompile.
CarlosAlbertoEnciso
pushed a commit
to SNSystems/llvm-debuginfo-analyzer
that referenced
this issue
Jan 24, 2023
llvm/llvm-project#60234 explains how widening of a branch by loop-invariant condition is causing a miscompile.
xortator
added a commit
that referenced
this issue
Jan 31, 2023
…branches. PR60234 When guards are represented as widenable branches, there is a tricky situation when the branch stays in loop but widenable condition doesn't. It means that the widenable condition is loop-invariant, and some other optimizations could have done changes using this fact. If widening is allowed to create widened condition inside this loop, and join the loop-invariant wc with some non-invariant facts, it can cause miscompile. See example of this at #60234. The solution is to adjust the point of generationg the wide condition, and therefore of hoisting all related parts there. It should not be before the branch, but before the widenable_condition call. The fact that `wc()` and the wide condition are in the same block guarantees that they will not violate the invariance property for any loop. Differential Revision: https://reviews.llvm.org/D142693 Reviewed By: apilipenko
|
CarlosAlbertoEnciso
pushed a commit
to SNSystems/llvm-debuginfo-analyzer
that referenced
this issue
Feb 1, 2023
…branches. PR60234 When guards are represented as widenable branches, there is a tricky situation when the branch stays in loop but widenable condition doesn't. It means that the widenable condition is loop-invariant, and some other optimizations could have done changes using this fact. If widening is allowed to create widened condition inside this loop, and join the loop-invariant wc with some non-invariant facts, it can cause miscompile. See example of this at llvm/llvm-project#60234. The solution is to adjust the point of generationg the wide condition, and therefore of hoisting all related parts there. It should not be before the branch, but before the widenable_condition call. The fact that `wc()` and the wide condition are in the same block guarantees that they will not violate the invariance property for any loop. Differential Revision: https://reviews.llvm.org/D142693 Reviewed By: apilipenko
xortator
added a commit
that referenced
this issue
Mar 20, 2023
Despite the fact that it is legal, it is not profitable. It may prevent Loop Guard Widening to happen. Because of bug described at #60234, now the guard widening is only possible when condtion we want to add is available at the point of the widenable_condition() of dominating guard. It means that, if all such calls are hoisted out of loop, and the loop conditions depend on loop-variants, we cannot widen. Hoisting is otherwise not helpful, because it does not introduce any optimization opportunities. Differential Revision: https://reviews.llvm.org/D146274 Reviewed By: apilipenko
aleks-tmb
pushed a commit
to aleks-tmb/llvm-project
that referenced
this issue
Nov 3, 2023
Turn GuardWidening and LoopPredication branch widening scheme when always `br(cond && WC)` form is maintained: ``` %wc = call i1 @widenable.condition() %guard = and i1 %cond0, %wc br i1 %guard ... ``` --> ``` %wide.check = and i1 %cond0, %cond1 %wc = call i1 @widenable.condition() %guard = and i1 %%wide.check, %wc br i1 %guard ... ``` ... into the scheme of widenable conditions widening when WC is being replaced by `NewCheck && WC`: ``` %wc = call i1 @widenable.condition() %guard = and i1 %cond0, %wc br i1 %guard ... ``` --> ``` %wc = call i1 @widenable.condition() %wide.check = and i1 %cond1, %wc %guard = and i1 %cond0, %wide.check br i1 %guard ... ``` The change aims to avoid cases when we turn loop-invarinat branch into a loop-variant one, like in this issue: llvm#60234
veselypeta
pushed a commit
to veselypeta/cherillvm
that referenced
this issue
Jun 17, 2024
llvm/llvm-project#60234 explains how widening of a branch by loop-invariant condition is causing a miscompile.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Alive2 repro: https://godbolt.org/z/P7fb4P9ab
The bug is in guard widening. However, to demonstrate why it is a bug, I will also run indvars. Consider case:
Let
start = 0
. The possible scenarios here are the following:wc1 = false
. In this case,@side_effect
is never called, we deopt withiv = 0
.wc1 = true, wc2 = false
(on 1st iteration). In this case,@side_effect
is never called, we deopt withiv = 0
.wc1 = true, wc2 = true
(on 1st iteration). In this case,@side_effect
will be called once. Deopt afterloop
will not happen (becausewc1 = true
and is loop-invariant), howevercond
will befalse
on 1st iteration (iv
will be equal tostart + 1
). So regardless onwc2
, we will then deopt withiv = 1
.As you can see, in all cases the number of calls of
@side_effect
matches the value we deoptimize with. We can expect that this fact stays true, as long as we do the right things.Now, indvars comes. Invars notices that branch by
wc1
is loop-invariant. Therefore, deopt there can only happen on 1st iteration or never. It means it is safe to replace theiv
in deopt value withstart
. Result: https://godbolt.org/z/Wq7EMT58MNote that this transform is completely legal and corresponds to LangRef for widenable condition, saying:
(when it was written, there is no such thing as
freeze
, but you can think that widenable condition outside the loop behaves exactly likefreeze(undef)
outside the loop, so it's a loop invariant).And now how the bug happens. Let's run guard-widening on top of it: https://godbolt.org/z/P7fb4P9ab
Now imagine that
wc1 = true
andwc2 = true
. On the first iteration,wide_check
is true (becauseiv = 0
andstart + 1 = 1
). So we will reach guard block and then backedge, calling@side_effect
once. Then, on the 2nd iteration,cond = false
and thereforewide.chk = false
. It means that we must deoptimize in blockexit_by_wc
withlv.lcssa = 0
.So now we've called side effect once, but after deopt, we think that
iv = 0
. If it was used to re-execute the loop in interpreter, it will make one extra iteration.My working theory is that widening into loop-invariant WCs is just wrong.
The text was updated successfully, but these errors were encountered: