We'd added support a while back from breaking the backedge if SCEV can prove the trip count is zero. However, we used the exact trip count which requires *all* exits be analyzeable. I noticed while writing test cases for another patch that this disallows cases where one exit is provably taken paired with another which is unknown. This patch adds the upper bound case. We could use a symbolic max trip count here instead, but we use an isKnownNonZero filter (presumably for compile time?) for the first-iteration reasoning. I decided this was a more obvious incremental step, and we could go back and untangle the schemes separately. Differential Revision: https://reviews.llvm.org/D108833
398 lines
10 KiB
LLVM
398 lines
10 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -loop-deletion -S | FileCheck %s
|
|
|
|
@G = external global i32
|
|
|
|
define void @test_trivial() {
|
|
; CHECK-LABEL: @test_trivial(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
store i32 0, i32* @G
|
|
br i1 false, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
|
|
define void @test_bottom_tested() {
|
|
; CHECK-LABEL: @test_bottom_tested(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: [[IV_INC:%.*]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[BE_TAKEN:%.*]] = icmp ne i32 [[IV_INC]], 1
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry], [ %iv.inc, %loop ]
|
|
store i32 0, i32* @G
|
|
%iv.inc = add i32 %iv, 1
|
|
%be_taken = icmp ne i32 %iv.inc, 1
|
|
br i1 %be_taken, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_early_exit() {
|
|
; CHECK-LABEL: @test_early_exit(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: [[IV_INC:%.*]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[BE_TAKEN:%.*]] = icmp ne i32 [[IV_INC]], 1
|
|
; CHECK-NEXT: br i1 [[BE_TAKEN]], label [[LATCH:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: unreachable
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry], [ %iv.inc, %latch ]
|
|
store i32 0, i32* @G
|
|
%iv.inc = add i32 %iv, 1
|
|
%be_taken = icmp ne i32 %iv.inc, 1
|
|
br i1 %be_taken, label %latch, label %exit
|
|
latch:
|
|
br label %loop
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_multi_exit1() {
|
|
; CHECK-LABEL: @test_multi_exit1(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: [[IV_INC:%.*]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[BE_TAKEN:%.*]] = icmp ne i32 [[IV_INC]], 1
|
|
; CHECK-NEXT: br i1 [[BE_TAKEN]], label [[LATCH:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: store i32 1, i32* @G, align 4
|
|
; CHECK-NEXT: [[COND2:%.*]] = icmp ult i32 [[IV_INC]], 30
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry], [ %iv.inc, %latch ]
|
|
store i32 0, i32* @G
|
|
%iv.inc = add i32 %iv, 1
|
|
%be_taken = icmp ne i32 %iv.inc, 1
|
|
br i1 %be_taken, label %latch, label %exit
|
|
latch:
|
|
store i32 1, i32* @G
|
|
%cond2 = icmp ult i32 %iv.inc, 30
|
|
br i1 %cond2, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_multi_exit2() {
|
|
; CHECK-LABEL: @test_multi_exit2(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: br i1 true, label [[LATCH:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: store i32 1, i32* @G, align 4
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
store i32 0, i32* @G
|
|
br i1 true, label %latch, label %exit
|
|
latch:
|
|
store i32 1, i32* @G
|
|
br i1 false, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_multi_exit3(i1 %cond1) {
|
|
; CHECK-LABEL: @test_multi_exit3(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: br i1 [[COND1:%.*]], label [[LATCH:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: store i32 1, i32* @G, align 4
|
|
; CHECK-NEXT: [[IV_INC:%.*]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[BE_TAKEN:%.*]] = icmp ne i32 [[IV_INC]], 1
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [ 0, %entry], [ %iv.inc, %latch ]
|
|
store i32 0, i32* @G
|
|
br i1 %cond1, label %latch, label %exit
|
|
latch:
|
|
store i32 1, i32* @G
|
|
%iv.inc = add i32 %iv, 1
|
|
%be_taken = icmp ne i32 %iv.inc, 1
|
|
br i1 %be_taken, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Subtle - This is either zero btc, or infinite, thus, can't break
|
|
; backedge
|
|
define void @test_multi_exit4(i1 %cond1, i1 %cond2) {
|
|
; CHECK-LABEL: @test_multi_exit4(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: br i1 [[COND1:%.*]], label [[LATCH:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: store i32 1, i32* @G, align 4
|
|
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[LOOP]], label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
store i32 0, i32* @G
|
|
br i1 %cond1, label %latch, label %exit
|
|
latch:
|
|
store i32 1, i32* @G
|
|
br i1 %cond2, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; A simple case with multiple exit blocks
|
|
define void @test_multi_exit5() {
|
|
; CHECK-LABEL: @test_multi_exit5(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: br i1 true, label [[LATCH:%.*]], label [[EXIT1:%.*]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: store i32 1, i32* @G, align 4
|
|
; CHECK-NEXT: br label [[EXIT2:%.*]]
|
|
; CHECK: exit1:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: exit2:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
store i32 0, i32* @G
|
|
br i1 true, label %latch, label %exit1
|
|
latch:
|
|
store i32 1, i32* @G
|
|
br i1 false, label %loop, label %exit2
|
|
|
|
exit1:
|
|
ret void
|
|
exit2:
|
|
ret void
|
|
}
|
|
|
|
declare i1 @unknown()
|
|
|
|
; We can't compute an exit count for the latch, but we know the upper
|
|
; bound on the trip count is zero anyways.
|
|
define void @test_dead_latch1() {
|
|
; CHECK-LABEL: @test_dead_latch1(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: br i1 false, label [[LATCH:%.*]], label [[EXIT1:%.*]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: [[LATCHCOND:%.*]] = call i1 @unknown()
|
|
; CHECK-NEXT: br label [[EXIT2:%.*]]
|
|
; CHECK: exit1:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: exit2:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
store i32 0, i32* @G
|
|
br i1 false, label %latch, label %exit1
|
|
latch:
|
|
%latchcond = call i1 @unknown()
|
|
br i1 %latchcond, label %loop, label %exit2
|
|
|
|
exit1:
|
|
ret void
|
|
exit2:
|
|
ret void
|
|
}
|
|
|
|
|
|
define void @test_live_inner() {
|
|
; CHECK-LABEL: @test_live_inner(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: br label [[INNER:%.*]]
|
|
; CHECK: inner:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[LOOP]] ], [ [[IV_INC:%.*]], [[INNER]] ]
|
|
; CHECK-NEXT: store i32 [[IV]], i32* @G, align 4
|
|
; CHECK-NEXT: [[IV_INC]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[CND:%.*]] = icmp ult i32 [[IV_INC]], 200
|
|
; CHECK-NEXT: br i1 [[CND]], label [[INNER]], label [[LATCH:%.*]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
store i32 0, i32* @G
|
|
br label %inner
|
|
|
|
inner:
|
|
%iv = phi i32 [0, %loop], [%iv.inc, %inner]
|
|
store i32 %iv, i32* @G
|
|
%iv.inc = add i32 %iv, 1
|
|
%cnd = icmp ult i32 %iv.inc, 200
|
|
br i1 %cnd, label %inner, label %latch
|
|
|
|
latch:
|
|
br i1 false, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
define void @test_live_outer() {
|
|
; CHECK-LABEL: @test_live_outer(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_INC:%.*]], [[LATCH:%.*]] ]
|
|
; CHECK-NEXT: br label [[INNER:%.*]]
|
|
; CHECK: inner:
|
|
; CHECK-NEXT: store i32 0, i32* @G, align 4
|
|
; CHECK-NEXT: br label [[LATCH]]
|
|
; CHECK: latch:
|
|
; CHECK-NEXT: store i32 [[IV]], i32* @G, align 4
|
|
; CHECK-NEXT: [[IV_INC]] = add i32 [[IV]], 1
|
|
; CHECK-NEXT: [[CND:%.*]] = icmp ult i32 [[IV_INC]], 200
|
|
; CHECK-NEXT: br i1 [[CND]], label [[LOOP]], label [[EXIT:%.*]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%iv = phi i32 [0, %entry], [%iv.inc, %latch]
|
|
br label %inner
|
|
|
|
inner:
|
|
store i32 0, i32* @G
|
|
br i1 false, label %inner, label %latch
|
|
|
|
latch:
|
|
store i32 %iv, i32* @G
|
|
%iv.inc = add i32 %iv, 1
|
|
%cnd = icmp ult i32 %iv.inc, 200
|
|
br i1 %cnd, label %loop, label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Key point is that inner_latch drops out of the outer loop when
|
|
; the inner loop is deleted, and thus the lcssa phi needs to be
|
|
; in the inner_latch block to preserve LCSSA. We either have to
|
|
; insert the LCSSA phi, or not break the inner backedge.
|
|
define void @loop_nest_lcssa() {
|
|
; CHECK-LABEL: @loop_nest_lcssa(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = add i32 1, 2
|
|
; CHECK-NEXT: br label [[OUTER_HEADER:%.*]]
|
|
; CHECK: outer_header:
|
|
; CHECK-NEXT: br label [[INNER_HEADER:%.*]]
|
|
; CHECK: inner_header:
|
|
; CHECK-NEXT: br i1 false, label [[INNER_LATCH:%.*]], label [[OUTER_LATCH:%.*]]
|
|
; CHECK: inner_latch:
|
|
; CHECK-NEXT: [[DOTLCSSA:%.*]] = phi i32 [ [[TMP0]], [[INNER_HEADER]] ]
|
|
; CHECK-NEXT: br label [[LOOPEXIT:%.*]]
|
|
; CHECK: outer_latch:
|
|
; CHECK-NEXT: br label [[OUTER_HEADER]]
|
|
; CHECK: loopexit:
|
|
; CHECK-NEXT: [[DOTLCSSA32:%.*]] = phi i32 [ [[DOTLCSSA]], [[INNER_LATCH]] ]
|
|
; CHECK-NEXT: unreachable
|
|
;
|
|
entry:
|
|
br label %outer_header
|
|
|
|
outer_header:
|
|
%0 = add i32 1, 2
|
|
br label %inner_header
|
|
|
|
inner_header:
|
|
br i1 false, label %inner_latch, label %outer_latch
|
|
|
|
inner_latch:
|
|
br i1 false, label %inner_header, label %loopexit
|
|
|
|
outer_latch:
|
|
br label %outer_header
|
|
|
|
loopexit:
|
|
%.lcssa32 = phi i32 [ %0, %inner_latch ]
|
|
unreachable
|
|
}
|