[llvm] [SCEV] Prove no-self-wrap from negative power of two step (PR #101416)
Philip Reames via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 1 12:24:51 PDT 2024
https://github.com/preames updated https://github.com/llvm/llvm-project/pull/101416
>From 2f5144156aaad00f01ea87584abfd0f8dcfe5732 Mon Sep 17 00:00:00 2001
From: Philip Reames <preames at rivosinc.com>
Date: Wed, 31 Jul 2024 14:42:14 -0700
Subject: [PATCH 1/4] [SCEV] Prove no-self-wrap from negative power of two step
We have existing code which reasons about a step evenly dividing
the iteration space is a finite loop with a single exit implying
no-self-wrap. The sign of the step doesn't effect this.
Not really a fan of the conditional negate logic here, but it seemed
like a bit of overkill to have isKnownToBeAPossibleNegativePowerOfTwo.
Any better ideas on how to structure this?
---
llvm/lib/Analysis/ScalarEvolution.cpp | 18 ++++++++-----
.../trip-count-scalable-stride.ll | 26 ++++++++++---------
2 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 264ac392b16d1..755fc9a6d35bc 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -9157,13 +9157,17 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
if (auto *ZExt = dyn_cast<SCEVZeroExtendExpr>(LHS))
InnerLHS = ZExt->getOperand();
if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(InnerLHS);
- AR && !AR->hasNoSelfWrap() && AR->getLoop() == L && AR->isAffine() &&
- isKnownToBeAPowerOfTwo(AR->getStepRecurrence(*this), /*OrZero=*/true)) {
- auto Flags = AR->getNoWrapFlags();
- Flags = setFlags(Flags, SCEV::FlagNW);
- SmallVector<const SCEV *> Operands{AR->operands()};
- Flags = StrengthenNoWrapFlags(this, scAddRecExpr, Operands, Flags);
- setNoWrapFlags(const_cast<SCEVAddRecExpr *>(AR), Flags);
+ AR && !AR->hasNoSelfWrap() && AR->getLoop() == L && AR->isAffine()) {
+ const SCEV *Step = AR->getStepRecurrence(*this);
+ if (isKnownNegative(Step))
+ Step = getNegativeSCEV(Step);
+ if (isKnownToBeAPowerOfTwo(Step, /*OrZero=*/true)) {
+ auto Flags = AR->getNoWrapFlags();
+ Flags = setFlags(Flags, SCEV::FlagNW);
+ SmallVector<const SCEV *> Operands{AR->operands()};
+ Flags = StrengthenNoWrapFlags(this, scAddRecExpr, Operands, Flags);
+ setNoWrapFlags(const_cast<SCEVAddRecExpr *>(AR), Flags);
+ }
}
}
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count-scalable-stride.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-scalable-stride.ll
index 7c9498304e939..eda28e24f1b0e 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count-scalable-stride.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-scalable-stride.ll
@@ -455,15 +455,16 @@ define void @vscale_countdown_ne(ptr nocapture %A, i32 %n) mustprogress vscale_r
; CHECK-NEXT: %start = sub i32 %n, %vscale
; CHECK-NEXT: --> ((-1 * vscale)<nsw> + %n) U: full-set S: full-set
; CHECK-NEXT: %iv = phi i32 [ %sub, %for.body ], [ %start, %entry ]
-; CHECK-NEXT: --> {((-1 * vscale)<nsw> + %n),+,(-1 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %for.body: Computable }
+; CHECK-NEXT: --> {((-1 * vscale)<nsw> + %n),+,(-1 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: ((vscale * (-1 + (-1 * (((-2 * vscale)<nsw> + %n) /u vscale))<nsw>)<nsw>) + %n) LoopDispositions: { %for.body: Computable }
; CHECK-NEXT: %arrayidx = getelementptr inbounds i32, ptr %A, i32 %iv
-; CHECK-NEXT: --> {((4 * %n) + (-4 * vscale)<nsw> + %A),+,(-4 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %for.body: Computable }
+; CHECK-NEXT: --> {((4 * %n) + (-4 * vscale)<nsw> + %A),+,(-4 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: ((4 * %n) + (vscale * (-4 + (-4 * (((-2 * vscale)<nsw> + %n) /u vscale)))) + %A) LoopDispositions: { %for.body: Computable }
; CHECK-NEXT: %sub = sub i32 %iv, %vscale
-; CHECK-NEXT: --> {((-2 * vscale)<nsw> + %n),+,(-1 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %for.body: Computable }
+; CHECK-NEXT: --> {((-2 * vscale)<nsw> + %n),+,(-1 * vscale)<nsw>}<nw><%for.body> U: full-set S: full-set Exits: ((vscale * (-2 + (-1 * (((-2 * vscale)<nsw> + %n) /u vscale))<nsw>)) + %n) LoopDispositions: { %for.body: Computable }
; CHECK-NEXT: Determining loop execution counts for: @vscale_countdown_ne
-; CHECK-NEXT: Loop %for.body: Unpredictable backedge-taken count.
-; CHECK-NEXT: Loop %for.body: Unpredictable constant max backedge-taken count.
-; CHECK-NEXT: Loop %for.body: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %for.body: backedge-taken count is (((-2 * vscale)<nsw> + %n) /u vscale)
+; CHECK-NEXT: Loop %for.body: constant max backedge-taken count is i32 2147483647
+; CHECK-NEXT: Loop %for.body: symbolic max backedge-taken count is (((-2 * vscale)<nsw> + %n) /u vscale)
+; CHECK-NEXT: Loop %for.body: Trip multiple is 1
;
entry:
%vscale = call i32 @llvm.vscale.i32()
@@ -495,15 +496,16 @@ define void @vscalex4_countdown_ne(ptr nocapture %A, i32 %n) mustprogress vscale
; CHECK-NEXT: %start = sub i32 %n, %VF
; CHECK-NEXT: --> ((-4 * vscale)<nsw> + %n) U: full-set S: full-set
; CHECK-NEXT: %iv = phi i32 [ %sub, %for.body ], [ %start, %entry ]
-; CHECK-NEXT: --> {((-4 * vscale)<nsw> + %n),+,(-4 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %for.body: Computable }
+; CHECK-NEXT: --> {((-4 * vscale)<nsw> + %n),+,(-4 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: ((vscale * (-4 + (-4 * (((-8 * vscale)<nsw> + %n) /u (4 * vscale)<nuw><nsw>))<nsw>)<nsw>) + %n) LoopDispositions: { %for.body: Computable }
; CHECK-NEXT: %arrayidx = getelementptr inbounds i32, ptr %A, i32 %iv
-; CHECK-NEXT: --> {((4 * %n) + (-16 * vscale)<nsw> + %A),+,(-16 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %for.body: Computable }
+; CHECK-NEXT: --> {((4 * %n) + (-16 * vscale)<nsw> + %A),+,(-16 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: ((4 * %n) + (vscale * (-16 + (-16 * (((-8 * vscale)<nsw> + %n) /u (4 * vscale)<nuw><nsw>)))) + %A) LoopDispositions: { %for.body: Computable }
; CHECK-NEXT: %sub = sub i32 %iv, %VF
-; CHECK-NEXT: --> {((-8 * vscale)<nsw> + %n),+,(-4 * vscale)<nsw>}<%for.body> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %for.body: Computable }
+; CHECK-NEXT: --> {((-8 * vscale)<nsw> + %n),+,(-4 * vscale)<nsw>}<nw><%for.body> U: full-set S: full-set Exits: ((vscale * (-8 + (-4 * (((-8 * vscale)<nsw> + %n) /u (4 * vscale)<nuw><nsw>))<nsw>)) + %n) LoopDispositions: { %for.body: Computable }
; CHECK-NEXT: Determining loop execution counts for: @vscalex4_countdown_ne
-; CHECK-NEXT: Loop %for.body: Unpredictable backedge-taken count.
-; CHECK-NEXT: Loop %for.body: Unpredictable constant max backedge-taken count.
-; CHECK-NEXT: Loop %for.body: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: Loop %for.body: backedge-taken count is (((-8 * vscale)<nsw> + %n) /u (4 * vscale)<nuw><nsw>)
+; CHECK-NEXT: Loop %for.body: constant max backedge-taken count is i32 536870911
+; CHECK-NEXT: Loop %for.body: symbolic max backedge-taken count is (((-8 * vscale)<nsw> + %n) /u (4 * vscale)<nuw><nsw>)
+; CHECK-NEXT: Loop %for.body: Trip multiple is 1
;
entry:
%vscale = call i32 @llvm.vscale.i32()
>From 86a72c84c0ac192d0cc790397378811dc53230c8 Mon Sep 17 00:00:00 2001
From: Philip Reames <preames at rivosinc.com>
Date: Thu, 1 Aug 2024 09:16:40 -0700
Subject: [PATCH 2/4] Rework to use OrNegative parameter
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 6 +++--
llvm/lib/Analysis/ScalarEvolution.cpp | 24 ++++++++++++--------
2 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index fbefa2bd074dd..1081357e734a6 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -1028,8 +1028,10 @@ class ScalarEvolution {
/// Test if the given expression is known to be non-zero.
bool isKnownNonZero(const SCEV *S);
- /// Test if the given expression is known to be a power of 2.
- bool isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero = false);
+ /// Test if the given expression is known to be a power of 2. OrNegative
+ /// allows matching negative power of 2s, and OrZero allows matching 0.
+ bool isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero = false,
+ bool OrNegative = false);
/// Splits SCEV expression \p S into two SCEVs. One of them is obtained from
/// \p S by substitution of all AddRec sub-expression related to loop \p L
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 755fc9a6d35bc..d831b7a7dd34f 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -9158,10 +9158,8 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
InnerLHS = ZExt->getOperand();
if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(InnerLHS);
AR && !AR->hasNoSelfWrap() && AR->getLoop() == L && AR->isAffine()) {
- const SCEV *Step = AR->getStepRecurrence(*this);
- if (isKnownNegative(Step))
- Step = getNegativeSCEV(Step);
- if (isKnownToBeAPowerOfTwo(Step, /*OrZero=*/true)) {
+ if (isKnownToBeAPowerOfTwo(AR->getStepRecurrence(*this), /*OrZero=*/true,
+ /*OrNegative*/ true)) {
auto Flags = AR->getNoWrapFlags();
Flags = setFlags(Flags, SCEV::FlagNW);
SmallVector<const SCEV *> Operands{AR->operands()};
@@ -10847,10 +10845,13 @@ bool ScalarEvolution::isKnownNonZero(const SCEV *S) {
return getUnsignedRangeMin(S) != 0;
}
-bool ScalarEvolution::isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero) {
- auto NonRecursive = [this](const SCEV *S) {
+bool ScalarEvolution::isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero,
+ bool OrNegative) {
+ auto NonRecursive = [this, OrNegative](const SCEV *S) {
if (auto *C = dyn_cast<SCEVConstant>(S))
- return C->getAPInt().isPowerOf2();
+ return C->getAPInt().isPowerOf2() ||
+ (OrNegative && C->getAPInt().isNegatedPowerOf2());
+
// The vscale_range indicates vscale is a power-of-two.
return isa<SCEVVScale>(S) && F.hasFnAttribute(Attribute::VScaleRange);
};
@@ -10861,7 +10862,11 @@ bool ScalarEvolution::isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero) {
auto *Mul = dyn_cast<SCEVMulExpr>(S);
if (!Mul)
return false;
- return all_of(Mul->operands(), NonRecursive) && (OrZero || isKnownNonZero(S));
+ return all_of(Mul->operands(), NonRecursive) &&
+ (OrZero || isKnownNonZero(S)) &&
+ (!OrNegative || llvm::count_if(Mul->operands(), [this](const SCEV *S) {
+ return isKnownNegative(S);
+ }) <= 1);
}
std::pair<const SCEV *, const SCEV *>
@@ -12794,7 +12799,8 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
if (!isLoopInvariant(RHS, L))
return false;
- if (!isKnownToBeAPowerOfTwo(AR->getStepRecurrence(*this), /*OrZero=*/true))
+ if (!isKnownToBeAPowerOfTwo(AR->getStepRecurrence(*this), /*OrZero=*/true,
+ /*OrNegative*/ true))
return false;
if (!ControlsOnlyExit || !loopHasNoAbnormalExits(L))
>From f9cae58e38f501a590eb8ed9490495c345da641a Mon Sep 17 00:00:00 2001
From: Philip Reames <preames at rivosinc.com>
Date: Thu, 1 Aug 2024 10:25:38 -0700
Subject: [PATCH 3/4] Remove stray style change
---
llvm/lib/Analysis/ScalarEvolution.cpp | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index d831b7a7dd34f..03180f3ffc372 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -9157,15 +9157,14 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
if (auto *ZExt = dyn_cast<SCEVZeroExtendExpr>(LHS))
InnerLHS = ZExt->getOperand();
if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(InnerLHS);
- AR && !AR->hasNoSelfWrap() && AR->getLoop() == L && AR->isAffine()) {
- if (isKnownToBeAPowerOfTwo(AR->getStepRecurrence(*this), /*OrZero=*/true,
- /*OrNegative*/ true)) {
- auto Flags = AR->getNoWrapFlags();
- Flags = setFlags(Flags, SCEV::FlagNW);
- SmallVector<const SCEV *> Operands{AR->operands()};
- Flags = StrengthenNoWrapFlags(this, scAddRecExpr, Operands, Flags);
- setNoWrapFlags(const_cast<SCEVAddRecExpr *>(AR), Flags);
- }
+ AR && !AR->hasNoSelfWrap() && AR->getLoop() == L && AR->isAffine() &&
+ isKnownToBeAPowerOfTwo(AR->getStepRecurrence(*this), /*OrZero=*/true,
+ /*OrNegative*/ true)) {
+ auto Flags = AR->getNoWrapFlags();
+ Flags = setFlags(Flags, SCEV::FlagNW);
+ SmallVector<const SCEV *> Operands{AR->operands()};
+ Flags = StrengthenNoWrapFlags(this, scAddRecExpr, Operands, Flags);
+ setNoWrapFlags(const_cast<SCEVAddRecExpr *>(AR), Flags);
}
}
>From 12fa773cd43b9da92a2d973e5296cca17bae8920 Mon Sep 17 00:00:00 2001
From: Philip Reames <preames at rivosinc.com>
Date: Thu, 1 Aug 2024 12:24:43 -0700
Subject: [PATCH 4/4] Update llvm/lib/Analysis/ScalarEvolution.cpp
Co-authored-by: Nikita Popov <github at npopov.com>
---
llvm/lib/Analysis/ScalarEvolution.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 03180f3ffc372..d341d282e2f64 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -9159,7 +9159,7 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(InnerLHS);
AR && !AR->hasNoSelfWrap() && AR->getLoop() == L && AR->isAffine() &&
isKnownToBeAPowerOfTwo(AR->getStepRecurrence(*this), /*OrZero=*/true,
- /*OrNegative*/ true)) {
+ /*OrNegative=*/true)) {
auto Flags = AR->getNoWrapFlags();
Flags = setFlags(Flags, SCEV::FlagNW);
SmallVector<const SCEV *> Operands{AR->operands()};
More information about the llvm-commits
mailing list