-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[ConstraintElim] Use cond from header as upper bound on IV in exit BB. #94610
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
Conversation
@llvm/pr-subscribers-llvm-transforms Author: Florian Hahn (fhahn) ChangesFor loops, we can use the condition in the loop header as upper bound on the compared induction in the unique exit block, if it exists. This can be done even if there are multiple in-loop edges to the unique exit block, as any other exit may only exit earlier. More generally, we could add the OR of all exit conditions to the exit, but that's a possible future extension. Fixes #90417. Full diff: https://github.com/llvm/llvm-project/pull/94610.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 70bfa469193bf..4b3ef4d4c222c 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1031,6 +1031,23 @@ void State::addInfoForInductions(BasicBlock &BB) {
WorkList.push_back(FactOrCheck::getConditionFact(
DTN, CmpInst::ICMP_SLT, PN, B,
ConditionTy(CmpInst::ICMP_SLE, StartValue, B)));
+
+ assert(!StepOffset.isNegative() && "induction must be increasing");
+ // Try to add condition from header to the unique exit block, if there is one.
+ // When exiting either with EQ or NE, we know that the induction value must be
+ // u<= B, as a different exit may exit earlier.
+ if (Pred == CmpInst::ICMP_EQ) {
+ BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(0);
+ if (L->getUniqueExitBlock() == EB)
+ WorkList.emplace_back(FactOrCheck::getConditionFact(
+ DT.getNode(EB), CmpInst::ICMP_ULE, A, B));
+ }
+ if (Pred == CmpInst::ICMP_NE) {
+ BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(1);
+ if (L->getUniqueExitBlock() == EB)
+ WorkList.emplace_back(FactOrCheck::getConditionFact(
+ DT.getNode(EB), CmpInst::ICMP_ULE, A, B));
+ }
}
void State::addInfoFor(BasicBlock &BB) {
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit.ll
index 44ce82b51d707..86828b5e7f369 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit.ll
@@ -17,8 +17,7 @@ define i1 @multi_exiting_loop_eq_same_unique_exit_const_compare_known(ptr %s) {
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
; CHECK-NEXT: br i1 [[LATCH_C]], label %[[LOOP_HEADER]], label %[[EXIT]]
; CHECK: [[EXIT]]:
-; CHECK-NEXT: [[T:%.*]] = icmp ult i32 [[IV]], 1235
-; CHECK-NEXT: ret i1 [[T]]
+; CHECK-NEXT: ret i1 true
;
entry:
br label %loop.header
@@ -175,8 +174,7 @@ define i1 @multi_exiting_loop_eq_same_unique_exit_var_compare_known(ptr %s, i32
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
; CHECK-NEXT: br i1 [[LATCH_C]], label %[[LOOP_HEADER]], label %[[EXIT]]
; CHECK: [[EXIT]]:
-; CHECK-NEXT: [[T:%.*]] = icmp ule i32 [[IV]], [[N]]
-; CHECK-NEXT: ret i1 [[T]]
+; CHECK-NEXT: ret i1 true
;
entry:
br label %loop.header
@@ -214,8 +212,7 @@ define i1 @multi_exiting_loop_ne_same_unique_exit_const_compare_known(ptr %s) {
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
; CHECK-NEXT: br i1 [[LATCH_C]], label %[[LOOP_HEADER]], label %[[EXIT]]
; CHECK: [[EXIT]]:
-; CHECK-NEXT: [[T:%.*]] = icmp ult i32 [[IV]], 1235
-; CHECK-NEXT: ret i1 [[T]]
+; CHECK-NEXT: ret i1 true
;
entry:
br label %loop.header
|
// Try to add condition from header to the unique exit block, if there is one. | ||
// When exiting either with EQ or NE, we know that the induction value must be | ||
// u<= B, as a different exit may exit earlier. | ||
if (Pred == CmpInst::ICMP_EQ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is incorrect when the initial value of indvar is greater than the bound.
Alive2: https://alive2.llvm.org/ce/z/UYsbAQ
We need ConditionTy(CmpInst::ICMP_ULE, StartValue, B)
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point thanks! Incorrectly assume that we only reach here for monotonically increasing cases, should have added tests... Done in b7b8d02
and update the code here
dc6c9f5
to
ff5519f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
ConditionTy Precond; | ||
if (!MonotonicallyIncreasingUnsigned) | ||
Precond = {CmpInst::ICMP_ULE, StartValue, B}; | ||
if (Pred == CmpInst::ICMP_EQ) { | ||
BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(0); | ||
if (L->getUniqueExitBlock() == EB) { | ||
WorkList.emplace_back(FactOrCheck::getConditionFact( | ||
DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond)); | ||
} | ||
} | ||
if (Pred == CmpInst::ICMP_NE) { | ||
BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(1); | ||
if (L->getUniqueExitBlock() == EB) | ||
WorkList.emplace_back(FactOrCheck::getConditionFact( | ||
DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ConditionTy Precond; | |
if (!MonotonicallyIncreasingUnsigned) | |
Precond = {CmpInst::ICMP_ULE, StartValue, B}; | |
if (Pred == CmpInst::ICMP_EQ) { | |
BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(0); | |
if (L->getUniqueExitBlock() == EB) { | |
WorkList.emplace_back(FactOrCheck::getConditionFact( | |
DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond)); | |
} | |
} | |
if (Pred == CmpInst::ICMP_NE) { | |
BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(1); | |
if (L->getUniqueExitBlock() == EB) | |
WorkList.emplace_back(FactOrCheck::getConditionFact( | |
DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond)); | |
} | |
if (ICmpInst::isEquality(Pred)) { | |
ConditionTy Precond; | |
if (!MonotonicallyIncreasingUnsigned) | |
Precond = {CmpInst::ICMP_ULE, StartValue, B}; | |
BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(Pred == CmpInst::ICMP_NE); | |
if (L->getUniqueExitBlock() == EB) | |
WorkList.emplace_back(FactOrCheck::getConditionFact( | |
DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond)); | |
} |
It should be simpler :)
BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(0); | ||
if (L->getUniqueExitBlock() == EB) { | ||
WorkList.emplace_back(FactOrCheck::getConditionFact( | ||
DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond)); | |
DT.getNode(EB), CmpInst::ICMP_ULE, PN, B, Precond)); |
I found the switch from PN in all prior code to A here confusing.
// u<= B, as a different exit may exit earlier. | ||
ConditionTy Precond; | ||
if (!MonotonicallyIncreasingUnsigned) | ||
Precond = {CmpInst::ICMP_ULE, StartValue, B}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused, why do we only need this pre-condition if !MonotonicallyIncreasingUnsigned
? Let's say we have a monotonic nuw addrec, a header eq exit where the start value is greater that is not taken and some other exit that is taken. Wouldn't we miscompile that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't MonotonicallyIncreasingUnsigned
imply that StartValue u<= ExitValue == B
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. It just implies that PN is monotonically increasing from StartValue, but it doesn't make a statement about how that relates to B.
If I take your first test case from https://alive2.llvm.org/ce/z/UYsbAQ, the current patch still folds it to true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, updated to always add the precond, originally also thought that MonotonicallyIncreasingUnsigned
was implying StartValue <= B
. Test added in 3d11b3d
} | ||
if (Pred == CmpInst::ICMP_NE) { | ||
BasicBlock *EB = cast<BranchInst>(BB.getTerminator())->getSuccessor(1); | ||
if (L->getUniqueExitBlock() == EB) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the significance of the unique exit block here. Even if the exit is non-unique, wouldn't we be still add this condition fact to the header exit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the significance of the unique exit block here. Even if the exit is non-unique, wouldn't we be still add this condition fact to the header exit?
It may cause a miscompilation if there is a path from another exit block to the header exit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Isn't the argument here that even if we go through another exit, it can only exit earlier than the header, so the condition still holds? Why would it matter whether the other exit has a direct edge to the header exit vs going there indirectly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not needed I think, removed the restriction (for the case where each exit block only has a single predecessor in the loop, the patch isn't needed). Tests added in 798754f
|
Additional test coverage for a miscompile in earlier versions of #94610.
Additional test coverage with multi-exit loops for #94610.
For loops, we can use the condition in the loop header as upper bound on the compared induction in the unique exit block, if it exists. This can be done even if there are multiple in-loop edges to the unique exit block, as any other exit may only exit earlier. More generally, we could add the OR of all exit conditions to the exit, but that's a possible future extension. Fixes llvm#90417.
ff5519f
to
d51a036
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM if no compile-time impact.
Please double-check whether this actually fixes #90417 (with current phase ordering) and drop the line from the PR description if not.
Additional test coverage for a miscompile in earlier versions of llvm#94610.
Additional test coverage with multi-exit loops for llvm#94610.
Thanks, compile-time impact looks neutral. As @v01dXYZ pointed out, it the current version doesn't apply to #90417 with the current phase ordering. I removed the |
llvm#94610) For loops, we can use the condition in the loop header as upper bound on the compared induction in the unique exit block, if it exists. This can be done even if there are multiple in-loop edges to the unique exit block, as any other exit may only exit earlier. More generally, we could add the OR of all exit conditions to the exit, but that's a possible future extension. PR: llvm#94610
For loops, we can use the condition in the loop header as upper bound on the compared induction in the unique exit block, if it exists. This can be done even if there are multiple in-loop edges to the unique exit block, as any other exit may only exit earlier.
More generally, we could add the OR of all exit conditions to the exit, but that's a possible future extension.