xref: /llvm-project/llvm/test/Transforms/JumpThreading/guards.ll (revision 07e34d2de565a88da2724d52cdcf47b4bca873db)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -S -passes=jump-threading,dce < %s | FileCheck %s
3
4declare void @llvm.experimental.guard(i1, ...)
5
6declare i32 @f1()
7declare i32 @f2()
8
9define i32 @branch_implies_guard(i32 %a) {
10; CHECK-LABEL: @branch_implies_guard(
11; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 10
12; CHECK-NEXT:    br i1 [[COND]], label [[T1_SPLIT:%.*]], label [[F1_SPLIT:%.*]]
13; CHECK:       T1.split:
14; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1()
15; CHECK-NEXT:    [[RETVAL3:%.*]] = add i32 [[V1]], 10
16; CHECK-NEXT:    br label [[MERGE:%.*]]
17; CHECK:       F1.split:
18; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2()
19; CHECK-NEXT:    [[RETVAL1:%.*]] = add i32 [[V2]], 10
20; CHECK-NEXT:    [[CONDGUARD2:%.*]] = icmp slt i32 [[A]], 20
21; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD2]]) [ "deopt"() ]
22; CHECK-NEXT:    br label [[MERGE]]
23; CHECK:       Merge:
24; CHECK-NEXT:    [[TMP1:%.*]] = phi i32 [ [[RETVAL3]], [[T1_SPLIT]] ], [ [[RETVAL1]], [[F1_SPLIT]] ]
25; CHECK-NEXT:    ret i32 [[TMP1]]
26;
27  %cond = icmp slt i32 %a, 10
28  br i1 %cond, label %T1, label %F1
29
30T1:
31  %v1 = call i32 @f1()
32  br label %Merge
33
34F1:
35  %v2 = call i32 @f2()
36  br label %Merge
37
38Merge:
39  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
40  %retVal = add i32 %retPhi, 10
41  %condGuard = icmp slt i32 %a, 20
42  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
43  ret i32 %retVal
44}
45
46define i32 @not_branch_implies_guard(i32 %a) {
47; CHECK-LABEL: @not_branch_implies_guard(
48; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 20
49; CHECK-NEXT:    br i1 [[COND]], label [[T1_SPLIT:%.*]], label [[F1_SPLIT:%.*]]
50; CHECK:       T1.split:
51; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1()
52; CHECK-NEXT:    [[RETVAL1:%.*]] = add i32 [[V1]], 10
53; CHECK-NEXT:    [[CONDGUARD2:%.*]] = icmp sgt i32 [[A]], 10
54; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD2]]) [ "deopt"() ]
55; CHECK-NEXT:    br label [[MERGE:%.*]]
56; CHECK:       F1.split:
57; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2()
58; CHECK-NEXT:    [[RETVAL3:%.*]] = add i32 [[V2]], 10
59; CHECK-NEXT:    br label [[MERGE]]
60; CHECK:       Merge:
61; CHECK-NEXT:    [[TMP1:%.*]] = phi i32 [ [[RETVAL3]], [[F1_SPLIT]] ], [ [[RETVAL1]], [[T1_SPLIT]] ]
62; CHECK-NEXT:    ret i32 [[TMP1]]
63;
64  %cond = icmp slt i32 %a, 20
65  br i1 %cond, label %T1, label %F1
66
67T1:
68  %v1 = call i32 @f1()
69  br label %Merge
70
71F1:
72  %v2 = call i32 @f2()
73  br label %Merge
74
75Merge:
76  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
77  %retVal = add i32 %retPhi, 10
78  %condGuard = icmp sgt i32 %a, 10
79  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
80  ret i32 %retVal
81}
82
83define i32 @branch_overlaps_guard(i32 %a) {
84; CHECK-LABEL: @branch_overlaps_guard(
85; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 20
86; CHECK-NEXT:    br i1 [[COND]], label [[T1:%.*]], label [[F1:%.*]]
87; CHECK:       T1:
88; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1()
89; CHECK-NEXT:    br label [[MERGE:%.*]]
90; CHECK:       F1:
91; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2()
92; CHECK-NEXT:    br label [[MERGE]]
93; CHECK:       Merge:
94; CHECK-NEXT:    [[RETPHI:%.*]] = phi i32 [ [[V1]], [[T1]] ], [ [[V2]], [[F1]] ]
95; CHECK-NEXT:    [[RETVAL:%.*]] = add i32 [[RETPHI]], 10
96; CHECK-NEXT:    [[CONDGUARD:%.*]] = icmp slt i32 [[A]], 10
97; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD]]) [ "deopt"() ]
98; CHECK-NEXT:    ret i32 [[RETVAL]]
99;
100  %cond = icmp slt i32 %a, 20
101  br i1 %cond, label %T1, label %F1
102
103T1:
104  %v1 = call i32 @f1()
105  br label %Merge
106
107F1:
108  %v2 = call i32 @f2()
109  br label %Merge
110
111Merge:
112  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
113  %retVal = add i32 %retPhi, 10
114  %condGuard = icmp slt i32 %a, 10
115  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
116  ret i32 %retVal
117}
118
119define i32 @branch_doesnt_overlap_guard(i32 %a) {
120; CHECK-LABEL: @branch_doesnt_overlap_guard(
121; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 10
122; CHECK-NEXT:    br i1 [[COND]], label [[T1:%.*]], label [[F1:%.*]]
123; CHECK:       T1:
124; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1()
125; CHECK-NEXT:    br label [[MERGE:%.*]]
126; CHECK:       F1:
127; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2()
128; CHECK-NEXT:    br label [[MERGE]]
129; CHECK:       Merge:
130; CHECK-NEXT:    [[RETPHI:%.*]] = phi i32 [ [[V1]], [[T1]] ], [ [[V2]], [[F1]] ]
131; CHECK-NEXT:    [[RETVAL:%.*]] = add i32 [[RETPHI]], 10
132; CHECK-NEXT:    [[CONDGUARD:%.*]] = icmp sgt i32 [[A]], 20
133; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD]]) [ "deopt"() ]
134; CHECK-NEXT:    ret i32 [[RETVAL]]
135;
136  %cond = icmp slt i32 %a, 10
137  br i1 %cond, label %T1, label %F1
138
139T1:
140  %v1 = call i32 @f1()
141  br label %Merge
142
143F1:
144  %v2 = call i32 @f2()
145  br label %Merge
146
147Merge:
148  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
149  %retVal = add i32 %retPhi, 10
150  %condGuard = icmp sgt i32 %a, 20
151  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
152  ret i32 %retVal
153}
154
155define i32 @not_a_diamond1(i32 %a, i1 %cond1) {
156; CHECK-LABEL: @not_a_diamond1(
157; CHECK-NEXT:    br i1 [[COND1:%.*]], label [[PRED:%.*]], label [[EXIT:%.*]]
158; CHECK:       Pred:
159; CHECK-NEXT:    switch i32 [[A:%.*]], label [[EXIT]] [
160; CHECK-NEXT:    i32 10, label [[MERGE:%.*]]
161; CHECK-NEXT:    i32 20, label [[MERGE]]
162; CHECK-NEXT:    ]
163; CHECK:       Merge:
164; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND1]]) [ "deopt"() ]
165; CHECK-NEXT:    br label [[EXIT]]
166; CHECK:       Exit:
167; CHECK-NEXT:    ret i32 [[A]]
168;
169  br i1 %cond1, label %Pred, label %Exit
170
171Pred:
172  switch i32 %a, label %Exit [
173  i32 10, label %Merge
174  i32 20, label %Merge
175  ]
176
177Merge:
178  call void(i1, ...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
179  br label %Exit
180
181Exit:
182  ret i32 %a
183}
184
185define void @not_a_diamond2(i32 %a, i1 %cond1) {
186; CHECK-LABEL: @not_a_diamond2(
187; CHECK-NEXT:  Pred:
188; CHECK-NEXT:    switch i32 [[A:%.*]], label [[EXIT:%.*]] [
189; CHECK-NEXT:    i32 10, label [[MERGE:%.*]]
190; CHECK-NEXT:    i32 20, label [[MERGE]]
191; CHECK-NEXT:    ]
192; CHECK:       Merge:
193; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND1:%.*]]) [ "deopt"() ]
194; CHECK-NEXT:    ret void
195; CHECK:       Exit:
196; CHECK-NEXT:    ret void
197;
198  br label %Parent
199
200Merge:
201  call void(i1, ...) @llvm.experimental.guard(i1 %cond1)[ "deopt"() ]
202  ret void
203
204Pred:
205  switch i32 %a, label %Exit [
206  i32 10, label %Merge
207  i32 20, label %Merge
208  ]
209
210Parent:
211  br label %Pred
212
213Exit:
214  ret void
215}
216
217declare void @never_called(i1)
218
219; LVI uses guard to identify value of %c2 in branch as true, we cannot replace that
220; guard with guard(true & c1).
221define void @dont_fold_guard(ptr %addr, i32 %i, i32 %length) {
222; CHECK-LABEL: @dont_fold_guard(
223; CHECK-NEXT:  BB1:
224; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[I:%.*]], [[LENGTH:%.*]]
225; CHECK-NEXT:    [[C2:%.*]] = icmp eq i32 [[I]], 0
226; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]
227; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
228; CHECK-NEXT:    call void @never_called(i1 true)
229; CHECK-NEXT:    ret void
230;
231  %c1 = icmp ult i32 %i, %length
232  %c2 = icmp eq i32 %i, 0
233  %wide.chk = and i1 %c1, %c2
234  call void(i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
235  br i1 %c2, label %BB1, label %BB2
236
237BB1:
238  call void @never_called(i1 %c2)
239  ret void
240
241BB2:
242  ret void
243}
244
245declare void @dummy(i1) nounwind willreturn
246; same as dont_fold_guard1 but there's a use immediately after guard and before
247; branch. We can fold that use.
248define void @dont_fold_guard2(ptr %addr, i32 %i, i32 %length) {
249; CHECK-LABEL: @dont_fold_guard2(
250; CHECK-NEXT:  BB1:
251; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[I:%.*]], [[LENGTH:%.*]]
252; CHECK-NEXT:    [[C2:%.*]] = icmp eq i32 [[I]], 0
253; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[C1]], [[C2]]
254; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ]
255; CHECK-NEXT:    call void @dummy(i1 true)
256; CHECK-NEXT:    call void @never_called(i1 true)
257; CHECK-NEXT:    ret void
258;
259  %c1 = icmp ult i32 %i, %length
260  %c2 = icmp eq i32 %i, 0
261  %wide.chk = and i1 %c1, %c2
262  call void(i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
263  call void @dummy(i1 %c2)
264  br i1 %c2, label %BB1, label %BB2
265
266BB1:
267  call void @never_called(i1 %c2)
268  ret void
269
270BB2:
271  ret void
272}
273
274; same as dont_fold_guard1 but condition %cmp is not an instruction.
275; We cannot fold the guard under any circumstance.
276; FIXME: We can merge unreachableBB2 into not_zero.
277define void @dont_fold_guard3(ptr %addr, i1 %cmp, i32 %i, i32 %length) {
278; CHECK-LABEL: @dont_fold_guard3(
279; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CMP:%.*]]) [ "deopt"() ]
280; CHECK-NEXT:    br i1 [[CMP]], label [[BB1:%.*]], label [[BB2:%.*]]
281; CHECK:       BB1:
282; CHECK-NEXT:    call void @never_called(i1 [[CMP]])
283; CHECK-NEXT:    ret void
284; CHECK:       BB2:
285; CHECK-NEXT:    ret void
286;
287  call void(i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ]
288  br i1 %cmp, label %BB1, label %BB2
289
290BB1:
291  call void @never_called(i1 %cmp)
292  ret void
293
294BB2:
295  ret void
296}
297
298declare void @f(i1)
299; Same as dont_fold_guard1 but use switch instead of branch.
300; triggers source code `ProcessThreadableEdges`.
301define void @dont_fold_guard4(i1 %cmp1, i32 %i) nounwind {
302; CHECK-LABEL: @dont_fold_guard4(
303; CHECK-NEXT:  entry:
304; CHECK-NEXT:    br i1 [[CMP1:%.*]], label [[L2:%.*]], label [[L3:%.*]]
305; CHECK:       L2:
306; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[I:%.*]], 0
307; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ]
308; CHECK-NEXT:    call void @dummy(i1 true)
309; CHECK-NEXT:    call void @f(i1 true)
310; CHECK-NEXT:    ret void
311; CHECK:       L3:
312; CHECK-NEXT:    ret void
313;
314entry:
315  br i1 %cmp1, label %L0, label %L3
316L0:
317  %cmp = icmp eq i32 %i, 0
318  call void(i1, ...) @llvm.experimental.guard(i1 %cmp) [ "deopt"() ]
319  call void @dummy(i1 %cmp)
320  switch i1 %cmp, label %L3 [
321  i1 false, label %L1
322  i1 true, label %L2
323  ]
324
325L1:
326  ret void
327L2:
328  call void @f(i1 %cmp)
329  ret void
330L3:
331  ret void
332}
333
334; Make sure that we don't PRE a non-speculable load across a guard.
335define void @unsafe_pre_across_guard(ptr %p, i1 %load.is.valid) {
336; CHECK-LABEL: @unsafe_pre_across_guard(
337; CHECK-NEXT:  entry:
338; CHECK-NEXT:    br label [[LOOP:%.*]]
339; CHECK:       loop:
340; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[LOAD_IS_VALID:%.*]]) [ "deopt"() ]
341; CHECK-NEXT:    [[LOADED:%.*]] = load i8, ptr [[P:%.*]], align 1
342; CHECK-NEXT:    [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0
343; CHECK-NEXT:    br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]
344; CHECK:       exit:
345; CHECK-NEXT:    ret void
346;
347entry:
348  br label %loop
349
350loop:                                             ; preds = %loop, %entry
351  call void (i1, ...) @llvm.experimental.guard(i1 %load.is.valid) [ "deopt"() ]
352  %loaded = load i8, ptr %p
353  %continue = icmp eq i8 %loaded, 0
354  br i1 %continue, label %exit, label %loop
355
356exit:                                             ; preds = %loop
357  ret void
358}
359
360; Make sure that we can safely PRE a speculable load across a guard.
361define void @safe_pre_across_guard(ptr noalias nocapture readonly dereferenceable(8) %p, i1 %load.is.valid) nofree nosync {
362; CHECK-LABEL: @safe_pre_across_guard(
363; CHECK-NEXT:  entry:
364; CHECK-NEXT:    [[LOADED_PR:%.*]] = load i8, ptr [[P:%.*]], align 1
365; CHECK-NEXT:    br label [[LOOP:%.*]]
366; CHECK:       loop:
367; CHECK-NEXT:    [[LOADED:%.*]] = phi i8 [ [[LOADED]], [[LOOP]] ], [ [[LOADED_PR]], [[ENTRY:%.*]] ]
368; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[LOAD_IS_VALID:%.*]]) [ "deopt"() ]
369; CHECK-NEXT:    [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0
370; CHECK-NEXT:    br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]
371; CHECK:       exit:
372; CHECK-NEXT:    ret void
373;
374
375entry:
376  br label %loop
377
378loop:                                             ; preds = %loop, %entry
379  call void (i1, ...) @llvm.experimental.guard(i1 %load.is.valid) [ "deopt"() ]
380  %loaded = load i8, ptr %p
381  %continue = icmp eq i8 %loaded, 0
382  br i1 %continue, label %exit, label %loop
383
384exit:                                             ; preds = %loop
385  ret void
386}
387
388; Make sure that we don't PRE a non-speculable load across a call which may
389; alias with the load.
390define void @unsafe_pre_across_call(ptr %p) {
391; CHECK-LABEL: @unsafe_pre_across_call(
392; CHECK-NEXT:  entry:
393; CHECK-NEXT:    br label [[LOOP:%.*]]
394; CHECK:       loop:
395; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @f1()
396; CHECK-NEXT:    [[LOADED:%.*]] = load i8, ptr [[P:%.*]], align 1
397; CHECK-NEXT:    [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0
398; CHECK-NEXT:    br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]
399; CHECK:       exit:
400; CHECK-NEXT:    ret void
401;
402entry:
403  br label %loop
404
405loop:                                             ; preds = %loop, %entry
406  call i32 @f1()
407  %loaded = load i8, ptr %p
408  %continue = icmp eq i8 %loaded, 0
409  br i1 %continue, label %exit, label %loop
410
411exit:                                             ; preds = %loop
412  ret void
413}
414
415; Make sure that we can safely PRE a speculable load across a call.
416define void @safe_pre_across_call(ptr noalias nocapture readonly dereferenceable(8) %p) nofree nosync {
417; CHECK-LABEL: @safe_pre_across_call(
418; CHECK-NEXT:  entry:
419; CHECK-NEXT:    [[LOADED_PR:%.*]] = load i8, ptr [[P:%.*]], align 1
420; CHECK-NEXT:    br label [[LOOP:%.*]]
421; CHECK:       loop:
422; CHECK-NEXT:    [[LOADED:%.*]] = phi i8 [ [[LOADED]], [[LOOP]] ], [ [[LOADED_PR]], [[ENTRY:%.*]] ]
423; CHECK-NEXT:    [[TMP0:%.*]] = call i32 @f1()
424; CHECK-NEXT:    [[CONTINUE:%.*]] = icmp eq i8 [[LOADED]], 0
425; CHECK-NEXT:    br i1 [[CONTINUE]], label [[EXIT:%.*]], label [[LOOP]]
426; CHECK:       exit:
427; CHECK-NEXT:    ret void
428;
429
430entry:
431  br label %loop
432
433loop:                                             ; preds = %loop, %entry
434  call i32 @f1()
435  %loaded = load i8, ptr %p
436  %continue = icmp eq i8 %loaded, 0
437  br i1 %continue, label %exit, label %loop
438
439exit:                                             ; preds = %loop
440  ret void
441}
442