Skip to content

Commit

Permalink
[IRCE] Smarter detection of empty ranges using SCEV
Browse files Browse the repository at this point in the history
For a SCEV range, this patch replaces the naive emptiness check for SCEV ranges
which looks like `Begin == End` with a SCEV check. The range is guaranteed to be
empty of `Begin >= End`. We should filter such ranges out and do not try to perform
IRCE for them.

For example, we can get such range when intersecting range `[A, B)` and `[C, D)`
where `A < B < C < D`. The resulting range is `[max(A, C), min(B, D)) = [C, B)`.
This range is empty, but its `Begin` does not match with `End`.

Making IRCE for an empty range is basically safe but unprofitable because we
never actually get into the main loop where the range checks are supposed to
be eliminated. This patch uses SCEV mechanisms to treat loops with proved
`Begin >= End` as empty.

Differential Revision: https://reviews.llvm.org/D39082

llvm-svn: 316550
  • Loading branch information
Max Kazantsev committed Oct 25, 2017
1 parent f012360 commit 4332a94
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 6 deletions.
21 changes: 15 additions & 6 deletions llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp
Expand Up @@ -208,7 +208,14 @@ class InductiveRangeCheck {
Type *getType() const { return Begin->getType(); }
const SCEV *getBegin() const { return Begin; }
const SCEV *getEnd() const { return End; }
bool isEmpty() const { return Begin == End; }
bool isEmpty(ScalarEvolution &SE, bool IsSigned) const {
if (Begin == End)
return true;
if (IsSigned)
return SE.isKnownPredicate(ICmpInst::ICMP_SGE, Begin, End);
else
return SE.isKnownPredicate(ICmpInst::ICMP_UGE, Begin, End);
}
};

/// This is the value the condition of the branch needs to evaluate to for the
Expand Down Expand Up @@ -1666,14 +1673,15 @@ static Optional<InductiveRangeCheck::Range>
IntersectRange(ScalarEvolution &SE,
const Optional<InductiveRangeCheck::Range> &R1,
const InductiveRangeCheck::Range &R2) {
if (R2.isEmpty())
if (R2.isEmpty(SE, /* IsSigned */ true))
return None;
if (!R1.hasValue())
return R2;
auto &R1Value = R1.getValue();
// We never return empty ranges from this function, and R1 is supposed to be
// a result of intersection. Thus, R1 is never empty.
assert(!R1Value.isEmpty() && "We should never have empty R1!");
assert(!R1Value.isEmpty(SE, /* IsSigned */ true) &&
"We should never have empty R1!");

// TODO: we could widen the smaller range and have this work; but for now we
// bail out to keep things simple.
Expand All @@ -1685,7 +1693,7 @@ IntersectRange(ScalarEvolution &SE,

// If the resulting range is empty, just return None.
auto Ret = InductiveRangeCheck::Range(NewBegin, NewEnd);
if (Ret.isEmpty())
if (Ret.isEmpty(SE, /* IsSigned */ true))
return None;
return Ret;
}
Expand Down Expand Up @@ -1756,8 +1764,9 @@ bool InductiveRangeCheckElimination::runOnLoop(Loop *L, LPPassManager &LPM) {
auto MaybeSafeIterRange =
IntersectRange(SE, SafeIterRange, Result.getValue());
if (MaybeSafeIterRange.hasValue()) {
assert(!MaybeSafeIterRange.getValue().isEmpty() &&
"We should never return empty ranges!");
assert(
!MaybeSafeIterRange.getValue().isEmpty(SE, LS.IsSignedPredicate) &&
"We should never return empty ranges!");
RangeChecksToEliminate.push_back(IRC);
SafeIterRange = MaybeSafeIterRange.getValue();
}
Expand Down
4 changes: 4 additions & 0 deletions llvm/test/Transforms/IRCE/clamp.ll
@@ -1,3 +1,7 @@
; This test demonstrates the confusion in ranges: we have unsigned ranges here,
; but signed comparisons in IntersectRanges produce bad results. We temporarily
; disable it and re-enable once the unsigned ranges are supported again.
; XFAIL: *
; RUN: opt -verify-loop-info -irce-print-changed-loops -irce -irce-allow-unsigned-latch=true -S < %s 2>&1 | FileCheck %s

; The test demonstrates that incorrect behavior of Clamp may lead to incorrect
Expand Down
69 changes: 69 additions & 0 deletions llvm/test/Transforms/IRCE/empty_ranges.ll
@@ -0,0 +1,69 @@
; RUN: opt -verify-loop-info -irce-print-changed-loops -irce -irce-allow-unsigned-latch=true -S


; Make sure that IRCE doesn't apply in case of empty ranges.
; (i + 30 < 40) if i in [-30, 10).
; Intersected with iteration space, it is [0, 10).
; (i - 60 < 40) if i in [60 , 100).
; The intersection with safe iteration space is the empty range [60, 10).
; It is better to eliminate one range check than attempt to eliminate both given
; that we will never go to the main loop in the latter case and basically
; only duplicate code with no benefits.

define void @test_01(i32* %arr, i32* %a_len_ptr) #0 {

; CHECK-LABEL: test_01(
; CHECK-NOT: preloop
; CHECK: entry:
; CHECK-NEXT: br i1 true, label %loop.preheader, label %main.pseudo.exit
; CHECK: in.bounds.1:
; CHECK-NEXT: %addr = getelementptr i32, i32* %arr, i32 %idx
; CHECK-NEXT: store i32 0, i32* %addr
; CHECK-NEXT: %off1 = add i32 %idx, 30
; CHECK-NEXT: %c2 = icmp slt i32 %off1, 40
; CHECK-NEXT: br i1 true, label %in.bounds.2, label %exit.loopexit2
; CHECK: in.bounds.2:
; CHECK-NEXT: %off2 = add i32 %idx, -60
; CHECK-NEXT: %c3 = icmp slt i32 %off2, 40
; CHECK-NEXT: br i1 %c3, label %in.bounds.3, label %exit.loopexit2
; CHECK: in.bounds.3:
; CHECK-NEXT: %next = icmp ult i32 %idx.next, 100
; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ult i32 %idx.next, 10
; CHECK-NEXT: br i1 [[COND1]], label %loop, label %main.exit.selector
; CHECK: main.exit.selector:
; CHECK-NEXT: %idx.next.lcssa = phi i32 [ %idx.next, %in.bounds.3 ]
; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ult i32 %idx.next.lcssa, 100
; CHECK-NEXT: br i1 [[COND2]], label %main.pseudo.exit, label %exit
; CHECK: postloop:

entry:
br label %loop

loop:
%idx = phi i32 [ 0, %entry ], [ %idx.next, %in.bounds.3 ]
%idx.next = add nsw nuw i32 %idx, 1
%c1 = icmp slt i32 %idx, 20
br i1 %c1, label %in.bounds.1, label %out.of.bounds

in.bounds.1:
%addr = getelementptr i32, i32* %arr, i32 %idx
store i32 0, i32* %addr
%off1 = add i32 %idx, 30
%c2 = icmp slt i32 %off1, 40
br i1 %c2, label %in.bounds.2, label %exit

in.bounds.2:
%off2 = add i32 %idx, -60
%c3 = icmp slt i32 %off2, 40
br i1 %c3, label %in.bounds.3, label %exit

in.bounds.3:
%next = icmp ult i32 %idx.next, 100
br i1 %next, label %loop, label %exit

out.of.bounds:
ret void

exit:
ret void
}

0 comments on commit 4332a94

Please sign in to comment.