1 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2 ; RUN: opt < %s -passes=unify-loop-exits -max-booleans-in-control-flow-hub=1 -S | FileCheck %s
3 ; RUN: opt < %s -passes=unify-loop-exits -S | FileCheck --check-prefix=BOOLEAN %s
5 ; A loop with multiple exit blocks.
7 define void @loop_two_exits(i1 %PredEntry, i1 %PredA) {
8 ; CHECK-LABEL: @loop_two_exits(
10 ; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[E:%.*]]
12 ; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ]
13 ; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C]]
15 ; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]]
16 ; CHECK-NEXT: br label [[D:%.*]]
18 ; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1
19 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
20 ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]]
22 ; CHECK-NEXT: unreachable
24 ; CHECK-NEXT: ret void
25 ; CHECK: loop.exit.guard:
26 ; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A]] ], [ 1, [[C]] ]
27 ; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
28 ; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[E]]
30 ; BOOLEAN-LABEL: @loop_two_exits(
31 ; BOOLEAN-NEXT: entry:
32 ; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[E:%.*]]
34 ; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ]
35 ; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C]]
37 ; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]]
38 ; BOOLEAN-NEXT: br label [[D:%.*]]
40 ; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1
41 ; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
42 ; BOOLEAN-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]]
44 ; BOOLEAN-NEXT: unreachable
46 ; BOOLEAN-NEXT: ret void
47 ; BOOLEAN: loop.exit.guard:
48 ; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ]
49 ; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[E]]
52 br i1 %PredEntry, label %A, label %E
55 %inc1 = phi i32 [ 0, %entry ], [ %inc2, %C ]
56 br i1 %PredA, label %B, label %C
59 tail call fastcc void @check(i32 1) #0
63 %inc2 = add i32 %inc1, 1
64 %cmp = icmp ult i32 %inc2, 10
65 br i1 %cmp, label %A, label %E
74 ; The loop exit blocks appear in an inner loop.
76 define void @inner_loop(i1 %PredEntry, i1 %PredA, i1 %PredB) {
77 ; CHECK-LABEL: @inner_loop(
79 ; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[I:%.*]]
81 ; CHECK-NEXT: [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ]
82 ; CHECK-NEXT: br label [[B:%.*]]
84 ; CHECK-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ]
85 ; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]]
87 ; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
88 ; CHECK-NEXT: br label [[H:%.*]]
90 ; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD2]], label [[F]]
92 ; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
93 ; CHECK-NEXT: br label [[H]]
95 ; CHECK-NEXT: [[INNER2]] = add i32 [[INNER1]], 1
96 ; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20
97 ; CHECK-NEXT: br i1 [[CMP1]], label [[B]], label [[LOOP_EXIT_GUARD2]]
99 ; CHECK-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1
100 ; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10
101 ; CHECK-NEXT: br i1 [[CMP2]], label [[A]], label [[LOOP_EXIT_GUARD:%.*]]
103 ; CHECK-NEXT: unreachable
105 ; CHECK-NEXT: ret void
106 ; CHECK: loop.exit.guard:
107 ; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 2, [[G]] ], [ [[MERGED_BB_IDX_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ]
108 ; CHECK-NEXT: [[C_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
109 ; CHECK-NEXT: br i1 [[C_PREDICATE]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
110 ; CHECK: loop.exit.guard1:
111 ; CHECK-NEXT: [[E_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 1
112 ; CHECK-NEXT: br i1 [[E_PREDICATE]], label [[E:%.*]], label [[I]]
113 ; CHECK: loop.exit.guard2:
114 ; CHECK-NEXT: [[MERGED_BB_IDX_MOVED]] = phi i32 [ 0, [[B]] ], [ 1, [[D]] ], [ undef, [[F]] ]
115 ; CHECK-NEXT: [[MERGED_BB_IDX3:%.*]] = phi i32 [ 0, [[B]] ], [ 0, [[D]] ], [ 1, [[F]] ]
116 ; CHECK-NEXT: [[LOOP_EXIT_GUARD_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX3]], 0
117 ; CHECK-NEXT: br i1 [[LOOP_EXIT_GUARD_PREDICATE]], label [[LOOP_EXIT_GUARD]], label [[G]]
119 ; BOOLEAN-LABEL: @inner_loop(
120 ; BOOLEAN-NEXT: entry:
121 ; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[I:%.*]]
123 ; BOOLEAN-NEXT: [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ]
124 ; BOOLEAN-NEXT: br label [[B:%.*]]
126 ; BOOLEAN-NEXT: [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ]
127 ; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]]
129 ; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
130 ; BOOLEAN-NEXT: br label [[H:%.*]]
132 ; BOOLEAN-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD2]], label [[F]]
134 ; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
135 ; BOOLEAN-NEXT: br label [[H]]
137 ; BOOLEAN-NEXT: [[INNER2]] = add i32 [[INNER1]], 1
138 ; BOOLEAN-NEXT: [[CMP1:%.*]] = icmp ult i32 [[INNER2]], 20
139 ; BOOLEAN-NEXT: br i1 [[CMP1]], label [[B]], label [[LOOP_EXIT_GUARD2]]
141 ; BOOLEAN-NEXT: [[OUTER2]] = add i32 [[OUTER1]], 1
142 ; BOOLEAN-NEXT: [[CMP2:%.*]] = icmp ult i32 [[OUTER2]], 10
143 ; BOOLEAN-NEXT: br i1 [[CMP2]], label [[A]], label [[LOOP_EXIT_GUARD:%.*]]
145 ; BOOLEAN-NEXT: unreachable
147 ; BOOLEAN-NEXT: ret void
148 ; BOOLEAN: loop.exit.guard:
149 ; BOOLEAN-NEXT: [[GUARD_C:%.*]] = phi i1 [ false, [[G]] ], [ [[GUARD_C_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ]
150 ; BOOLEAN-NEXT: [[GUARD_E:%.*]] = phi i1 [ false, [[G]] ], [ [[GUARD_E_MOVED:%.*]], [[LOOP_EXIT_GUARD2]] ]
151 ; BOOLEAN-NEXT: br i1 [[GUARD_C]], label [[C:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
152 ; BOOLEAN: loop.exit.guard1:
153 ; BOOLEAN-NEXT: br i1 [[GUARD_E]], label [[E:%.*]], label [[I]]
154 ; BOOLEAN: loop.exit.guard2:
155 ; BOOLEAN-NEXT: [[GUARD_E_MOVED]] = phi i1 [ false, [[B]] ], [ true, [[D]] ], [ undef, [[F]] ]
156 ; BOOLEAN-NEXT: [[GUARD_C_MOVED]] = phi i1 [ true, [[B]] ], [ false, [[D]] ], [ undef, [[F]] ]
157 ; BOOLEAN-NEXT: [[GUARD_LOOP_EXIT_GUARD:%.*]] = phi i1 [ true, [[B]] ], [ true, [[D]] ], [ false, [[F]] ]
158 ; BOOLEAN-NEXT: br i1 [[GUARD_LOOP_EXIT_GUARD]], label [[LOOP_EXIT_GUARD]], label [[G]]
161 br i1 %PredEntry, label %A, label %I
164 %outer1 = phi i32 [ 0, %entry ], [ %outer2, %G ]
168 %inner1 = phi i32 [ 0, %A ], [ %inner2, %F ]
169 br i1 %PredA, label %D, label %C
172 tail call fastcc void @check(i32 1) #0
176 br i1 %PredB, label %E, label %F
179 tail call fastcc void @check(i32 2) #0
183 %inner2 = add i32 %inner1, 1
184 %cmp1 = icmp ult i32 %inner2, 20
185 br i1 %cmp1, label %B, label %G
188 %outer2 = add i32 %outer1, 1
189 %cmp2 = icmp ult i32 %outer2, 10
190 br i1 %cmp2, label %A, label %I
199 ; A loop with more exit blocks.
201 define void @loop_five_exits(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC, i1 %PredD) {
202 ; CHECK-LABEL: @loop_five_exits(
204 ; CHECK-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[L:%.*]]
206 ; CHECK-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ]
207 ; CHECK-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C:%.*]]
209 ; CHECK-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
210 ; CHECK-NEXT: br label [[J:%.*]]
212 ; CHECK-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD]], label [[E:%.*]]
214 ; CHECK-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
215 ; CHECK-NEXT: br label [[J]]
217 ; CHECK-NEXT: br i1 [[PREDC:%.*]], label [[LOOP_EXIT_GUARD]], label [[G:%.*]]
219 ; CHECK-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]]
220 ; CHECK-NEXT: br label [[K:%.*]]
222 ; CHECK-NEXT: br i1 [[PREDD:%.*]], label [[LOOP_EXIT_GUARD]], label [[I]]
224 ; CHECK-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]]
225 ; CHECK-NEXT: br label [[K]]
227 ; CHECK-NEXT: [[INC2]] = add i32 [[INC1]], 1
228 ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
229 ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]]
231 ; CHECK-NEXT: br label [[L]]
233 ; CHECK-NEXT: br label [[L]]
235 ; CHECK-NEXT: ret void
236 ; CHECK: loop.exit.guard:
237 ; CHECK-NEXT: [[MERGED_BB_IDX:%.*]] = phi i32 [ 0, [[A]] ], [ 1, [[C]] ], [ 2, [[E]] ], [ 3, [[G]] ], [ 4, [[I]] ]
238 ; CHECK-NEXT: [[B_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 0
239 ; CHECK-NEXT: br i1 [[B_PREDICATE]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
240 ; CHECK: loop.exit.guard1:
241 ; CHECK-NEXT: [[D_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 1
242 ; CHECK-NEXT: br i1 [[D_PREDICATE]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]]
243 ; CHECK: loop.exit.guard2:
244 ; CHECK-NEXT: [[F_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 2
245 ; CHECK-NEXT: br i1 [[F_PREDICATE]], label [[F:%.*]], label [[LOOP_EXIT_GUARD3:%.*]]
246 ; CHECK: loop.exit.guard3:
247 ; CHECK-NEXT: [[H_PREDICATE:%.*]] = icmp eq i32 [[MERGED_BB_IDX]], 3
248 ; CHECK-NEXT: br i1 [[H_PREDICATE]], label [[H:%.*]], label [[L]]
250 ; BOOLEAN-LABEL: @loop_five_exits(
251 ; BOOLEAN-NEXT: entry:
252 ; BOOLEAN-NEXT: br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[L:%.*]]
254 ; BOOLEAN-NEXT: [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ]
255 ; BOOLEAN-NEXT: br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C:%.*]]
257 ; BOOLEAN-NEXT: tail call fastcc void @check(i32 1) #[[ATTR0]]
258 ; BOOLEAN-NEXT: br label [[J:%.*]]
260 ; BOOLEAN-NEXT: br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD]], label [[E:%.*]]
262 ; BOOLEAN-NEXT: tail call fastcc void @check(i32 2) #[[ATTR0]]
263 ; BOOLEAN-NEXT: br label [[J]]
265 ; BOOLEAN-NEXT: br i1 [[PREDC:%.*]], label [[LOOP_EXIT_GUARD]], label [[G:%.*]]
267 ; BOOLEAN-NEXT: tail call fastcc void @check(i32 3) #[[ATTR0]]
268 ; BOOLEAN-NEXT: br label [[K:%.*]]
270 ; BOOLEAN-NEXT: br i1 [[PREDD:%.*]], label [[LOOP_EXIT_GUARD]], label [[I]]
272 ; BOOLEAN-NEXT: tail call fastcc void @check(i32 4) #[[ATTR0]]
273 ; BOOLEAN-NEXT: br label [[K]]
275 ; BOOLEAN-NEXT: [[INC2]] = add i32 [[INC1]], 1
276 ; BOOLEAN-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC2]], 10
277 ; BOOLEAN-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]]
279 ; BOOLEAN-NEXT: br label [[L]]
281 ; BOOLEAN-NEXT: br label [[L]]
283 ; BOOLEAN-NEXT: ret void
284 ; BOOLEAN: loop.exit.guard:
285 ; BOOLEAN-NEXT: [[GUARD_B:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ], [ false, [[E]] ], [ false, [[G]] ], [ false, [[I]] ]
286 ; BOOLEAN-NEXT: [[GUARD_D:%.*]] = phi i1 [ false, [[A]] ], [ true, [[C]] ], [ false, [[E]] ], [ false, [[G]] ], [ false, [[I]] ]
287 ; BOOLEAN-NEXT: [[GUARD_F:%.*]] = phi i1 [ false, [[A]] ], [ false, [[C]] ], [ true, [[E]] ], [ false, [[G]] ], [ false, [[I]] ]
288 ; BOOLEAN-NEXT: [[GUARD_H:%.*]] = phi i1 [ false, [[A]] ], [ false, [[C]] ], [ false, [[E]] ], [ true, [[G]] ], [ false, [[I]] ]
289 ; BOOLEAN-NEXT: br i1 [[GUARD_B]], label [[B:%.*]], label [[LOOP_EXIT_GUARD1:%.*]]
290 ; BOOLEAN: loop.exit.guard1:
291 ; BOOLEAN-NEXT: br i1 [[GUARD_D]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]]
292 ; BOOLEAN: loop.exit.guard2:
293 ; BOOLEAN-NEXT: br i1 [[GUARD_F]], label [[F:%.*]], label [[LOOP_EXIT_GUARD3:%.*]]
294 ; BOOLEAN: loop.exit.guard3:
295 ; BOOLEAN-NEXT: br i1 [[GUARD_H]], label [[H:%.*]], label [[L]]
298 br i1 %PredEntry, label %A, label %L
301 %inc1 = phi i32 [ 0, %entry ], [ %inc2, %I ]
302 br i1 %PredA, label %B, label %C
305 tail call fastcc void @check(i32 1) #0
309 br i1 %PredB, label %D, label %E
312 tail call fastcc void @check(i32 2) #0
316 br i1 %PredC, label %F, label %G
319 tail call fastcc void @check(i32 3) #0
323 br i1 %PredD, label %H, label %I
326 tail call fastcc void @check(i32 4) #0
330 %inc2 = add i32 %inc1, 1
331 %cmp = icmp ult i32 %inc2, 10
332 br i1 %cmp, label %A, label %L
345 declare void @check(i32 noundef %i) #0
347 attributes #0 = { noreturn nounwind }