Skip to content

Commit aed78e6

Browse files
committed
[MLIR][CF] Avoid collapsing blocks which participate in cycles (#159743)
Previously, collapseBranch did not return failure for successor blocks which were part of a cycle. mlir-opt --canonicalize would run indefinitely for any N-block cycle which is kicked off with an unconditional jump. The simplifyPassThroughBr transform would continue alternating which block was targeted in ^bb0, resulting in an infinite loop. collapseBranch will not result in any useful transformation on blocks which participate in cycles, since the block is aliased by a different block. To avoid this, we can check for cycles in collapseBranch and abort when one is detected. Simplification of the cycle is left for other transforms.
1 parent 47017af commit aed78e6

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ static LogicalResult collapseBranch(Block *&successor,
122122
Block *successorDest = successorBranch.getDest();
123123
if (successorDest == successor)
124124
return failure();
125+
// Don't try to collapse branches which participate in a cycle.
126+
BranchOp nextBranch = dyn_cast<BranchOp>(successorDest->getTerminator());
127+
while (nextBranch) {
128+
Block *nextBranchDest = nextBranch.getDest();
129+
if (!nextBranchDest)
130+
break;
131+
else if (nextBranchDest == successor)
132+
return failure();
133+
nextBranch = dyn_cast<BranchOp>(nextBranchDest->getTerminator());
134+
}
125135

126136
// Update the operands to the successor. If the branch parent has no
127137
// arguments, we can use the branch operands directly.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: mlir-opt --canonicalize %s | FileCheck %s
2+
3+
// Test that control-flow cycles are not simplified infinitely.
4+
5+
// CHECK-LABEL: @cycle_2_blocks
6+
// CHECK-NEXT: cf.br ^bb1
7+
// CHECK-NEXT: ^bb1:
8+
// CHECK-NEXT: cf.br ^bb1
9+
func.func @cycle_2_blocks() {
10+
cf.br ^bb1
11+
^bb1:
12+
cf.br ^bb2
13+
^bb2:
14+
cf.br ^bb1
15+
}
16+
17+
// CHECK-LABEL: @no_cycle_2_blocks
18+
// CHECK-NEXT: %c1_i32 = arith.constant 1 : i32
19+
// CHECK-NEXT: return %c1_i32 : i32
20+
func.func @no_cycle_2_blocks() -> i32 {
21+
cf.br ^bb1
22+
^bb1:
23+
cf.br ^bb2
24+
^bb2:
25+
cf.br ^bb3
26+
^bb3:
27+
%ret = arith.constant 1 : i32
28+
return %ret : i32
29+
}
30+
31+
// CHECK-LABEL: @cycle_4_blocks
32+
// CHECK-NEXT: cf.br ^bb1
33+
// CHECK-NEXT: ^bb1:
34+
// CHECK-NEXT: cf.br ^bb1
35+
func.func @cycle_4_blocks() {
36+
cf.br ^bb1
37+
^bb1:
38+
cf.br ^bb2
39+
^bb2:
40+
cf.br ^bb3
41+
^bb3:
42+
cf.br ^bb4
43+
^bb4:
44+
cf.br ^bb1
45+
}
46+
47+
// CHECK-LABEL: @no_cycle_4_blocks
48+
// CHECK-NEXT: %c1_i32 = arith.constant 1 : i32
49+
// CHECK-NEXT: return %c1_i32 : i32
50+
func.func @no_cycle_4_blocks() -> i32 {
51+
cf.br ^bb1
52+
^bb1:
53+
cf.br ^bb2
54+
^bb2:
55+
cf.br ^bb3
56+
^bb3:
57+
cf.br ^bb4
58+
^bb4:
59+
cf.br ^bb5
60+
^bb5:
61+
%ret = arith.constant 1 : i32
62+
return %ret : i32
63+
}
64+
65+
// CHECK-LABEL: @delayed_3_cycle
66+
// CHECK-NEXT: cf.br ^bb1
67+
// CHECK-NEXT: ^bb1:
68+
// CHECK-NEXT: cf.br ^bb1
69+
func.func @delayed_3_cycle() {
70+
cf.br ^bb1
71+
^bb1:
72+
cf.br ^bb2
73+
^bb2:
74+
cf.br ^bb3
75+
^bb3:
76+
cf.br ^bb4
77+
^bb4:
78+
cf.br ^bb5
79+
^bb5:
80+
cf.br ^bb3
81+
}

0 commit comments

Comments
 (0)