[llvm] [ValueTracking] Handle recursive select/PHI in ComputeKnownBits (PR #114689)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Nov 3 10:24:57 PST 2024
https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/114689
>From 15e0b4686db078bfa9bbb1303007da18fda43ba0 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 2 Nov 2024 19:08:03 -0500
Subject: [PATCH 1/5] [ValueTracking] Add tests for handling recursive
select/phi in computeKnownBits/isKnownNonZero; NFC
---
.../InstCombine/known-phi-recurse.ll | 139 ++++++++++++++++++
1 file changed, 139 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/known-phi-recurse.ll b/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
index fd3728324b8ea8..d2f280a43dbead 100644
--- a/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
+++ b/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
@@ -256,4 +256,143 @@ exit:
ret i8 %bool
}
+define i8 @knownbits_umax_select_test() {
+; CHECK-LABEL: @knownbits_umax_select_test(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[INDVAR:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[CONTAIN:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[COND0:%.*]] = call i1 @cond()
+; CHECK-NEXT: [[CONTAIN]] = call i8 @llvm.umax.i8(i8 [[INDVAR]], i8 1)
+; CHECK-NEXT: [[COND1:%.*]] = call i1 @cond()
+; CHECK-NEXT: br i1 [[COND1]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: [[BOOL:%.*]] = and i8 [[CONTAIN]], 1
+; CHECK-NEXT: ret i8 [[BOOL]]
+;
+entry:
+ br label %loop
+
+loop:
+ %indvar = phi i8 [ 0, %entry ], [ %contain, %loop ]
+ %cond0 = call i1 @cond()
+ %contain = call i8 @llvm.umax.i8(i8 1, i8 %indvar)
+ %cond1 = call i1 @cond()
+ br i1 %cond1, label %exit, label %loop
+
+exit:
+ %bool = and i8 %contain, 1
+ ret i8 %bool
+}
+
+define i8 @knownbits_phi_phi_test() {
+; CHECK-LABEL: @knownbits_phi_phi_test(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[INDVAR:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[CONTAIN:%.*]], [[LOOP_BB1:%.*]] ]
+; CHECK-NEXT: [[COND0:%.*]] = call i1 @cond()
+; CHECK-NEXT: br i1 [[COND0]], label [[LOOP_BB0:%.*]], label [[LOOP_BB1]]
+; CHECK: loop.bb0:
+; CHECK-NEXT: call void @side.effect()
+; CHECK-NEXT: br label [[LOOP_BB1]]
+; CHECK: loop.bb1:
+; CHECK-NEXT: [[CONTAIN]] = phi i8 [ 1, [[LOOP_BB0]] ], [ [[INDVAR]], [[LOOP]] ]
+; CHECK-NEXT: [[COND1:%.*]] = call i1 @cond()
+; CHECK-NEXT: br i1 [[COND1]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: [[BOOL:%.*]] = and i8 [[CONTAIN]], 1
+; CHECK-NEXT: ret i8 [[BOOL]]
+;
+entry:
+ br label %loop
+
+loop:
+ %indvar = phi i8 [ 0, %entry ], [ %contain, %loop.bb1 ]
+ %cond0 = call i1 @cond()
+ br i1 %cond0, label %loop.bb0, label %loop.bb1
+loop.bb0:
+ call void @side.effect()
+ br label %loop.bb1
+loop.bb1:
+ %contain = phi i8 [ 1, %loop.bb0 ], [ %indvar, %loop ]
+ %cond1 = call i1 @cond()
+ br i1 %cond1, label %exit, label %loop
+
+exit:
+ %bool = and i8 %contain, 1
+ ret i8 %bool
+}
+
+
+define i1 @known_non_zero_phi_phi_test() {
+; CHECK-LABEL: @known_non_zero_phi_phi_test(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[INDVAR:%.*]] = phi i8 [ 2, [[ENTRY:%.*]] ], [ [[CONTAIN:%.*]], [[LOOP_BB1:%.*]] ]
+; CHECK-NEXT: [[COND0:%.*]] = call i1 @cond()
+; CHECK-NEXT: br i1 [[COND0]], label [[LOOP_BB0:%.*]], label [[LOOP_BB1]]
+; CHECK: loop.bb0:
+; CHECK-NEXT: call void @side.effect()
+; CHECK-NEXT: br label [[LOOP_BB1]]
+; CHECK: loop.bb1:
+; CHECK-NEXT: [[CONTAIN]] = phi i8 [ 1, [[LOOP_BB0]] ], [ [[INDVAR]], [[LOOP]] ]
+; CHECK-NEXT: [[COND1:%.*]] = call i1 @cond()
+; CHECK-NEXT: br i1 [[COND1]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: [[BOOL:%.*]] = icmp eq i8 [[CONTAIN]], 0
+; CHECK-NEXT: ret i1 [[BOOL]]
+;
+entry:
+ br label %loop
+
+loop:
+ %indvar = phi i8 [ 2, %entry ], [ %contain, %loop.bb1 ]
+ %cond0 = call i1 @cond()
+ br i1 %cond0, label %loop.bb0, label %loop.bb1
+loop.bb0:
+ call void @side.effect()
+ br label %loop.bb1
+loop.bb1:
+ %contain = phi i8 [ 1, %loop.bb0 ], [ %indvar, %loop ]
+ %cond1 = call i1 @cond()
+ br i1 %cond1, label %exit, label %loop
+
+exit:
+ %bool = icmp eq i8 %contain, 0
+ ret i1 %bool
+}
+
+define i1 @known_non_zero_phi_select_test() {
+; CHECK-LABEL: @known_non_zero_phi_select_test(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[INDVAR:%.*]] = phi i8 [ 2, [[ENTRY:%.*]] ], [ [[CONTAIN:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[COND0:%.*]] = call i1 @cond()
+; CHECK-NEXT: [[CONTAIN]] = select i1 [[COND0]], i8 1, i8 [[INDVAR]]
+; CHECK-NEXT: [[COND1:%.*]] = call i1 @cond()
+; CHECK-NEXT: br i1 [[COND1]], label [[EXIT:%.*]], label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: [[BOOL:%.*]] = icmp eq i8 [[CONTAIN]], 0
+; CHECK-NEXT: ret i1 [[BOOL]]
+;
+entry:
+ br label %loop
+
+loop:
+ %indvar = phi i8 [ 2, %entry ], [ %contain, %loop ]
+ %cond0 = call i1 @cond()
+ %contain = select i1 %cond0, i8 1, i8 %indvar
+ %cond1 = call i1 @cond()
+ br i1 %cond1, label %exit, label %loop
+
+exit:
+ %bool = icmp eq i8 %contain, 0
+ ret i1 %bool
+}
+
declare i1 @cond()
+declare void @side.effect()
+
>From 72949780302262454e717d238e9e83d9d691edc6 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 2 Nov 2024 18:34:19 -0500
Subject: [PATCH 2/5] [ValueTracking] Handle recursive PHI's in
computeKnownBits
Finish porting #114008 to `KnownBits` (Follow up to #113707).
---
llvm/lib/Analysis/ValueTracking.cpp | 89 ++++++++++---------
.../Analysis/ScalarEvolution/cycled_phis.ll | 4 +-
.../Analysis/ScalarEvolution/unknown_phis.ll | 4 +-
.../InstCombine/known-phi-recurse.ll | 8 +-
.../switch-branch-fold-indirectbr-102351.ll | 24 ++---
5 files changed, 63 insertions(+), 66 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 5c20c24d0ae00a..3e934c0d123c45 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -580,6 +580,43 @@ static bool cmpExcludesZero(CmpInst::Predicate Pred, const Value *RHS) {
return true;
}
+static void breakSelfRecursivePHI(const Use *U, const PHINode *PHI,
+ Value *&ValOut, Instruction *&CtxIOut,
+ unsigned *DepthInOut = nullptr) {
+ ValOut = U->get();
+ if (ValOut == PHI)
+ return;
+ CtxIOut = PHI->getIncomingBlock(*U)->getTerminator();
+ Value *V;
+ // If the Use is a select of this phi, compute analysis on other arm to be
+ // recusion.
+ // TODO: FMin/FMax
+ if (match(ValOut, m_Select(m_Value(), m_Specific(PHI), m_Value(V))) ||
+ match(ValOut, m_Select(m_Value(), m_Value(V), m_Specific(PHI))) ||
+ match(ValOut, m_c_MaxOrMin(m_Value(V), m_Specific(PHI)))) {
+ ValOut = V;
+ return;
+ }
+
+ if (DepthInOut)
+ *DepthInOut = MaxAnalysisRecursionDepth - 1;
+
+ // Same for select, if this phi is 2-operand phi, compute analysis on other
+ // incoming value to break recursion.
+ // TODO: We could handle any number of incoming edges as long as we only have
+ // two unique values.
+ if (auto *IncPhi = dyn_cast<PHINode>(ValOut);
+ IncPhi && IncPhi->getNumIncomingValues() == 2) {
+ for (int Idx = 0; Idx < 2; ++Idx) {
+ if (IncPhi->getIncomingValue(Idx) == PHI) {
+ ValOut = IncPhi->getIncomingValue(1 - Idx);
+ CtxIOut = IncPhi->getIncomingBlock(1 - Idx)->getTerminator();
+ return;
+ }
+ }
+ }
+}
+
static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
// Use of assumptions is context-sensitive. If we don't have a context, we
// cannot use them!
@@ -1561,33 +1598,20 @@ static void computeKnownBitsFromOperator(const Operator *I,
Known.Zero.setAllBits();
Known.One.setAllBits();
- for (unsigned u = 0, e = P->getNumIncomingValues(); u < e; ++u) {
- Value *IncValue = P->getIncomingValue(u);
+ for (const Use &U : P->operands()) {
+ Value *IncValue;
+ Instruction *CxtI;
+ unsigned IncDepth = Depth + 1;
+ breakSelfRecursivePHI(&U, P, IncValue, CxtI, &IncDepth);
// Skip direct self references.
- if (IncValue == P) continue;
-
- // Recurse, but cap the recursion to one level, because we don't
- // want to waste time spinning around in loops.
- // TODO: See if we can base recursion limiter on number of incoming phi
- // edges so we don't overly clamp analysis.
- unsigned IncDepth = MaxAnalysisRecursionDepth - 1;
-
- // If the Use is a select of this phi, use the knownbit of the other
- // operand to break the recursion.
- if (auto *SI = dyn_cast<SelectInst>(IncValue)) {
- if (SI->getTrueValue() == P || SI->getFalseValue() == P) {
- IncValue = SI->getTrueValue() == P ? SI->getFalseValue()
- : SI->getTrueValue();
- IncDepth = Depth + 1;
- }
- }
+ if (IncValue == P)
+ continue;
// Change the context instruction to the "edge" that flows into the
// phi. This is important because that is where the value is actually
// "evaluated" even though it is used later somewhere else. (see also
// D69571).
- SimplifyQuery RecQ = Q.getWithoutCondContext();
- RecQ.CxtI = P->getIncomingBlock(u)->getTerminator();
+ SimplifyQuery RecQ = Q.getWithoutCondContext().getWithInstruction(CxtI);
Known2 = KnownBits(BitWidth);
computeKnownBits(IncValue, DemandedElts, Known2, IncDepth, RecQ);
@@ -6008,30 +6032,13 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
bool First = true;
for (const Use &U : P->operands()) {
- Value *IncValue = U.get();
+ Value *IncValue;
+ Instruction *CxtI;
+ breakSelfRecursivePHI(&U, P, IncValue, CxtI);
// Skip direct self references.
if (IncValue == P)
continue;
- Instruction *CxtI = P->getIncomingBlock(U)->getTerminator();
-
- // If the Use is a select of this phi, use the fp class of the other
- // operand to break the recursion. Same around 2-operand phi nodes
- Value *V;
- if (match(IncValue, m_Select(m_Value(), m_Specific(P), m_Value(V))) ||
- match(IncValue, m_Select(m_Value(), m_Value(V), m_Specific(P)))) {
- IncValue = V;
- } else if (auto *IncPhi = dyn_cast<PHINode>(IncValue);
- IncPhi && IncPhi->getNumIncomingValues() == 2) {
- for (int Idx = 0; Idx < 2; ++Idx) {
- if (IncPhi->getIncomingValue(Idx) == P) {
- IncValue = IncPhi->getIncomingValue(1 - Idx);
- CxtI = IncPhi->getIncomingBlock(1 - Idx)->getTerminator();
- break;
- }
- }
- }
-
KnownFPClass KnownSrc;
// Recurse, but cap the recursion to two levels, because we don't want
// to waste time spinning around in loops. We need at least depth 2 to
diff --git a/llvm/test/Analysis/ScalarEvolution/cycled_phis.ll b/llvm/test/Analysis/ScalarEvolution/cycled_phis.ll
index ec244595e8fe39..478bcf94daf697 100644
--- a/llvm/test/Analysis/ScalarEvolution/cycled_phis.ll
+++ b/llvm/test/Analysis/ScalarEvolution/cycled_phis.ll
@@ -8,9 +8,9 @@ define void @test_01() {
; CHECK-LABEL: 'test_01'
; CHECK-NEXT: Classifying expressions for: @test_01
; CHECK-NEXT: %phi_1 = phi i32 [ 10, %entry ], [ %phi_2, %loop ]
-; CHECK-NEXT: --> %phi_1 U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: --> %phi_1 U: [0,31) S: [0,31) Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
; CHECK-NEXT: %phi_2 = phi i32 [ 20, %entry ], [ %phi_1, %loop ]
-; CHECK-NEXT: --> %phi_2 U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: --> %phi_2 U: [0,31) S: [0,31) Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
; CHECK-NEXT: %cond = call i1 @cond()
; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
; CHECK-NEXT: Determining loop execution counts for: @test_01
diff --git a/llvm/test/Analysis/ScalarEvolution/unknown_phis.ll b/llvm/test/Analysis/ScalarEvolution/unknown_phis.ll
index bdfe38f67de0b9..c6d430f96b7de1 100644
--- a/llvm/test/Analysis/ScalarEvolution/unknown_phis.ll
+++ b/llvm/test/Analysis/ScalarEvolution/unknown_phis.ll
@@ -39,9 +39,9 @@ define void @merge_values_with_ranges_looped(ptr %a_len_ptr, ptr %b_len_ptr) {
; CHECK-NEXT: %len_b = load i32, ptr %b_len_ptr, align 4, !range !0
; CHECK-NEXT: --> %len_b U: [0,2147483647) S: [0,2147483647)
; CHECK-NEXT: %p1 = phi i32 [ %len_a, %entry ], [ %p2, %loop ]
-; CHECK-NEXT: --> %p1 U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: --> %p1 U: [0,-2147483648) S: [0,-2147483648) Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
; CHECK-NEXT: %p2 = phi i32 [ %len_b, %entry ], [ %p1, %loop ]
-; CHECK-NEXT: --> %p2 U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: --> %p2 U: [0,-2147483648) S: [0,-2147483648) Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
; CHECK-NEXT: %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
; CHECK-NEXT: --> {0,+,1}<nuw><nsw><%loop> U: [0,100) S: [0,100) Exits: 99 LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %iv.next = add i32 %iv, 1
diff --git a/llvm/test/Transforms/InstCombine/known-phi-recurse.ll b/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
index d2f280a43dbead..b7e7c9f320b04e 100644
--- a/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
+++ b/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
@@ -261,14 +261,11 @@ define i8 @knownbits_umax_select_test() {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[INDVAR:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[CONTAIN:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[COND0:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[CONTAIN]] = call i8 @llvm.umax.i8(i8 [[INDVAR]], i8 1)
; CHECK-NEXT: [[COND1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[COND1]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
-; CHECK-NEXT: [[BOOL:%.*]] = and i8 [[CONTAIN]], 1
-; CHECK-NEXT: ret i8 [[BOOL]]
+; CHECK-NEXT: ret i8 1
;
entry:
br label %loop
@@ -301,8 +298,7 @@ define i8 @knownbits_phi_phi_test() {
; CHECK-NEXT: [[COND1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[COND1]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
-; CHECK-NEXT: [[BOOL:%.*]] = and i8 [[CONTAIN]], 1
-; CHECK-NEXT: ret i8 [[BOOL]]
+; CHECK-NEXT: ret i8 [[CONTAIN]]
;
entry:
br label %loop
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-branch-fold-indirectbr-102351.ll b/llvm/test/Transforms/SimplifyCFG/switch-branch-fold-indirectbr-102351.ll
index 03aee68fa4248c..7066207255fd49 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-branch-fold-indirectbr-102351.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-branch-fold-indirectbr-102351.ll
@@ -8,29 +8,23 @@ define dso_local noundef i32 @main() {
; CHECK-LABEL: define dso_local noundef i32 @main() {
; CHECK-NEXT: [[BB:.*]]:
; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [2 x ptr], align 16
-; CHECK-NEXT: store ptr blockaddress(@main, %[[BB4:.*]]), ptr [[ALLOCA]], align 16, !tbaa [[TBAA0:![0-9]+]]
+; CHECK-NEXT: store ptr blockaddress(@main, %[[BB1:.*]]), ptr [[ALLOCA]], align 16, !tbaa [[TBAA0:![0-9]+]]
; CHECK-NEXT: [[GETELEMENTPTR:%.*]] = getelementptr inbounds [2 x ptr], ptr [[ALLOCA]], i64 0, i64 1
; CHECK-NEXT: store ptr blockaddress(@main, %[[BB10:.*]]), ptr [[GETELEMENTPTR]], align 8, !tbaa [[TBAA0]]
-; CHECK-NEXT: br label %[[BB1:.*]]
+; CHECK-NEXT: br label %[[BB1]]
; CHECK: [[BB1]]:
-; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[BB]] ], [ [[PHI8:%.*]], %[[BB7:.*]] ]
-; CHECK-NEXT: [[PHI2:%.*]] = phi i32 [ 0, %[[BB]] ], [ [[PHI9:%.*]], %[[BB7]] ]
-; CHECK-NEXT: switch i32 [[PHI]], label %[[BB7]] [
-; CHECK-NEXT: i32 0, label %[[BB12:.*]]
-; CHECK-NEXT: i32 1, label %[[BB4]]
+; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, %[[BB]] ], [ 2, %[[BB12:.*]] ]
+; CHECK-NEXT: [[PHI2:%.*]] = phi i32 [ 0, %[[BB]] ], [ [[PHI13:%.*]], %[[BB12]] ]
+; CHECK-NEXT: switch i32 [[PHI]], label %[[BB1_UNREACHABLEDEFAULT:.*]] [
+; CHECK-NEXT: i32 0, label %[[BB12]]
; CHECK-NEXT: i32 2, label %[[BB6:.*]]
; CHECK-NEXT: ]
-; CHECK: [[BB4]]:
-; CHECK-NEXT: [[PHI5:%.*]] = phi i32 [ [[PHI13:%.*]], %[[BB12]] ], [ [[PHI2]], %[[BB1]] ]
-; CHECK-NEXT: br label %[[BB7]]
; CHECK: [[BB6]]:
; CHECK-NEXT: [[CALL:%.*]] = call i32 @foo(i32 noundef [[PHI2]])
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[PHI2]], 1
; CHECK-NEXT: br label %[[BB12]]
-; CHECK: [[BB7]]:
-; CHECK-NEXT: [[PHI8]] = phi i32 [ [[PHI]], %[[BB1]] ], [ 2, %[[BB4]] ]
-; CHECK-NEXT: [[PHI9]] = phi i32 [ [[PHI2]], %[[BB1]] ], [ [[PHI5]], %[[BB4]] ]
-; CHECK-NEXT: br label %[[BB1]], !llvm.loop [[LOOP4:![0-9]+]]
+; CHECK: [[BB1_UNREACHABLEDEFAULT]]:
+; CHECK-NEXT: unreachable
; CHECK: [[BB10]]:
; CHECK-NEXT: [[CALL11:%.*]] = call i32 @foo(i32 noundef [[PHI13]])
; CHECK-NEXT: ret i32 0
@@ -39,7 +33,7 @@ define dso_local noundef i32 @main() {
; CHECK-NEXT: [[SEXT:%.*]] = sext i32 [[PHI13]] to i64
; CHECK-NEXT: [[GETELEMENTPTR14:%.*]] = getelementptr inbounds [2 x ptr], ptr [[ALLOCA]], i64 0, i64 [[SEXT]]
; CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr [[GETELEMENTPTR14]], align 8, !tbaa [[TBAA0]]
-; CHECK-NEXT: indirectbr ptr [[LOAD]], [label %[[BB4]], label %bb10]
+; CHECK-NEXT: indirectbr ptr [[LOAD]], [label %[[BB1]], label %bb10], !llvm.loop [[LOOP4:![0-9]+]]
;
bb:
%alloca = alloca [2 x ptr], align 16
>From 1ff72017ae8d3cb1ff95b54df9a17569a2bb628c Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sun, 3 Nov 2024 09:11:53 -0600
Subject: [PATCH 3/5] Drop Min/MAx
---
llvm/lib/Analysis/ValueTracking.cpp | 5 ++---
llvm/test/Transforms/InstCombine/known-phi-recurse.ll | 5 ++++-
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 3e934c0d123c45..89a65a10b48f79 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -590,10 +590,9 @@ static void breakSelfRecursivePHI(const Use *U, const PHINode *PHI,
Value *V;
// If the Use is a select of this phi, compute analysis on other arm to be
// recusion.
- // TODO: FMin/FMax
+ // TODO: Min/Max
if (match(ValOut, m_Select(m_Value(), m_Specific(PHI), m_Value(V))) ||
- match(ValOut, m_Select(m_Value(), m_Value(V), m_Specific(PHI))) ||
- match(ValOut, m_c_MaxOrMin(m_Value(V), m_Specific(PHI)))) {
+ match(ValOut, m_Select(m_Value(), m_Value(V), m_Specific(PHI)))) {
ValOut = V;
return;
}
diff --git a/llvm/test/Transforms/InstCombine/known-phi-recurse.ll b/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
index b7e7c9f320b04e..cafbb2661fae7d 100644
--- a/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
+++ b/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
@@ -261,11 +261,14 @@ define i8 @knownbits_umax_select_test() {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
+; CHECK-NEXT: [[INDVAR:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[CONTAIN:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[COND0:%.*]] = call i1 @cond()
+; CHECK-NEXT: [[CONTAIN]] = call i8 @llvm.umax.i8(i8 [[INDVAR]], i8 1)
; CHECK-NEXT: [[COND1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[COND1]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
-; CHECK-NEXT: ret i8 1
+; CHECK-NEXT: [[BOOL:%.*]] = and i8 [[CONTAIN]], 1
+; CHECK-NEXT: ret i8 [[BOOL]]
;
entry:
br label %loop
>From 7bfbd4f74287e709a157044ffcd2ffe8459fcc15 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sun, 3 Nov 2024 09:12:27 -0600
Subject: [PATCH 4/5] Fixup spelling mistake
---
llvm/lib/Analysis/ValueTracking.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 89a65a10b48f79..414a6da2c7f2f5 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -589,7 +589,7 @@ static void breakSelfRecursivePHI(const Use *U, const PHINode *PHI,
CtxIOut = PHI->getIncomingBlock(*U)->getTerminator();
Value *V;
// If the Use is a select of this phi, compute analysis on other arm to be
- // recusion.
+ // recursion.
// TODO: Min/Max
if (match(ValOut, m_Select(m_Value(), m_Specific(PHI), m_Value(V))) ||
match(ValOut, m_Select(m_Value(), m_Value(V), m_Specific(PHI)))) {
>From 99ce071e614d78fb9b7896216fd9ce7374c49995 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sun, 3 Nov 2024 12:24:42 -0600
Subject: [PATCH 5/5] Limit pathological case for PHI recursion
---
llvm/lib/Analysis/ValueTracking.cpp | 15 ++++++++-------
llvm/test/Analysis/ScalarEvolution/outer_phi.ll | 2 +-
llvm/test/Transforms/InstCombine/cast_phi.ll | 2 +-
.../Transforms/InstCombine/known-phi-recurse.ll | 6 ++----
.../LoopUnroll/AArch64/runtime-unroll-generic.ll | 4 ++--
.../LoopUnroll/runtime-unroll-remainder.ll | 4 ++--
6 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 414a6da2c7f2f5..05e27a26c7692c 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -594,26 +594,27 @@ static void breakSelfRecursivePHI(const Use *U, const PHINode *PHI,
if (match(ValOut, m_Select(m_Value(), m_Specific(PHI), m_Value(V))) ||
match(ValOut, m_Select(m_Value(), m_Value(V), m_Specific(PHI)))) {
ValOut = V;
- return;
}
- if (DepthInOut)
- *DepthInOut = MaxAnalysisRecursionDepth - 1;
-
// Same for select, if this phi is 2-operand phi, compute analysis on other
// incoming value to break recursion.
// TODO: We could handle any number of incoming edges as long as we only have
// two unique values.
- if (auto *IncPhi = dyn_cast<PHINode>(ValOut);
- IncPhi && IncPhi->getNumIncomingValues() == 2) {
+ else if (auto *IncPhi = dyn_cast<PHINode>(ValOut);
+ IncPhi && IncPhi->getNumIncomingValues() == 2) {
for (int Idx = 0; Idx < 2; ++Idx) {
if (IncPhi->getIncomingValue(Idx) == PHI) {
ValOut = IncPhi->getIncomingValue(1 - Idx);
CtxIOut = IncPhi->getIncomingBlock(1 - Idx)->getTerminator();
- return;
+ break;
}
}
}
+ // If we are going to continue recursing on a phi-node limit depth to avoid
+ // exponential explosion of work.
+ if (DepthInOut)
+ *DepthInOut = std::max(MaxAnalysisRecursionDepth - 2 + isa<PHINode>(ValOut),
+ *DepthInOut);
}
static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
diff --git a/llvm/test/Analysis/ScalarEvolution/outer_phi.ll b/llvm/test/Analysis/ScalarEvolution/outer_phi.ll
index e4a9753b240547..5dfceef1647fb5 100644
--- a/llvm/test/Analysis/ScalarEvolution/outer_phi.ll
+++ b/llvm/test/Analysis/ScalarEvolution/outer_phi.ll
@@ -7,7 +7,7 @@ define i32 @test_01(i32 %a, i32 %b) {
; CHECK-LABEL: 'test_01'
; CHECK-NEXT: Classifying expressions for: @test_01
; CHECK-NEXT: %outer.iv = phi i32 [ 0, %entry ], [ %iv.next, %outer.backedge ]
-; CHECK-NEXT: --> %outer.iv U: [0,-2147483647) S: [0,-2147483647) Exits: <<Unknown>> LoopDispositions: { %outer: Variant, %inner: Invariant }
+; CHECK-NEXT: --> %outer.iv U: [0,-2147483648) S: [0,-2147483648) Exits: <<Unknown>> LoopDispositions: { %outer: Variant, %inner: Invariant }
; CHECK-NEXT: %iv = phi i32 [ 0, %outer ], [ %iv.next, %inner.backedge ]
; CHECK-NEXT: --> {0,+,1}<nuw><nsw><%inner> U: [0,-2147483648) S: [0,-2147483648) Exits: <<Unknown>> LoopDispositions: { %inner: Computable, %outer: Variant }
; CHECK-NEXT: %iv.next = add nuw nsw i32 %iv, 1
diff --git a/llvm/test/Transforms/InstCombine/cast_phi.ll b/llvm/test/Transforms/InstCombine/cast_phi.ll
index 6b05edc31deb87..3589394aad2ac7 100644
--- a/llvm/test/Transforms/InstCombine/cast_phi.ll
+++ b/llvm/test/Transforms/InstCombine/cast_phi.ll
@@ -322,7 +322,7 @@ define i8 @trunc_in_loop_exit_block() "instcombine-no-verify-fixpoint" {
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
; CHECK-NEXT: br label [[LOOP]]
; CHECK: exit:
-; CHECK-NEXT: [[TRUNC:%.*]] = trunc i32 [[PHI]] to i8
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i32 [[PHI]] to i8
; CHECK-NEXT: ret i8 [[TRUNC]]
;
entry:
diff --git a/llvm/test/Transforms/InstCombine/known-phi-recurse.ll b/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
index cafbb2661fae7d..7401d2867da1b4 100644
--- a/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
+++ b/llvm/test/Transforms/InstCombine/known-phi-recurse.ll
@@ -43,8 +43,7 @@ define i32 @two_entry_phi_with_constant(i64 %x, i1 %c) {
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[TRUNC]], [[ENTRY:%.*]] ], [ 255, [[BODY]] ]
-; CHECK-NEXT: [[RES:%.*]] = and i32 [[PHI]], 255
-; CHECK-NEXT: ret i32 [[RES]]
+; CHECK-NEXT: ret i32 [[PHI]]
;
entry:
%y = call i64 @llvm.ctpop.i64(i64 %x)
@@ -70,8 +69,7 @@ define i32 @two_entry_phi_non_constant(i64 %x, i64 %x2, i1 %c) {
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[TRUNC]], [[ENTRY:%.*]] ], [ [[TRUNC2]], [[BODY]] ]
-; CHECK-NEXT: [[RES:%.*]] = and i32 [[PHI]], 255
-; CHECK-NEXT: ret i32 [[RES]]
+; CHECK-NEXT: ret i32 [[PHI]]
;
entry:
%y = call i64 @llvm.ctpop.i64(i64 %x)
diff --git a/llvm/test/Transforms/LoopUnroll/AArch64/runtime-unroll-generic.ll b/llvm/test/Transforms/LoopUnroll/AArch64/runtime-unroll-generic.ll
index cba80a3a2bc5fb..cd9163be23e1f5 100644
--- a/llvm/test/Transforms/LoopUnroll/AArch64/runtime-unroll-generic.ll
+++ b/llvm/test/Transforms/LoopUnroll/AArch64/runtime-unroll-generic.ll
@@ -89,7 +89,7 @@ define void @runtime_unroll_generic(i32 %arg_0, ptr %arg_1, ptr %arg_2, ptr %arg
; CHECK-A55-NEXT: [[EPIL_ITER_CMP_NOT:%.*]] = icmp eq i64 [[XTRAITER]], 1
; CHECK-A55-NEXT: br i1 [[EPIL_ITER_CMP_NOT]], label [[FOR_END]], label [[FOR_BODY6_EPIL_1:%.*]]
; CHECK-A55: for.body6.epil.1:
-; CHECK-A55-NEXT: [[INDVARS_IV_NEXT_EPIL:%.*]] = add nuw nsw i64 [[INDVARS_IV_UNR]], 1
+; CHECK-A55-NEXT: [[INDVARS_IV_NEXT_EPIL:%.*]] = or disjoint i64 [[INDVARS_IV_UNR]], 1
; CHECK-A55-NEXT: [[ARRAYIDX10_EPIL_1:%.*]] = getelementptr inbounds i16, ptr [[ARG_2]], i64 [[INDVARS_IV_NEXT_EPIL]]
; CHECK-A55-NEXT: [[TMP16:%.*]] = load i16, ptr [[ARRAYIDX10_EPIL_1]], align 2
; CHECK-A55-NEXT: [[CONV_EPIL_1:%.*]] = sext i16 [[TMP16]] to i32
@@ -104,7 +104,7 @@ define void @runtime_unroll_generic(i32 %arg_0, ptr %arg_1, ptr %arg_2, ptr %arg
; CHECK-A55-NEXT: [[EPIL_ITER_CMP_1_NOT:%.*]] = icmp eq i64 [[XTRAITER]], 2
; CHECK-A55-NEXT: br i1 [[EPIL_ITER_CMP_1_NOT]], label [[FOR_END]], label [[FOR_BODY6_EPIL_2:%.*]]
; CHECK-A55: for.body6.epil.2:
-; CHECK-A55-NEXT: [[INDVARS_IV_NEXT_EPIL_1:%.*]] = add nuw nsw i64 [[INDVARS_IV_UNR]], 2
+; CHECK-A55-NEXT: [[INDVARS_IV_NEXT_EPIL_1:%.*]] = or disjoint i64 [[INDVARS_IV_UNR]], 2
; CHECK-A55-NEXT: [[ARRAYIDX10_EPIL_2:%.*]] = getelementptr inbounds i16, ptr [[ARG_2]], i64 [[INDVARS_IV_NEXT_EPIL_1]]
; CHECK-A55-NEXT: [[TMP19:%.*]] = load i16, ptr [[ARRAYIDX10_EPIL_2]], align 2
; CHECK-A55-NEXT: [[CONV_EPIL_2:%.*]] = sext i16 [[TMP19]] to i32
diff --git a/llvm/test/Transforms/LoopUnroll/runtime-unroll-remainder.ll b/llvm/test/Transforms/LoopUnroll/runtime-unroll-remainder.ll
index 4307be7a8b5a73..93ec4db374569b 100644
--- a/llvm/test/Transforms/LoopUnroll/runtime-unroll-remainder.ll
+++ b/llvm/test/Transforms/LoopUnroll/runtime-unroll-remainder.ll
@@ -35,7 +35,7 @@ define i32 @unroll(ptr nocapture readonly %a, ptr nocapture readonly %b, i32 %N)
; CHECK-NEXT: [[EPIL_ITER_CMP_NOT:%.*]] = icmp eq i64 [[XTRAITER]], 1
; CHECK-NEXT: br i1 [[EPIL_ITER_CMP_NOT]], label [[FOR_COND_CLEANUP_LOOPEXIT_EPILOG_LCSSA:%.*]], label [[FOR_BODY_EPIL_1:%.*]]
; CHECK: for.body.epil.1:
-; CHECK-NEXT: [[INDVARS_IV_NEXT_EPIL:%.*]] = add nuw nsw i64 [[INDVARS_IV_UNR]], 1
+; CHECK-NEXT: [[INDVARS_IV_NEXT_EPIL:%.*]] = or disjoint i64 [[INDVARS_IV_UNR]], 1
; CHECK-NEXT: [[ARRAYIDX_EPIL_1:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[INDVARS_IV_NEXT_EPIL]]
; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX_EPIL_1]], align 4
; CHECK-NEXT: [[ARRAYIDX2_EPIL_1:%.*]] = getelementptr inbounds i32, ptr [[B]], i64 [[INDVARS_IV_NEXT_EPIL]]
@@ -45,7 +45,7 @@ define i32 @unroll(ptr nocapture readonly %a, ptr nocapture readonly %b, i32 %N)
; CHECK-NEXT: [[EPIL_ITER_CMP_1_NOT:%.*]] = icmp eq i64 [[XTRAITER]], 2
; CHECK-NEXT: br i1 [[EPIL_ITER_CMP_1_NOT]], label [[FOR_COND_CLEANUP_LOOPEXIT_EPILOG_LCSSA]], label [[FOR_BODY_EPIL_2:%.*]]
; CHECK: for.body.epil.2:
-; CHECK-NEXT: [[INDVARS_IV_NEXT_EPIL_1:%.*]] = add nuw nsw i64 [[INDVARS_IV_UNR]], 2
+; CHECK-NEXT: [[INDVARS_IV_NEXT_EPIL_1:%.*]] = or disjoint i64 [[INDVARS_IV_UNR]], 2
; CHECK-NEXT: [[ARRAYIDX_EPIL_2:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[INDVARS_IV_NEXT_EPIL_1]]
; CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX_EPIL_2]], align 4
; CHECK-NEXT: [[ARRAYIDX2_EPIL_2:%.*]] = getelementptr inbounds i32, ptr [[B]], i64 [[INDVARS_IV_NEXT_EPIL_1]]
More information about the llvm-commits
mailing list