1 ; RUN: opt -S -guard-widening < %s | FileCheck %s
2 ; RUN: opt -S -passes=guard-widening < %s | FileCheck %s
4 declare void @llvm.experimental.guard(i1,...)
6 ; Basic test case: we wide the first check to check both the
8 define void @f_0(i1 %cond_0, i1 %cond_1) {
11 ; CHECK: %wide.chk = and i1 %cond_0, %cond_1
12 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
15 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
16 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
20 ; Same as @f_0, but with using a more general notion of postdominance.
21 define void @f_1(i1 %cond_0, i1 %cond_1) {
24 ; CHECK: %wide.chk = and i1 %cond_0, %cond_1
25 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
26 ; CHECK: br i1 undef, label %left, label %right
28 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
29 br i1 undef, label %left, label %right
39 ; CHECK-NOT: call void (i1, ...) @llvm.experimental.guard(
41 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
45 ; Like @f_1, but we have some code we need to hoist before we can
46 ; widen a dominanting check.
47 define void @f_2(i32 %a, i32 %b) {
50 ; CHECK: %cond_0 = icmp ult i32 %a, 10
51 ; CHECK: %cond_1 = icmp ult i32 %b, 10
52 ; CHECK: %wide.chk = and i1 %cond_0, %cond_1
53 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
54 ; CHECK: br i1 undef, label %left, label %right
56 %cond_0 = icmp ult i32 %a, 10
57 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
58 br i1 undef, label %left, label %right
67 %cond_1 = icmp ult i32 %b, 10
68 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
72 ; Negative test: don't hoist stuff out of control flow
73 ; indiscriminately, since that can make us do more work than needed.
74 define void @f_3(i32 %a, i32 %b) {
77 ; CHECK: %cond_0 = icmp ult i32 %a, 10
78 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
79 ; CHECK: br i1 undef, label %left, label %right
81 %cond_0 = icmp ult i32 %a, 10
82 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
83 br i1 undef, label %left, label %right
87 ; CHECK: %cond_1 = icmp ult i32 %b, 10
88 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
91 %cond_1 = icmp ult i32 %b, 10
92 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
99 ; But hoisting out of control flow is fine if it makes a loop computed
100 ; condition loop invariant. This behavior may require some tuning in
102 define void @f_4(i32 %a, i32 %b) {
105 ; CHECK: %cond_0 = icmp ult i32 %a, 10
106 ; CHECK: %cond_1 = icmp ult i32 %b, 10
107 ; CHECK: %wide.chk = and i1 %cond_0, %cond_1
108 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
109 ; CHECK: br i1 undef, label %loop, label %leave
111 %cond_0 = icmp ult i32 %a, 10
112 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
113 br i1 undef, label %loop, label %leave
116 %cond_1 = icmp ult i32 %b, 10
117 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
118 br i1 undef, label %loop, label %leave
124 ; Hoisting out of control flow is also fine if we can widen the
125 ; dominating check without doing any extra work.
126 define void @f_5(i32 %a) {
129 ; CHECK: %wide.chk = icmp uge i32 %a, 11
130 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
131 ; CHECK: br i1 undef, label %left, label %right
133 %cond_0 = icmp ugt i32 %a, 7
134 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
135 br i1 undef, label %left, label %right
138 %cond_1 = icmp ugt i32 %a, 10
139 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
146 ; Negative test: the load from %a can be safely speculated to before
147 ; the first guard, but there is no guarantee that it will produce the
149 define void @f_6(i1* dereferenceable(32) %a, i1* %b, i1 %unknown) {
151 ; CHECK: call void (i1, ...) @llvm.experimental.guard(
152 ; CHECK: call void (i1, ...) @llvm.experimental.guard(
155 %cond_0 = load i1, i1* %a
156 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
157 store i1 %unknown, i1* %b
158 %cond_1 = load i1, i1* %a
159 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
163 ; All else equal, we try to widen the earliest guard we can. This
164 ; heuristic can use some tuning.
165 define void @f_7(i32 %a, i1* %cond_buf) {
168 ; CHECK: %cond_1 = load volatile i1, i1* %cond_buf
169 ; CHECK: %cond_3 = icmp ult i32 %a, 7
170 ; CHECK: %wide.chk = and i1 %cond_1, %cond_3
171 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
172 ; CHECK: %cond_2 = load volatile i1, i1* %cond_buf
173 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ]
174 ; CHECK: br i1 undef, label %left, label %right
176 %cond_1 = load volatile i1, i1* %cond_buf
177 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
178 %cond_2 = load volatile i1, i1* %cond_buf
179 call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ]
180 br i1 undef, label %left, label %right
183 %cond_3 = icmp ult i32 %a, 7
184 call void(i1, ...) @llvm.experimental.guard(i1 %cond_3) [ "deopt"() ]
191 ; In this case the earliest dominating guard is in a loop, and we
192 ; don't want to put extra work in there. This heuristic can use some
194 define void @f_8(i32 %a, i1 %cond_1, i1 %cond_2) {
200 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
201 br i1 undef, label %loop, label %leave
205 ; CHECK: %cond_3 = icmp ult i32 %a, 7
206 ; CHECK: %wide.chk = and i1 %cond_2, %cond_3
207 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
208 ; CHECK: br i1 undef, label %loop2, label %leave2
210 call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ]
211 br i1 undef, label %loop2, label %leave2
214 %cond_3 = icmp ult i32 %a, 7
215 call void(i1, ...) @llvm.experimental.guard(i1 %cond_3) [ "deopt"() ]
222 ; In cases like these where there isn't any "obviously profitable"
223 ; widening sites, we refuse to do anything.
224 define void @f_9(i32 %a, i1 %cond_0, i1 %cond_1) {
231 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
232 ; CHECK: br i1 undef, label %first_loop, label %second_loop
234 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
235 br i1 undef, label %first_loop, label %second_loop
238 ; CHECK: second_loop:
239 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
240 ; CHECK: br label %second_loop
242 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
243 br label %second_loop
246 ; Same situation as in @f_9: no "obviously profitable" widening sites,
247 ; so we refuse to do anything.
248 define void @f_10(i32 %a, i1 %cond_0, i1 %cond_1) {
249 ; CHECK-LABEL: @f_10(
255 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
256 ; CHECK: br i1 undef, label %loop, label %no_loop
258 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
259 br i1 undef, label %loop, label %no_loop
263 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
265 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
269 ; With guards in loops, we're okay hoisting out the guard into the
271 define void @f_11(i32 %a, i1 %cond_0, i1 %cond_1) {
272 ; CHECK-LABEL: @f_11(
278 ; CHECK: %wide.chk = and i1 %cond_0, %cond_1
279 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
280 ; CHECK: br i1 undef, label %inner, label %outer
282 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
283 br i1 undef, label %inner, label %outer
286 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
290 ; Checks that we are adequately guarded against exponential-time
291 ; behavior when hoisting code.
292 define void @f_12(i32 %a0) {
295 ; Eliding the earlier 29 multiplications for brevity
296 ; CHECK: %a30 = mul i32 %a29, %a29
297 ; CHECK-NEXT: %cond = trunc i32 %a30 to i1
298 ; CHECK-NEXT: %wide.chk = and i1 true, %cond
299 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
300 ; CHECK-NEXT: ret void
303 call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
304 %a1 = mul i32 %a0, %a0
305 %a2 = mul i32 %a1, %a1
306 %a3 = mul i32 %a2, %a2
307 %a4 = mul i32 %a3, %a3
308 %a5 = mul i32 %a4, %a4
309 %a6 = mul i32 %a5, %a5
310 %a7 = mul i32 %a6, %a6
311 %a8 = mul i32 %a7, %a7
312 %a9 = mul i32 %a8, %a8
313 %a10 = mul i32 %a9, %a9
314 %a11 = mul i32 %a10, %a10
315 %a12 = mul i32 %a11, %a11
316 %a13 = mul i32 %a12, %a12
317 %a14 = mul i32 %a13, %a13
318 %a15 = mul i32 %a14, %a14
319 %a16 = mul i32 %a15, %a15
320 %a17 = mul i32 %a16, %a16
321 %a18 = mul i32 %a17, %a17
322 %a19 = mul i32 %a18, %a18
323 %a20 = mul i32 %a19, %a19
324 %a21 = mul i32 %a20, %a20
325 %a22 = mul i32 %a21, %a21
326 %a23 = mul i32 %a22, %a22
327 %a24 = mul i32 %a23, %a23
328 %a25 = mul i32 %a24, %a24
329 %a26 = mul i32 %a25, %a25
330 %a27 = mul i32 %a26, %a26
331 %a28 = mul i32 %a27, %a27
332 %a29 = mul i32 %a28, %a28
333 %a30 = mul i32 %a29, %a29
334 %cond = trunc i32 %a30 to i1
335 call void(i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
339 define void @f_13(i32 %a) {
340 ; CHECK-LABEL: @f_13(
342 ; CHECK: %wide.chk = icmp ult i32 %a, 10
343 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %wide.chk) [ "deopt"() ]
344 ; CHECK: br i1 undef, label %left, label %right
346 %cond_0 = icmp ult i32 %a, 14
347 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
348 br i1 undef, label %left, label %right
351 %cond_1 = icmp slt i32 %a, 10
352 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
359 define void @f_14(i32 %a) {
360 ; CHECK-LABEL: @f_14(
362 ; CHECK: %cond_0 = icmp ult i32 %a, 14
363 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
364 ; CHECK: br i1 undef, label %left, label %right
366 %cond_0 = icmp ult i32 %a, 14
367 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
368 br i1 undef, label %left, label %right
372 ; CHECK: %cond_1 = icmp sgt i32 %a, 10
373 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
375 %cond_1 = icmp sgt i32 %a, 10
376 call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ]
383 ; Make sure we do not widen guard by trivial true conditions into something.
384 define void @f_15(i1 %cond_0, i1 %cond_1) {
385 ; CHECK-LABEL: @f_15(
387 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
388 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
391 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
392 call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
396 ; Make sure we do not widen guard by trivial false conditions into something.
397 define void @f_16(i1 %cond_0, i1 %cond_1) {
398 ; CHECK-LABEL: @f_16(
400 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
401 ; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ]
404 call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ]
405 call void(i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ]