[llvm] [SCEV] Prove implied conditions via matching SCEV differences (PR #201839)
Timur Golubovich via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 29 08:50:40 PDT 2026
https://github.com/timurgol007 updated https://github.com/llvm/llvm-project/pull/201839
>From 41f1bd4498bbec137740e383588a6acb4d839ffe Mon Sep 17 00:00:00 2001
From: Timur Golubovich <timur.golubovich at intel.com>
Date: Tue, 9 Jun 2026 12:52:59 +0200
Subject: [PATCH 1/3] [SCEV] Prove implied conditions via matching SCEV
differences
Add isImpliedCondOperandsViaMatchingDiff to fold equality comparisons
when getMinusSCEV(LHS, RHS) == getMinusSCEV(FoundLHS, FoundRHS).
This handles correlated IV comparisons in loops with multiple pointer
IVs sharing the same stride.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 10 ++++++
llvm/lib/Analysis/ScalarEvolution.cpp | 32 +++++++++++++++++
.../X86/fold-correlated-iv-comparisons.ll} | 36 +++++++++----------
3 files changed, 60 insertions(+), 18 deletions(-)
rename llvm/test/Transforms/{LoopStrengthReduce/X86/lsr-icmpzero-no-inttoptr.ll => IndVarSimplify/X86/fold-correlated-iv-comparisons.ll} (56%)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 50af763614a31..0f5906ad39e70 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2324,6 +2324,16 @@ class ScalarEvolution {
const SCEV *RHS, const SCEV *FoundLHS,
const SCEV *FoundRHS);
+ /// Test whether the condition described by Pred, LHS, and RHS is true
+ /// whenever the condition described by Pred, FoundLHS, and FoundRHS is
+ /// true.
+ ///
+ /// This routine tries to analyze if the SCEV differences match.
+ bool isImpliedCondOperandsViaMatchingDiff(CmpPredicate Pred, const SCEV *LHS,
+ const SCEV *RHS,
+ const SCEV *FoundLHS,
+ const SCEV *FoundRHS);
+
/// If we know that the specified Phi is in the header of its containing
/// loop, we know the loop executes a constant number of times, and the PHI
/// node is just a recurrence involving constants, fold it.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index c0cdce982e623..f84e4f1f6f91b 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12843,6 +12843,36 @@ bool ScalarEvolution::isImpliedCondOperandsViaShift(CmpPredicate Pred,
return false;
}
+bool ScalarEvolution::isImpliedCondOperandsViaMatchingDiff(
+ CmpPredicate Pred, const SCEV *LHS, const SCEV *RHS, const SCEV *FoundLHS,
+ const SCEV *FoundRHS) {
+ // Only valid for equality predicates: (A == B) implies (C == D) when
+ // the SCEV difference A - B equals C - D (they check the same
+ // underlying relationship at every iteration).
+ if (!ICmpInst::isEquality(Pred))
+ return false;
+
+ // Restrict to cases involving loop recurrences - that's where this
+ // pattern arises (correlated IV comparisons). This avoids calling
+ // getMinusSCEV on arbitrary non-loop expressions.
+ if (!isa<SCEVAddRecExpr>(LHS) && !isa<SCEVAddRecExpr>(RHS) &&
+ !isa<SCEVAddRecExpr>(FoundLHS) && !isa<SCEVAddRecExpr>(FoundRHS))
+ return false;
+
+ // Compute differences. For pointer-typed operands sharing the same base,
+ // getMinusSCEV strips the common base and returns an integer SCEV.
+ // For example, {base,+,8} - (base+8*n) = {-8n,+,8}
+ const SCEV *FoundDiff = getMinusSCEV(FoundLHS, FoundRHS);
+ if (isa<SCEVCouldNotCompute>(FoundDiff))
+ return false;
+
+ const SCEV *Diff = getMinusSCEV(LHS, RHS);
+ if (isa<SCEVCouldNotCompute>(Diff))
+ return false;
+
+ return Diff == FoundDiff;
+}
+
bool ScalarEvolution::isImpliedCondOperands(CmpPredicate Pred, const SCEV *LHS,
const SCEV *RHS,
const SCEV *FoundLHS,
@@ -12855,6 +12885,8 @@ bool ScalarEvolution::isImpliedCondOperands(CmpPredicate Pred, const SCEV *LHS,
isImpliedCondOperandsViaShift(Pred, LHS, RHS, FoundLHS, FoundRHS) ||
isImpliedCondOperandsViaAddRecStart(Pred, LHS, RHS, FoundLHS, FoundRHS,
CtxI) ||
+ isImpliedCondOperandsViaMatchingDiff(Pred, LHS, RHS, FoundLHS,
+ FoundRHS) ||
isImpliedCondOperandsHelper(Pred, LHS, RHS, FoundLHS, FoundRHS);
}
diff --git a/llvm/test/Transforms/LoopStrengthReduce/X86/lsr-icmpzero-no-inttoptr.ll b/llvm/test/Transforms/IndVarSimplify/X86/fold-correlated-iv-comparisons.ll
similarity index 56%
rename from llvm/test/Transforms/LoopStrengthReduce/X86/lsr-icmpzero-no-inttoptr.ll
rename to llvm/test/Transforms/IndVarSimplify/X86/fold-correlated-iv-comparisons.ll
index 0d6a374f86692..8a974ca670cc8 100644
--- a/llvm/test/Transforms/LoopStrengthReduce/X86/lsr-icmpzero-no-inttoptr.ll
+++ b/llvm/test/Transforms/IndVarSimplify/X86/fold-correlated-iv-comparisons.ll
@@ -1,31 +1,29 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=loop-reduce -S %s | FileCheck %s
+; RUN: opt -passes=indvars -S %s | FileCheck %s
;
-; Verify that LSR keeps ICmpZero comparisons in the integer domain when the
-; original operands are pointers, avoiding inttoptr casts entirely. The loop
-; has three pointer IVs sharing the same stride; LSR should produce a single
-; integer IV and compare it directly against the precomputed bound.
-; Target-dependent because TTI must report that scaled addressing is legal.
+; Verify that IndVarSimplify folds correlated IV comparisons. When multiple
+; pointer IVs share the same stride and their bounds are equidistant from their
+; bases, a dominating equality check (p2 == bound2) implies the same for the
+; other IVs (p1 == bound1, p3 == bound3). The redundant icmp ne instructions
+; in the dominated block should be folded to false.
+; Target-dependent because SCEV pointer handling depends on the data layout.
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
-define void @test_no_inttoptr_casts(ptr %base1, ptr %base2, ptr %base3, i64 %n) {
-; CHECK-LABEL: @test_no_inttoptr_casts(
+define void @fold_correlated_iv_comparisons(ptr %base1, ptr %base2, ptr %base3, i64 %n) {
+; CHECK-LABEL: @fold_correlated_iv_comparisons(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[N:%.*]], 3
+; CHECK-NEXT: [[BOUND2:%.*]] = getelementptr i64, ptr [[BASE2:%.*]], i64 [[N:%.*]]
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[LSR_IV:%.*]] = phi i64 [ [[LSR_IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ], [ 0, [[ENTRY:%.*]] ]
-; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[BASE1:%.*]], i64 [[LSR_IV]]
-; CHECK-NEXT: [[SCEVGEP1:%.*]] = getelementptr i8, ptr [[BASE2:%.*]], i64 [[LSR_IV]]
-; CHECK-NEXT: [[SCEVGEP2:%.*]] = getelementptr i8, ptr [[BASE3:%.*]], i64 [[LSR_IV]]
-; CHECK-NEXT: [[CMP_EXIT:%.*]] = icmp eq i64 [[TMP0]], [[LSR_IV]]
+; CHECK-NEXT: [[SCEVGEP:%.*]] = phi ptr [ [[BASE1:%.*]], [[ENTRY:%.*]] ], [ [[P1_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[SCEVGEP1:%.*]] = phi ptr [ [[BASE2]], [[ENTRY]] ], [ [[P2_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[SCEVGEP2:%.*]] = phi ptr [ [[BASE3:%.*]], [[ENTRY]] ], [ [[P3_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[CMP_EXIT:%.*]] = icmp eq ptr [[SCEVGEP1]], [[BOUND2]]
; CHECK-NEXT: br i1 [[CMP_EXIT]], label [[CHECK:%.*]], label [[BODY:%.*]]
; CHECK: check:
-; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i64 [[TMP0]], [[LSR_IV]]
-; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i64 [[TMP0]], [[LSR_IV]]
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP3]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 false, i1 true, i1 false
; CHECK-NEXT: br i1 [[SEL]], label [[BODY]], label [[EXIT:%.*]]
; CHECK: body:
; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[SCEVGEP]], align 8
@@ -34,7 +32,9 @@ define void @test_no_inttoptr_casts(ptr %base1, ptr %base2, ptr %base3, i64 %n)
; CHECK-NEXT: store i64 [[SUM]], ptr [[SCEVGEP2]], align 8
; CHECK-NEXT: br label [[LOOP_LATCH]]
; CHECK: loop.latch:
-; CHECK-NEXT: [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], 8
+; CHECK-NEXT: [[P1_NEXT]] = getelementptr inbounds i8, ptr [[SCEVGEP]], i64 8
+; CHECK-NEXT: [[P2_NEXT]] = getelementptr inbounds i8, ptr [[SCEVGEP1]], i64 8
+; CHECK-NEXT: [[P3_NEXT]] = getelementptr inbounds i8, ptr [[SCEVGEP2]], i64 8
; CHECK-NEXT: br label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
>From d14b0b4dcbe5a745cd9d3b271b9b2cda361b91fd Mon Sep 17 00:00:00 2001
From: Timur Golubovich <timur.golubovich at intel.com>
Date: Tue, 16 Jun 2026 17:34:17 +0200
Subject: [PATCH 2/3] Improved condition check (thanks @nikic). Added evident
loop matching check.
---
llvm/lib/Analysis/ScalarEvolution.cpp | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index f84e4f1f6f91b..9072ec76ac620 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12855,8 +12855,23 @@ bool ScalarEvolution::isImpliedCondOperandsViaMatchingDiff(
// Restrict to cases involving loop recurrences - that's where this
// pattern arises (correlated IV comparisons). This avoids calling
// getMinusSCEV on arbitrary non-loop expressions.
- if (!isa<SCEVAddRecExpr>(LHS) && !isa<SCEVAddRecExpr>(RHS) &&
- !isa<SCEVAddRecExpr>(FoundLHS) && !isa<SCEVAddRecExpr>(FoundRHS))
+ if ((!isa<SCEVAddRecExpr>(LHS) && !isa<SCEVAddRecExpr>(RHS)) ||
+ (!isa<SCEVAddRecExpr>(FoundLHS) && !isa<SCEVAddRecExpr>(FoundRHS)))
+ return false;
+
+ // AddRecs from different loops can never produce matching differences.
+ const SCEVAddRecExpr *QueryAddRec = dyn_cast<SCEVAddRecExpr>(LHS);
+ if (!QueryAddRec)
+ QueryAddRec = cast<SCEVAddRecExpr>(RHS);
+ const SCEVAddRecExpr *FoundAddRec = dyn_cast<SCEVAddRecExpr>(FoundLHS);
+ if (!FoundAddRec)
+ FoundAddRec = cast<SCEVAddRecExpr>(FoundRHS);
+ if (QueryAddRec->getLoop() != FoundAddRec->getLoop())
+ return false;
+
+ // If the strides differ, the differences can never match.
+ if (QueryAddRec->getStepRecurrence(*this) !=
+ FoundAddRec->getStepRecurrence(*this))
return false;
// Compute differences. For pointer-typed operands sharing the same base,
>From 216491a5d78b9c2ceb1a8909eb41cfb58f8f20ed Mon Sep 17 00:00:00 2001
From: Timur Golubovich <timur.golubovich at intel.com>
Date: Mon, 29 Jun 2026 17:49:29 +0200
Subject: [PATCH 3/3] add swapped-operand and negative test cases
---
.../X86/fold-correlated-iv-comparisons.ll | 293 +++++++++++++++++-
1 file changed, 287 insertions(+), 6 deletions(-)
diff --git a/llvm/test/Transforms/IndVarSimplify/X86/fold-correlated-iv-comparisons.ll b/llvm/test/Transforms/IndVarSimplify/X86/fold-correlated-iv-comparisons.ll
index 8a974ca670cc8..5c37c7201a322 100644
--- a/llvm/test/Transforms/IndVarSimplify/X86/fold-correlated-iv-comparisons.ll
+++ b/llvm/test/Transforms/IndVarSimplify/X86/fold-correlated-iv-comparisons.ll
@@ -1,12 +1,12 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=indvars -S %s | FileCheck %s
;
-; Verify that IndVarSimplify folds correlated IV comparisons. When multiple
-; pointer IVs share the same stride and their bounds are equidistant from their
-; bases, a dominating equality check (p2 == bound2) implies the same for the
-; other IVs (p1 == bound1, p3 == bound3). The redundant icmp ne instructions
-; in the dominated block should be folded to false.
-; Target-dependent because SCEV pointer handling depends on the data layout.
+; Verify that IndVarSimplify folds correlated IV comparisons: when pointer IVs
+; share a stride and their bounds are equidistant from their bases, a dominating
+; equality (p2 == bound2) folds the dominated icmp ne checks for the other IVs
+; to false. Target-dependent because SCEV pointer handling depends on data layout.
+; Positive cases: canonical, and with the dominated compare's operands swapped.
+; Negative cases: non-equality predicate, mismatched stride, mismatched bound offset.
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
@@ -74,3 +74,284 @@ loop.latch:
exit:
ret void
}
+
+; The dominated compares have their operands swapped (bound on the LHS).
+; SCEV canonicalization should normalize this, so the fold still applies.
+define void @fold_swapped_dominated_compare(ptr %base1, ptr %base2, ptr %base3, i64 %n) {
+; CHECK-LABEL: @fold_swapped_dominated_compare(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[BOUND2:%.*]] = getelementptr i64, ptr [[BASE2:%.*]], i64 [[N:%.*]]
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[SCEVGEP:%.*]] = phi ptr [ [[BASE1:%.*]], [[ENTRY:%.*]] ], [ [[P1_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[SCEVGEP1:%.*]] = phi ptr [ [[BASE2]], [[ENTRY]] ], [ [[P2_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[SCEVGEP2:%.*]] = phi ptr [ [[BASE3:%.*]], [[ENTRY]] ], [ [[P3_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[CMP_EXIT:%.*]] = icmp eq ptr [[SCEVGEP1]], [[BOUND2]]
+; CHECK-NEXT: br i1 [[CMP_EXIT]], label [[CHECK:%.*]], label [[BODY:%.*]]
+; CHECK: check:
+; CHECK-NEXT: [[SEL:%.*]] = select i1 false, i1 true, i1 false
+; CHECK-NEXT: br i1 [[SEL]], label [[BODY]], label [[EXIT:%.*]]
+; CHECK: body:
+; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[SCEVGEP]], align 8
+; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[SCEVGEP1]], align 8
+; CHECK-NEXT: [[SUM:%.*]] = add i64 [[V1]], [[V2]]
+; CHECK-NEXT: store i64 [[SUM]], ptr [[SCEVGEP2]], align 8
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[P1_NEXT]] = getelementptr inbounds i8, ptr [[SCEVGEP]], i64 8
+; CHECK-NEXT: [[P2_NEXT]] = getelementptr inbounds i8, ptr [[SCEVGEP1]], i64 8
+; CHECK-NEXT: [[P3_NEXT]] = getelementptr inbounds i8, ptr [[SCEVGEP2]], i64 8
+; CHECK-NEXT: br label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ %bound1 = getelementptr i64, ptr %base1, i64 %n
+ %bound2 = getelementptr i64, ptr %base2, i64 %n
+ %bound3 = getelementptr i64, ptr %base3, i64 %n
+ br label %loop
+
+loop:
+ %p1 = phi ptr [ %base1, %entry ], [ %p1.next, %loop.latch ]
+ %p2 = phi ptr [ %base2, %entry ], [ %p2.next, %loop.latch ]
+ %p3 = phi ptr [ %base3, %entry ], [ %p3.next, %loop.latch ]
+ %cmp.exit = icmp eq ptr %p2, %bound2
+ br i1 %cmp.exit, label %check, label %body
+
+check:
+ %cmp1 = icmp ne ptr %bound1, %p1
+ %cmp3 = icmp ne ptr %bound3, %p3
+ %sel = select i1 %cmp1, i1 true, i1 %cmp3
+ br i1 %sel, label %body, label %exit
+
+body:
+ %v1 = load i64, ptr %p1, align 8
+ %v2 = load i64, ptr %p2, align 8
+ %sum = add i64 %v1, %v2
+ store i64 %sum, ptr %p3, align 8
+ br label %loop.latch
+
+loop.latch:
+ %p1.next = getelementptr inbounds i8, ptr %p1, i64 8
+ %p2.next = getelementptr inbounds i8, ptr %p2, i64 8
+ %p3.next = getelementptr inbounds i8, ptr %p3, i64 8
+ br label %loop
+
+exit:
+ ret void
+}
+
+; Negative test: the dominating predicate is an ult. The
+; difference-matching reasoning only handles equality, so the dominated icmp ne
+; compares must remain.
+define void @no_fold_non_equality_predicate(ptr %base1, ptr %base2, ptr %base3, i64 %n) {
+; CHECK-LABEL: @no_fold_non_equality_predicate(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[BOUND1:%.*]] = getelementptr i64, ptr [[BASE1:%.*]], i64 [[N:%.*]]
+; CHECK-NEXT: [[BOUND2:%.*]] = getelementptr i64, ptr [[BASE2:%.*]], i64 [[N]]
+; CHECK-NEXT: [[BOUND3:%.*]] = getelementptr i64, ptr [[BASE3:%.*]], i64 [[N]]
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[P1:%.*]] = phi ptr [ [[BASE1]], [[ENTRY:%.*]] ], [ [[P1_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[P2:%.*]] = phi ptr [ [[BASE2]], [[ENTRY]] ], [ [[P2_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[P3:%.*]] = phi ptr [ [[BASE3]], [[ENTRY]] ], [ [[P3_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[CMP_EXIT:%.*]] = icmp uge ptr [[P2]], [[BOUND2]]
+; CHECK-NEXT: br i1 [[CMP_EXIT]], label [[CHECK:%.*]], label [[BODY:%.*]]
+; CHECK: check:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne ptr [[P1]], [[BOUND1]]
+; CHECK-NEXT: [[CMP3:%.*]] = icmp ne ptr [[P3]], [[BOUND3]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP3]]
+; CHECK-NEXT: br i1 [[SEL]], label [[BODY]], label [[EXIT:%.*]]
+; CHECK: body:
+; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[P1]], align 8
+; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[P2]], align 8
+; CHECK-NEXT: [[SUM:%.*]] = add i64 [[V1]], [[V2]]
+; CHECK-NEXT: store i64 [[SUM]], ptr [[P3]], align 8
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[P1_NEXT]] = getelementptr inbounds i8, ptr [[P1]], i64 8
+; CHECK-NEXT: [[P2_NEXT]] = getelementptr inbounds i8, ptr [[P2]], i64 8
+; CHECK-NEXT: [[P3_NEXT]] = getelementptr inbounds i8, ptr [[P3]], i64 8
+; CHECK-NEXT: br label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ %bound1 = getelementptr i64, ptr %base1, i64 %n
+ %bound2 = getelementptr i64, ptr %base2, i64 %n
+ %bound3 = getelementptr i64, ptr %base3, i64 %n
+ br label %loop
+
+loop:
+ %p1 = phi ptr [ %base1, %entry ], [ %p1.next, %loop.latch ]
+ %p2 = phi ptr [ %base2, %entry ], [ %p2.next, %loop.latch ]
+ %p3 = phi ptr [ %base3, %entry ], [ %p3.next, %loop.latch ]
+ %cmp.exit = icmp uge ptr %p2, %bound2
+ br i1 %cmp.exit, label %check, label %body
+
+check:
+ %cmp1 = icmp ne ptr %p1, %bound1
+ %cmp3 = icmp ne ptr %p3, %bound3
+ %sel = select i1 %cmp1, i1 true, i1 %cmp3
+ br i1 %sel, label %body, label %exit
+
+body:
+ %v1 = load i64, ptr %p1, align 8
+ %v2 = load i64, ptr %p2, align 8
+ %sum = add i64 %v1, %v2
+ store i64 %sum, ptr %p3, align 8
+ br label %loop.latch
+
+loop.latch:
+ %p1.next = getelementptr inbounds i8, ptr %p1, i64 8
+ %p2.next = getelementptr inbounds i8, ptr %p2, i64 8
+ %p3.next = getelementptr inbounds i8, ptr %p3, i64 8
+ br label %loop
+
+exit:
+ ret void
+}
+
+; Negative test: p1/p3 advance by a different stride than p2. The SCEV
+; difference for the dominated compares does not match the dominating compare,
+; so the fold must not happen.
+define void @no_fold_mismatched_stride(ptr %base1, ptr %base2, ptr %base3, i64 %n) {
+; CHECK-LABEL: @no_fold_mismatched_stride(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[BOUND1:%.*]] = getelementptr i64, ptr [[BASE1:%.*]], i64 [[N:%.*]]
+; CHECK-NEXT: [[BOUND2:%.*]] = getelementptr i64, ptr [[BASE2:%.*]], i64 [[N]]
+; CHECK-NEXT: [[BOUND3:%.*]] = getelementptr i64, ptr [[BASE3:%.*]], i64 [[N]]
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[P1:%.*]] = phi ptr [ [[BASE1]], [[ENTRY:%.*]] ], [ [[P1_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[P2:%.*]] = phi ptr [ [[BASE2]], [[ENTRY]] ], [ [[P2_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[P3:%.*]] = phi ptr [ [[BASE3]], [[ENTRY]] ], [ [[P3_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[CMP_EXIT:%.*]] = icmp eq ptr [[P2]], [[BOUND2]]
+; CHECK-NEXT: br i1 [[CMP_EXIT]], label [[CHECK:%.*]], label [[BODY:%.*]]
+; CHECK: check:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne ptr [[P1]], [[BOUND1]]
+; CHECK-NEXT: [[CMP3:%.*]] = icmp ne ptr [[P3]], [[BOUND3]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP3]]
+; CHECK-NEXT: br i1 [[SEL]], label [[BODY]], label [[EXIT:%.*]]
+; CHECK: body:
+; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[P1]], align 8
+; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[P2]], align 8
+; CHECK-NEXT: [[SUM:%.*]] = add i64 [[V1]], [[V2]]
+; CHECK-NEXT: store i64 [[SUM]], ptr [[P3]], align 8
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[P1_NEXT]] = getelementptr inbounds i8, ptr [[P1]], i64 16
+; CHECK-NEXT: [[P2_NEXT]] = getelementptr inbounds i8, ptr [[P2]], i64 8
+; CHECK-NEXT: [[P3_NEXT]] = getelementptr inbounds i8, ptr [[P3]], i64 16
+; CHECK-NEXT: br label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ %bound1 = getelementptr i64, ptr %base1, i64 %n
+ %bound2 = getelementptr i64, ptr %base2, i64 %n
+ %bound3 = getelementptr i64, ptr %base3, i64 %n
+ br label %loop
+
+loop:
+ %p1 = phi ptr [ %base1, %entry ], [ %p1.next, %loop.latch ]
+ %p2 = phi ptr [ %base2, %entry ], [ %p2.next, %loop.latch ]
+ %p3 = phi ptr [ %base3, %entry ], [ %p3.next, %loop.latch ]
+ %cmp.exit = icmp eq ptr %p2, %bound2
+ br i1 %cmp.exit, label %check, label %body
+
+check:
+ %cmp1 = icmp ne ptr %p1, %bound1
+ %cmp3 = icmp ne ptr %p3, %bound3
+ %sel = select i1 %cmp1, i1 true, i1 %cmp3
+ br i1 %sel, label %body, label %exit
+
+body:
+ %v1 = load i64, ptr %p1, align 8
+ %v2 = load i64, ptr %p2, align 8
+ %sum = add i64 %v1, %v2
+ store i64 %sum, ptr %p3, align 8
+ br label %loop.latch
+
+loop.latch:
+ %p1.next = getelementptr inbounds i8, ptr %p1, i64 16
+ %p2.next = getelementptr inbounds i8, ptr %p2, i64 8
+ %p3.next = getelementptr inbounds i8, ptr %p3, i64 16
+ br label %loop
+
+exit:
+ ret void
+}
+
+; Negative test: the bounds for p1/p3 are at a different offset from their
+; bases than p2's bound. The SCEV differences do not match, so the dominated
+; comparisons cannot be folded.
+define void @no_fold_mismatched_bound_offset(ptr %base1, ptr %base2, ptr %base3, i64 %n) {
+; CHECK-LABEL: @no_fold_mismatched_bound_offset(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[BOUND1:%.*]] = getelementptr i64, ptr [[BASE1:%.*]], i64 [[N:%.*]]
+; CHECK-NEXT: [[M:%.*]] = add i64 [[N]], 1
+; CHECK-NEXT: [[BOUND2:%.*]] = getelementptr i64, ptr [[BASE2:%.*]], i64 [[M]]
+; CHECK-NEXT: [[BOUND3:%.*]] = getelementptr i64, ptr [[BASE3:%.*]], i64 [[N]]
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[P1:%.*]] = phi ptr [ [[BASE1]], [[ENTRY:%.*]] ], [ [[P1_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
+; CHECK-NEXT: [[P2:%.*]] = phi ptr [ [[BASE2]], [[ENTRY]] ], [ [[P2_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[P3:%.*]] = phi ptr [ [[BASE3]], [[ENTRY]] ], [ [[P3_NEXT:%.*]], [[LOOP_LATCH]] ]
+; CHECK-NEXT: [[CMP_EXIT:%.*]] = icmp eq ptr [[P2]], [[BOUND2]]
+; CHECK-NEXT: br i1 [[CMP_EXIT]], label [[CHECK:%.*]], label [[BODY:%.*]]
+; CHECK: check:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne ptr [[P1]], [[BOUND1]]
+; CHECK-NEXT: [[CMP3:%.*]] = icmp ne ptr [[P3]], [[BOUND3]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP3]]
+; CHECK-NEXT: br i1 [[SEL]], label [[BODY]], label [[EXIT:%.*]]
+; CHECK: body:
+; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[P1]], align 8
+; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[P2]], align 8
+; CHECK-NEXT: [[SUM:%.*]] = add i64 [[V1]], [[V2]]
+; CHECK-NEXT: store i64 [[SUM]], ptr [[P3]], align 8
+; CHECK-NEXT: br label [[LOOP_LATCH]]
+; CHECK: loop.latch:
+; CHECK-NEXT: [[P1_NEXT]] = getelementptr inbounds i8, ptr [[P1]], i64 8
+; CHECK-NEXT: [[P2_NEXT]] = getelementptr inbounds i8, ptr [[P2]], i64 8
+; CHECK-NEXT: [[P3_NEXT]] = getelementptr inbounds i8, ptr [[P3]], i64 8
+; CHECK-NEXT: br label [[LOOP]]
+; CHECK: exit:
+; CHECK-NEXT: ret void
+;
+entry:
+ %bound1 = getelementptr i64, ptr %base1, i64 %n
+ %m = add i64 %n, 1
+ %bound2 = getelementptr i64, ptr %base2, i64 %m
+ %bound3 = getelementptr i64, ptr %base3, i64 %n
+ br label %loop
+
+loop:
+ %p1 = phi ptr [ %base1, %entry ], [ %p1.next, %loop.latch ]
+ %p2 = phi ptr [ %base2, %entry ], [ %p2.next, %loop.latch ]
+ %p3 = phi ptr [ %base3, %entry ], [ %p3.next, %loop.latch ]
+ %cmp.exit = icmp eq ptr %p2, %bound2
+ br i1 %cmp.exit, label %check, label %body
+
+check:
+ %cmp1 = icmp ne ptr %p1, %bound1
+ %cmp3 = icmp ne ptr %p3, %bound3
+ %sel = select i1 %cmp1, i1 true, i1 %cmp3
+ br i1 %sel, label %body, label %exit
+
+body:
+ %v1 = load i64, ptr %p1, align 8
+ %v2 = load i64, ptr %p2, align 8
+ %sum = add i64 %v1, %v2
+ store i64 %sum, ptr %p3, align 8
+ br label %loop.latch
+
+loop.latch:
+ %p1.next = getelementptr inbounds i8, ptr %p1, i64 8
+ %p2.next = getelementptr inbounds i8, ptr %p2, i64 8
+ %p3.next = getelementptr inbounds i8, ptr %p3, i64 8
+ br label %loop
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list