xref: /llvm-project/llvm/test/Transforms/UnifyLoopExits/integer_guards.ll (revision 5f6172f0684b6a224d207ff8d093fc9aad92e331)
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
4
5; A loop with multiple exit blocks.
6
7define void @loop_two_exits(i1 %PredEntry, i1 %PredA) {
8; CHECK-LABEL: @loop_two_exits(
9; CHECK-NEXT:  entry:
10; CHECK-NEXT:    br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[E:%.*]]
11; CHECK:       A:
12; CHECK-NEXT:    [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ]
13; CHECK-NEXT:    br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C]]
14; CHECK:       B:
15; CHECK-NEXT:    tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]]
16; CHECK-NEXT:    br label [[D:%.*]]
17; CHECK:       C:
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]]
21; CHECK:       D:
22; CHECK-NEXT:    unreachable
23; CHECK:       E:
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]]
29;
30; BOOLEAN-LABEL: @loop_two_exits(
31; BOOLEAN-NEXT:  entry:
32; BOOLEAN-NEXT:    br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[E:%.*]]
33; BOOLEAN:       A:
34; BOOLEAN-NEXT:    [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[C:%.*]] ]
35; BOOLEAN-NEXT:    br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C]]
36; BOOLEAN:       B:
37; BOOLEAN-NEXT:    tail call fastcc void @check(i32 1) #[[ATTR0:[0-9]+]]
38; BOOLEAN-NEXT:    br label [[D:%.*]]
39; BOOLEAN:       C:
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]]
43; BOOLEAN:       D:
44; BOOLEAN-NEXT:    unreachable
45; BOOLEAN:       E:
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]]
50;
51entry:
52  br i1 %PredEntry, label %A, label %E
53
54A:
55  %inc1 = phi i32 [ 0, %entry ], [ %inc2, %C ]
56  br i1 %PredA, label %B, label %C
57
58B:
59  tail call fastcc void @check(i32 1) #0
60  br label %D
61
62C:
63  %inc2 = add i32 %inc1, 1
64  %cmp = icmp ult i32 %inc2, 10
65  br i1 %cmp, label %A, label %E
66
67D:
68  unreachable
69
70E:
71  ret void
72}
73
74; The loop exit blocks appear in an inner loop.
75
76define void @inner_loop(i1 %PredEntry, i1 %PredA, i1 %PredB) {
77; CHECK-LABEL: @inner_loop(
78; CHECK-NEXT:  entry:
79; CHECK-NEXT:    br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[I:%.*]]
80; CHECK:       A:
81; CHECK-NEXT:    [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ]
82; CHECK-NEXT:    br label [[B:%.*]]
83; CHECK:       B:
84; CHECK-NEXT:    [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ]
85; CHECK-NEXT:    br i1 [[PREDA:%.*]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]]
86; CHECK:       C:
87; CHECK-NEXT:    tail call fastcc void @check(i32 1) #[[ATTR0]]
88; CHECK-NEXT:    br label [[H:%.*]]
89; CHECK:       D:
90; CHECK-NEXT:    br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD2]], label [[F]]
91; CHECK:       E:
92; CHECK-NEXT:    tail call fastcc void @check(i32 2) #[[ATTR0]]
93; CHECK-NEXT:    br label [[H]]
94; CHECK:       F:
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]]
98; CHECK:       G:
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:%.*]]
102; CHECK:       H:
103; CHECK-NEXT:    unreachable
104; CHECK:       I:
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]] ], [ poison, [[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]]
118;
119; BOOLEAN-LABEL: @inner_loop(
120; BOOLEAN-NEXT:  entry:
121; BOOLEAN-NEXT:    br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[I:%.*]]
122; BOOLEAN:       A:
123; BOOLEAN-NEXT:    [[OUTER1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[OUTER2:%.*]], [[G:%.*]] ]
124; BOOLEAN-NEXT:    br label [[B:%.*]]
125; BOOLEAN:       B:
126; BOOLEAN-NEXT:    [[INNER1:%.*]] = phi i32 [ 0, [[A]] ], [ [[INNER2:%.*]], [[F:%.*]] ]
127; BOOLEAN-NEXT:    br i1 [[PREDA:%.*]], label [[D:%.*]], label [[LOOP_EXIT_GUARD2:%.*]]
128; BOOLEAN:       C:
129; BOOLEAN-NEXT:    tail call fastcc void @check(i32 1) #[[ATTR0]]
130; BOOLEAN-NEXT:    br label [[H:%.*]]
131; BOOLEAN:       D:
132; BOOLEAN-NEXT:    br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD2]], label [[F]]
133; BOOLEAN:       E:
134; BOOLEAN-NEXT:    tail call fastcc void @check(i32 2) #[[ATTR0]]
135; BOOLEAN-NEXT:    br label [[H]]
136; BOOLEAN:       F:
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]]
140; BOOLEAN:       G:
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:%.*]]
144; BOOLEAN:       H:
145; BOOLEAN-NEXT:    unreachable
146; BOOLEAN:       I:
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]] ], [ poison, [[F]] ]
156; BOOLEAN-NEXT:    [[GUARD_C_MOVED]] = phi i1 [ true, [[B]] ], [ false, [[D]] ], [ poison, [[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]]
159;
160entry:
161  br i1 %PredEntry, label %A, label %I
162
163A:
164  %outer1 = phi i32 [ 0, %entry ], [ %outer2, %G ]
165  br label %B
166
167B:
168  %inner1 = phi i32 [ 0, %A ], [ %inner2, %F ]
169  br i1 %PredA, label %D, label %C
170
171C:
172  tail call fastcc void @check(i32 1) #0
173  br label %H
174
175D:
176  br i1 %PredB, label %E, label %F
177
178E:
179  tail call fastcc void @check(i32 2) #0
180  br label %H
181
182F:
183  %inner2 = add i32 %inner1, 1
184  %cmp1 = icmp ult i32 %inner2, 20
185  br i1 %cmp1, label %B, label %G
186
187G:
188  %outer2 = add i32 %outer1, 1
189  %cmp2 = icmp ult i32 %outer2, 10
190  br i1 %cmp2, label %A, label %I
191
192H:
193  unreachable
194
195I:
196  ret void
197}
198
199; A loop with more exit blocks.
200
201define void @loop_five_exits(i1 %PredEntry, i1 %PredA, i1 %PredB, i1 %PredC, i1 %PredD) {
202; CHECK-LABEL: @loop_five_exits(
203; CHECK-NEXT:  entry:
204; CHECK-NEXT:    br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[L:%.*]]
205; CHECK:       A:
206; CHECK-NEXT:    [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ]
207; CHECK-NEXT:    br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C:%.*]]
208; CHECK:       B:
209; CHECK-NEXT:    tail call fastcc void @check(i32 1) #[[ATTR0]]
210; CHECK-NEXT:    br label [[J:%.*]]
211; CHECK:       C:
212; CHECK-NEXT:    br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD]], label [[E:%.*]]
213; CHECK:       D:
214; CHECK-NEXT:    tail call fastcc void @check(i32 2) #[[ATTR0]]
215; CHECK-NEXT:    br label [[J]]
216; CHECK:       E:
217; CHECK-NEXT:    br i1 [[PREDC:%.*]], label [[LOOP_EXIT_GUARD]], label [[G:%.*]]
218; CHECK:       F:
219; CHECK-NEXT:    tail call fastcc void @check(i32 3) #[[ATTR0]]
220; CHECK-NEXT:    br label [[K:%.*]]
221; CHECK:       G:
222; CHECK-NEXT:    br i1 [[PREDD:%.*]], label [[LOOP_EXIT_GUARD]], label [[I]]
223; CHECK:       H:
224; CHECK-NEXT:    tail call fastcc void @check(i32 4) #[[ATTR0]]
225; CHECK-NEXT:    br label [[K]]
226; CHECK:       I:
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]]
230; CHECK:       J:
231; CHECK-NEXT:    br label [[L]]
232; CHECK:       K:
233; CHECK-NEXT:    br label [[L]]
234; CHECK:       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]]
249;
250; BOOLEAN-LABEL: @loop_five_exits(
251; BOOLEAN-NEXT:  entry:
252; BOOLEAN-NEXT:    br i1 [[PREDENTRY:%.*]], label [[A:%.*]], label [[L:%.*]]
253; BOOLEAN:       A:
254; BOOLEAN-NEXT:    [[INC1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC2:%.*]], [[I:%.*]] ]
255; BOOLEAN-NEXT:    br i1 [[PREDA:%.*]], label [[LOOP_EXIT_GUARD:%.*]], label [[C:%.*]]
256; BOOLEAN:       B:
257; BOOLEAN-NEXT:    tail call fastcc void @check(i32 1) #[[ATTR0]]
258; BOOLEAN-NEXT:    br label [[J:%.*]]
259; BOOLEAN:       C:
260; BOOLEAN-NEXT:    br i1 [[PREDB:%.*]], label [[LOOP_EXIT_GUARD]], label [[E:%.*]]
261; BOOLEAN:       D:
262; BOOLEAN-NEXT:    tail call fastcc void @check(i32 2) #[[ATTR0]]
263; BOOLEAN-NEXT:    br label [[J]]
264; BOOLEAN:       E:
265; BOOLEAN-NEXT:    br i1 [[PREDC:%.*]], label [[LOOP_EXIT_GUARD]], label [[G:%.*]]
266; BOOLEAN:       F:
267; BOOLEAN-NEXT:    tail call fastcc void @check(i32 3) #[[ATTR0]]
268; BOOLEAN-NEXT:    br label [[K:%.*]]
269; BOOLEAN:       G:
270; BOOLEAN-NEXT:    br i1 [[PREDD:%.*]], label [[LOOP_EXIT_GUARD]], label [[I]]
271; BOOLEAN:       H:
272; BOOLEAN-NEXT:    tail call fastcc void @check(i32 4) #[[ATTR0]]
273; BOOLEAN-NEXT:    br label [[K]]
274; BOOLEAN:       I:
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]]
278; BOOLEAN:       J:
279; BOOLEAN-NEXT:    br label [[L]]
280; BOOLEAN:       K:
281; BOOLEAN-NEXT:    br label [[L]]
282; BOOLEAN:       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]]
296;
297entry:
298  br i1 %PredEntry, label %A, label %L
299
300A:
301  %inc1 = phi i32 [ 0, %entry ], [ %inc2, %I ]
302  br i1 %PredA, label %B, label %C
303
304B:
305  tail call fastcc void @check(i32 1) #0
306  br label %J
307
308C:
309  br i1 %PredB, label %D, label %E
310
311D:
312  tail call fastcc void @check(i32 2) #0
313  br label %J
314
315E:
316  br i1 %PredC, label %F, label %G
317
318F:
319  tail call fastcc void @check(i32 3) #0
320  br label %K
321
322G:
323  br i1 %PredD, label %H, label %I
324
325H:
326  tail call fastcc void @check(i32 4) #0
327  br label %K
328
329I:
330  %inc2 = add i32 %inc1, 1
331  %cmp = icmp ult i32 %inc2, 10
332  br i1 %cmp, label %A, label %L
333
334J:
335  br label %L
336
337K:
338  br label %L
339
340L:
341  ret void
342}
343
344
345declare void @check(i32 noundef %i) #0
346
347attributes #0 = { noreturn nounwind }
348
349