[SCEV] Prove implied conditions via matching SCEV differences (#201839)

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.
This commit is contained in:
Timur Golubovich 2026-07-01 09:26:36 +02:00 committed by GitHub
parent d1bd8d3f21
commit 9e0aa61f75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 414 additions and 76 deletions

View file

@ -2325,6 +2325,16 @@ private:
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.

View file

@ -12842,6 +12842,51 @@ 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;
// 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,
// 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,
@ -12854,6 +12899,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);
}

View file

@ -0,0 +1,357 @@
; 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 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"
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: [[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 %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
}
; 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
}

View file

@ -1,76 +0,0 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=loop-reduce -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.
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(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[N:%.*]], 3
; 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: 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: 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: [[LSR_IV_NEXT]] = add i64 [[LSR_IV]], 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 %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
}