-
Notifications
You must be signed in to change notification settings - Fork 678
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove unstructured loop exits: Don't introduce loops
Previously, if the latch exit was reachable from a different exit block for the loop, then the pass would introduce a loop involving that exit block and the latch exit. This is unwanted and unaccounted for. - Add a test for shared exits. - Add a test for non-dedicated latch exit
- Loading branch information
Showing
3 changed files
with
218 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
test/HLSL/passes/dxil_remove_unstructured_loop_exits/nondedicated-latch-exit.ll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
; RUN: opt %s -analyze -loops | FileCheck -check-prefix=LOOPBEFORE %s | ||
; RUN: opt %s -dxil-remove-unstructured-loop-exits -o %t.bc | ||
; RUN: opt %t.bc -S | FileCheck %s | ||
; RUN: opt %t.bc -analyze -loops | FileCheck -check-prefix=LOOPAFTER %s | ||
|
||
; Ensure the pass works when the latch exit is reachable from the exit block, | ||
; and does not introduce an extra loop. | ||
; If an exit block could reach the latch exit block, then an old version | ||
; of the pass would introduce a loop because after modification, the | ||
; latch-exit block would branch to the exit block (which has since been | ||
; repositioned in the CFG). | ||
|
||
; | ||
; entry | ||
; | | ||
; v | ||
; +-> header ---> then --+ | ||
; | | | | | ||
; | | +---------+ | | ||
; | | | v | ||
; | v v exit0 | ||
; +--- latch | | ||
; | v | ||
; | exit1 | ||
; | | | ||
; | v | ||
; | exit2 | ||
; | | | ||
; | +--------------+ | ||
; | | | ||
; v v | ||
; end # 'end' is the latch-exit block | ||
|
||
; Before performing the loop exit restructuring, split the latch -> end edge, like this: | ||
; | ||
; entry | ||
; | | ||
; v | ||
; +-> header ---> then --+ | ||
; | | | | | ||
; | | +---------+ | | ||
; | | | v | ||
; | v v exit0 | ||
; +--- latch | | ||
; | v | ||
; | exit1 | ||
; v | | ||
; latch.end_crit_edge v | ||
; | exit2 | ||
; | | | ||
; | +--------------+ | ||
; | | | ||
; v v | ||
; end | ||
|
||
; Then it will be safe to rewire 'then' block as follows. | ||
; This achieves the goal of making all exiting blocks dominate | ||
; the latch. And crucially, a new loop is not created. | ||
; | ||
; entry | ||
; | | ||
; v | ||
; +-> header ---> then | ||
; | | | | ||
; | | +---------+ | ||
; | | | | ||
; | v v | ||
; | dx.struct.new_exiting | ||
; | | | | ||
; | v | | ||
; +--- latch | | ||
; | +-----+ | ||
; v v | ||
; latch.end_crit_edge --> exit0 | ||
; | | | ||
; | v | ||
; | exit1 | ||
; | | | ||
; | v | ||
; | exit2 | ||
; | | | ||
; | +----------------+ | ||
; | | | ||
; v v | ||
; end | ||
|
||
; LOOPBEFORE: Loop at depth 1 containing: %header<header>,%then<exiting>,%latch<latch><exiting> | ||
; LOOPBEFORE-NOT: Loop at depth | ||
|
||
; Don't create an extra loop | ||
; LOOPAFTER-NOT: Loop at depth {{.*}} containing: {{.*}}%end | ||
; LOOPAFTER-NOT: Loop at depth {{.*}} containing: {{.*}}%exit0 | ||
; LOOPAFTER-NOT: Loop at depth {{.*}} containing: {{.*}}%exit1 | ||
; LOOPAFTER-NOT: Loop at depth {{.*}} containing: {{.*}}%exit2 | ||
; LOOPAFTER: Loop at depth 1 containing: %header<header>,%then,%dx.struct_exit.new_exiting<exiting>,%latch<latch><exiting> | ||
; LOOPAFTER-NOT: Loop at depth | ||
|
||
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" | ||
target triple = "dxil-ms-dx" | ||
|
||
define void @main(i1 %cond) { | ||
entry: | ||
br label %header | ||
|
||
header: | ||
br i1 %cond, label %then, label %latch | ||
|
||
then: | ||
br i1 %cond, label %exit0, label %latch | ||
|
||
latch: | ||
br i1 %cond, label %end, label %header | ||
|
||
; a long chain that eventually gets to %end | ||
exit0: | ||
br label %exit1 | ||
exit1: | ||
br label %exit2 | ||
exit2: | ||
br label %end | ||
|
||
end: | ||
ret void | ||
} | ||
|
||
; CHECK: define void @main |
72 changes: 72 additions & 0 deletions
72
test/HLSL/passes/dxil_remove_unstructured_loop_exits/shared-exit.ll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
; RUN: opt %s -analyze -loops | FileCheck -check-prefix=LOOPBEFORE %s | ||
; RUN: opt %s -dxil-remove-unstructured-loop-exits -o %t.bc | ||
; RUN: opt %t.bc -S | FileCheck %s | ||
; RUN: opt %t.bc -analyze -loops | FileCheck -check-prefix=LOOPAFTER %s | ||
|
||
; Two exiting blocks target the same exit block. This should work. | ||
; Also, the pass should not introduce a new loop, particularly not among | ||
; the latch-exiting blocks. | ||
|
||
; LOOPBEFORE: Loop at depth 1 containing: %header<header>,%then.a<exiting>,%midloop,%then.b<exiting>,%latch<latch><exiting> | ||
; LOOPBEFORE-NOT: Loop at depth | ||
|
||
; Don't create a loop containing: %end<header>,%0<exiting>,%shared_exit<latch> | ||
; LOOPAFTER-NOT: Loop at depth {{.*}} containing: {{.*}}%end | ||
; LOOPAFTER-NOT: Loop at depth {{.*}} containing: {{.*}}%0 | ||
; LOOPAFTER-NOT: Loop at depth {{.*}} containing: {{.*}}%shared_exit | ||
; LOOPAFTER: Loop at depth 1 containing: %header<header>,%then.a,%dx.struct_exit.new_exiting<exiting>,%midloop,%then.b,%dx.struct_exit.new_exiting2<exiting>,%latch<latch><exiting> | ||
; LOOPAFTER-NOT: Loop at depth | ||
|
||
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" | ||
target triple = "dxil-ms-dx" | ||
|
||
; The input has two if-then-else structures in a row: | ||
; header/then.a/midloop | ||
; midloop/then.b/latch | ||
; | ||
; entry | ||
; | | ||
; v | ||
; +-> header ---> then.a ------+ | ||
; | | | | | ||
; | | +-------+ | | ||
; | v v v | ||
; | midloop --> then.b --> shared_exit | ||
; | | | | | ||
; | | +-------+ | | ||
; | v v | | ||
; +--- latch | | ||
; | | | ||
; | +--------------------+ | ||
; | | | ||
; v v | ||
; end | ||
|
||
|
||
define void @main(i1 %cond) { | ||
entry: | ||
br label %header | ||
|
||
header: | ||
br i1 %cond, label %then.a, label %midloop | ||
|
||
then.a: | ||
br i1 %cond, label %shared_exit, label %midloop | ||
|
||
midloop: | ||
br i1 %cond, label %then.b, label %latch | ||
|
||
then.b: | ||
br i1 %cond, label %shared_exit, label %latch | ||
|
||
latch: | ||
br i1 %cond, label %end, label %header | ||
|
||
shared_exit: | ||
br label %end | ||
|
||
end: | ||
ret void | ||
} | ||
|
||
; CHECK: define void @main |