1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-02-01 05:01:59 +01:00
Chandler Carruth cb049b799a [PM/Unswitch] Fix a collection of closely related issues with trivial
switch unswitching.

The core problem was that the way we handled unswitching trivial exit
edges through the default successor of a switch. For some reason
I thought the right way to do this was to add a block containing
unreachable and point the default successor at this block. In
retrospect, this has an amazing number of problems.

The first issue is the one that this pass has always worked around -- we
have to *detect* such edges and avoid unswitching them again. This
seemed pretty easy really. You juts look for an edge to a block
containing unreachable. However, this pattern is woefully unsound. So
many things can break it. The amazing thing is that I found a test case
where *simple-loop-unswitch itself* breaks this! When we do
a *non-trivial* unswitch of a switch we will end up splitting this exit
edge. The result will be a default successor that is an exit and
terminates in ... a perfectly normal branch. So the first test case that
I started trying to fix is added to the nontrivial test cases. This is
a ridiculous example that did just amazing things previously. With just
unswitch, it would create 10+ copies of this stuff stamped out. But if
you combine it *just right* with a bunch of other passes (like
simplify-cfg, loop rotate, and some LICM) you can get it to do this
infinitely. Or at least, I never got it to finish. =[

This, in turn, uncovered another related issue. When we are manipulating
these switches after doing a trivial unswitch we never correctly updated
PHI nodes to reflect our edits. As soon as I started changing how these
edges were managed, it became obvious there were more issues that
I couldn't realistically leave unaddressed, so I wrote more test cases
around PHI updates here and ensured all of that works now.

And this, in turn, required some adjustment to how we collect and manage
the exit successor when it is the default successor. That showed a clear
bug where we failed to include it in our search for the outer-most loop
reached by an unswitched exit edge. This was actually already tested and
the test case didn't work. I (wrongly) thought that was due to SCEV
failing to analyze the switch. In fact, it was just a simple bug in the
code that skipped the default successor. While changing this, I handled
it correctly and have updated the test to reflect that we now get
precise SCEV analysis of trip counts for the outer loop in one of these
cases.

llvm-svn: 336646
2018-07-10 08:36:05 +00:00

187 lines
6.4 KiB
LLVM

; RUN: opt -passes='print<scalar-evolution>,loop(unswitch,loop-instsimplify),print<scalar-evolution>' -enable-nontrivial-unswitch -S < %s 2>%t.scev | FileCheck %s
; RUN: FileCheck %s --check-prefix=SCEV < %t.scev
target triple = "x86_64-unknown-linux-gnu"
declare void @f()
; Check that trivially unswitching an inner loop resets both the inner and outer
; loop trip count.
define void @test1(i32 %n, i32 %m, i1 %cond) {
; Check that SCEV has no trip count before unswitching.
; SCEV-LABEL: Determining loop execution counts for: @test1
; SCEV: Loop %inner_loop_begin: <multiple exits> Unpredictable backedge-taken count.
; SCEV: Loop %outer_loop_begin: Unpredictable backedge-taken count.
;
; Now check that after unswitching and simplifying instructions we get clean
; backedge-taken counts.
; SCEV-LABEL: Determining loop execution counts for: @test1
; SCEV: Loop %inner_loop_begin: backedge-taken count is (-1 + (1 smax %m))<nsw>
; SCEV: Loop %outer_loop_begin: backedge-taken count is (-1 + (1 smax %n))<nsw>
;
; And verify the code matches what we expect.
; CHECK-LABEL: define void @test1(
entry:
br label %outer_loop_begin
; Ensure the outer loop didn't get unswitched.
; CHECK: entry:
; CHECK-NEXT: br label %outer_loop_begin
outer_loop_begin:
%i = phi i32 [ %i.next, %outer_loop_latch ], [ 0, %entry ]
; Block unswitching of the outer loop with a noduplicate call.
call void @f() noduplicate
br label %inner_loop_begin
; Ensure the inner loop got unswitched into the outer loop.
; CHECK: outer_loop_begin:
; CHECK-NEXT: %{{.*}} = phi i32
; CHECK-NEXT: call void @f()
; CHECK-NEXT: br i1 %cond,
inner_loop_begin:
%j = phi i32 [ %j.next, %inner_loop_latch ], [ 0, %outer_loop_begin ]
br i1 %cond, label %inner_loop_latch, label %inner_loop_early_exit
inner_loop_latch:
%j.next = add nsw i32 %j, 1
%j.cmp = icmp slt i32 %j.next, %m
br i1 %j.cmp, label %inner_loop_begin, label %inner_loop_late_exit
inner_loop_early_exit:
%j.lcssa = phi i32 [ %i, %inner_loop_begin ]
br label %outer_loop_latch
inner_loop_late_exit:
br label %outer_loop_latch
outer_loop_latch:
%i.phi = phi i32 [ %j.lcssa, %inner_loop_early_exit ], [ %i, %inner_loop_late_exit ]
%i.next = add nsw i32 %i.phi, 1
%i.cmp = icmp slt i32 %i.next, %n
br i1 %i.cmp, label %outer_loop_begin, label %exit
exit:
ret void
}
; Check that trivially unswitching an inner loop resets both the inner and outer
; loop trip count.
define void @test2(i32 %n, i32 %m, i32 %cond) {
; Check that SCEV has no trip count before unswitching.
; SCEV-LABEL: Determining loop execution counts for: @test2
; SCEV: Loop %inner_loop_begin: <multiple exits> Unpredictable backedge-taken count.
; SCEV: Loop %outer_loop_begin: Unpredictable backedge-taken count.
;
; Now check that after unswitching and simplifying instructions we get clean
; backedge-taken counts.
; SCEV-LABEL: Determining loop execution counts for: @test2
; SCEV: Loop %inner_loop_begin: backedge-taken count is (-1 + (1 smax %m))<nsw>
; SCEV: Loop %outer_loop_begin: backedge-taken count is (-1 + (1 smax %n))<nsw>
;
; CHECK-LABEL: define void @test2(
entry:
br label %outer_loop_begin
; Ensure the outer loop didn't get unswitched.
; CHECK: entry:
; CHECK-NEXT: br label %outer_loop_begin
outer_loop_begin:
%i = phi i32 [ %i.next, %outer_loop_latch ], [ 0, %entry ]
; Block unswitching of the outer loop with a noduplicate call.
call void @f() noduplicate
br label %inner_loop_begin
; Ensure the inner loop got unswitched into the outer loop.
; CHECK: outer_loop_begin:
; CHECK-NEXT: %{{.*}} = phi i32
; CHECK-NEXT: call void @f()
; CHECK-NEXT: switch i32 %cond,
inner_loop_begin:
%j = phi i32 [ %j.next, %inner_loop_latch ], [ 0, %outer_loop_begin ]
switch i32 %cond, label %inner_loop_early_exit [
i32 1, label %inner_loop_latch
i32 2, label %inner_loop_latch
]
inner_loop_latch:
%j.next = add nsw i32 %j, 1
%j.cmp = icmp slt i32 %j.next, %m
br i1 %j.cmp, label %inner_loop_begin, label %inner_loop_late_exit
inner_loop_early_exit:
%j.lcssa = phi i32 [ %i, %inner_loop_begin ]
br label %outer_loop_latch
inner_loop_late_exit:
br label %outer_loop_latch
outer_loop_latch:
%i.phi = phi i32 [ %j.lcssa, %inner_loop_early_exit ], [ %i, %inner_loop_late_exit ]
%i.next = add nsw i32 %i.phi, 1
%i.cmp = icmp slt i32 %i.next, %n
br i1 %i.cmp, label %outer_loop_begin, label %exit
exit:
ret void
}
; Check that non-trivial unswitching of a branch in an inner loop into the outer
; loop invalidates both inner and outer.
define void @test3(i32 %n, i32 %m, i1 %cond) {
; Check that SCEV has no trip count before unswitching.
; SCEV-LABEL: Determining loop execution counts for: @test3
; SCEV: Loop %inner_loop_begin: <multiple exits> Unpredictable backedge-taken count.
; SCEV: Loop %outer_loop_begin: Unpredictable backedge-taken count.
;
; Now check that after unswitching and simplifying instructions we get clean
; backedge-taken counts.
; SCEV-LABEL: Determining loop execution counts for: @test3
; SCEV: Loop %inner_loop_begin{{.*}}: backedge-taken count is (-1 + (1 smax %m))<nsw>
; SCEV: Loop %outer_loop_begin: backedge-taken count is (-1 + (1 smax %n))<nsw>
;
; And verify the code matches what we expect.
; CHECK-LABEL: define void @test3(
entry:
br label %outer_loop_begin
; Ensure the outer loop didn't get unswitched.
; CHECK: entry:
; CHECK-NEXT: br label %outer_loop_begin
outer_loop_begin:
%i = phi i32 [ %i.next, %outer_loop_latch ], [ 0, %entry ]
; Block unswitching of the outer loop with a noduplicate call.
call void @f() noduplicate
br label %inner_loop_begin
; Ensure the inner loop got unswitched into the outer loop.
; CHECK: outer_loop_begin:
; CHECK-NEXT: %{{.*}} = phi i32
; CHECK-NEXT: call void @f()
; CHECK-NEXT: br i1 %cond,
inner_loop_begin:
%j = phi i32 [ %j.next, %inner_loop_latch ], [ 0, %outer_loop_begin ]
%j.tmp = add nsw i32 %j, 1
br i1 %cond, label %inner_loop_latch, label %inner_loop_early_exit
inner_loop_latch:
%j.next = add nsw i32 %j, 1
%j.cmp = icmp slt i32 %j.next, %m
br i1 %j.cmp, label %inner_loop_begin, label %inner_loop_late_exit
inner_loop_early_exit:
%j.lcssa = phi i32 [ %j.tmp, %inner_loop_begin ]
br label %outer_loop_latch
inner_loop_late_exit:
br label %outer_loop_latch
outer_loop_latch:
%inc.phi = phi i32 [ %j.lcssa, %inner_loop_early_exit ], [ 1, %inner_loop_late_exit ]
%i.next = add nsw i32 %i, %inc.phi
%i.cmp = icmp slt i32 %i.next, %n
br i1 %i.cmp, label %outer_loop_begin, label %exit
exit:
ret void
}