[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