[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:
parent
d1bd8d3f21
commit
9e0aa61f75
4 changed files with 414 additions and 76 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in a new issue