1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
2; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
3; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
4; RUN: opt -attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM
5; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM
6
7target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
8
9; Test cases specifically designed for the "undefined behavior" abstract function attribute.
10; We want to verify that whenever undefined behavior is assumed, the code becomes unreachable.
11; We use FIXME's to indicate problems and missing attributes.
12
13; -- Load tests --
14
15define void @load_wholly_unreachable() {
16; CHECK-LABEL: define {{[^@]+}}@load_wholly_unreachable()
17; CHECK-NEXT:    unreachable
18;
19  %a = load i32, i32* null
20  ret void
21}
22
23define void @loads_wholly_unreachable() {
24; CHECK-LABEL: define {{[^@]+}}@loads_wholly_unreachable()
25; CHECK-NEXT:    unreachable
26;
27  %a = load i32, i32* null
28  %b = load i32, i32* null
29  ret void
30}
31
32
33define void @load_single_bb_unreachable(i1 %cond) {
34; CHECK-LABEL: define {{[^@]+}}@load_single_bb_unreachable
35; CHECK-SAME: (i1 [[COND:%.*]])
36; CHECK-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[E:%.*]]
37; CHECK:       t:
38; CHECK-NEXT:    unreachable
39; CHECK:       e:
40; CHECK-NEXT:    ret void
41;
42  br i1 %cond, label %t, label %e
43t:
44  %b = load i32, i32* null
45  br label %e
46e:
47  ret void
48}
49
50; Note that while the load is removed (because it's unused), the block
51; is not changed to unreachable
52define void @load_null_pointer_is_defined() "null-pointer-is-valid"="true" {
53; CHECK-LABEL: define {{[^@]+}}@load_null_pointer_is_defined()
54; CHECK-NEXT:    ret void
55;
56  %a = load i32, i32* null
57  ret void
58}
59
60define internal i32* @ret_null() {
61; IS__CGSCC____-LABEL: define {{[^@]+}}@ret_null()
62; IS__CGSCC____-NEXT:    ret i32* null
63;
64  ret i32* null
65}
66
67define void @load_null_propagated() {
68; CHECK-LABEL: define {{[^@]+}}@load_null_propagated()
69; CHECK-NEXT:    unreachable
70;
71  %ptr = call i32* @ret_null()
72  %a = load i32, i32* %ptr
73  ret void
74}
75
76; -- Store tests --
77
78define void @store_wholly_unreachable() {
79; CHECK-LABEL: define {{[^@]+}}@store_wholly_unreachable()
80; CHECK-NEXT:    unreachable
81;
82  store i32 5, i32* null
83  ret void
84}
85
86define void @store_single_bb_unreachable(i1 %cond) {
87; CHECK-LABEL: define {{[^@]+}}@store_single_bb_unreachable
88; CHECK-SAME: (i1 [[COND:%.*]])
89; CHECK-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[E:%.*]]
90; CHECK:       t:
91; CHECK-NEXT:    unreachable
92; CHECK:       e:
93; CHECK-NEXT:    ret void
94;
95  br i1 %cond, label %t, label %e
96t:
97  store i32 5, i32* null
98  br label %e
99e:
100  ret void
101}
102
103define void @store_null_pointer_is_defined() "null-pointer-is-valid"="true" {
104; CHECK-LABEL: define {{[^@]+}}@store_null_pointer_is_defined()
105; CHECK-NEXT:    store i32 5, i32* null, align 536870912
106; CHECK-NEXT:    ret void
107;
108  store i32 5, i32* null
109  ret void
110}
111
112define void @store_null_propagated() {
113; ATTRIBUTOR-LABEL: @store_null_propagated(
114; ATTRIBUTOR-NEXT:    unreachable
115;
116; CHECK-LABEL: define {{[^@]+}}@store_null_propagated()
117; CHECK-NEXT:    unreachable
118;
119  %ptr = call i32* @ret_null()
120  store i32 5, i32* %ptr
121  ret void
122}
123
124; -- AtomicRMW tests --
125
126define void @atomicrmw_wholly_unreachable() {
127; CHECK-LABEL: define {{[^@]+}}@atomicrmw_wholly_unreachable()
128; CHECK-NEXT:    unreachable
129;
130  %a = atomicrmw add i32* null, i32 1 acquire
131  ret void
132}
133
134define void @atomicrmw_single_bb_unreachable(i1 %cond) {
135; CHECK-LABEL: define {{[^@]+}}@atomicrmw_single_bb_unreachable
136; CHECK-SAME: (i1 [[COND:%.*]])
137; CHECK-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[E:%.*]]
138; CHECK:       t:
139; CHECK-NEXT:    unreachable
140; CHECK:       e:
141; CHECK-NEXT:    ret void
142;
143  br i1 %cond, label %t, label %e
144t:
145  %a = atomicrmw add i32* null, i32 1 acquire
146  br label %e
147e:
148  ret void
149}
150
151define void @atomicrmw_null_pointer_is_defined() "null-pointer-is-valid"="true" {
152; CHECK-LABEL: define {{[^@]+}}@atomicrmw_null_pointer_is_defined()
153; CHECK-NEXT:    [[A:%.*]] = atomicrmw add i32* null, i32 1 acquire
154; CHECK-NEXT:    ret void
155;
156  %a = atomicrmw add i32* null, i32 1 acquire
157  ret void
158}
159
160define void @atomicrmw_null_propagated() {
161; ATTRIBUTOR-LABEL: @atomicrmw_null_propagated(
162; ATTRIBUTOR-NEXT:    unreachable
163;
164; CHECK-LABEL: define {{[^@]+}}@atomicrmw_null_propagated()
165; CHECK-NEXT:    unreachable
166;
167  %ptr = call i32* @ret_null()
168  %a = atomicrmw add i32* %ptr, i32 1 acquire
169  ret void
170}
171
172; -- AtomicCmpXchg tests --
173
174define void @atomiccmpxchg_wholly_unreachable() {
175; CHECK-LABEL: define {{[^@]+}}@atomiccmpxchg_wholly_unreachable()
176; CHECK-NEXT:    unreachable
177;
178  %a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic
179  ret void
180}
181
182define void @atomiccmpxchg_single_bb_unreachable(i1 %cond) {
183; CHECK-LABEL: define {{[^@]+}}@atomiccmpxchg_single_bb_unreachable
184; CHECK-SAME: (i1 [[COND:%.*]])
185; CHECK-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[E:%.*]]
186; CHECK:       t:
187; CHECK-NEXT:    unreachable
188; CHECK:       e:
189; CHECK-NEXT:    ret void
190;
191  br i1 %cond, label %t, label %e
192t:
193  %a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic
194  br label %e
195e:
196  ret void
197}
198
199define void @atomiccmpxchg_null_pointer_is_defined() "null-pointer-is-valid"="true" {
200; CHECK-LABEL: define {{[^@]+}}@atomiccmpxchg_null_pointer_is_defined()
201; CHECK-NEXT:    [[A:%.*]] = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic
202; CHECK-NEXT:    ret void
203;
204  %a = cmpxchg i32* null, i32 2, i32 3 acq_rel monotonic
205  ret void
206}
207
208define void @atomiccmpxchg_null_propagated() {
209; ATTRIBUTOR-LABEL: @atomiccmpxchg_null_propagated(
210; ATTRIBUTOR-NEXT:    unreachable
211;
212; CHECK-LABEL: define {{[^@]+}}@atomiccmpxchg_null_propagated()
213; CHECK-NEXT:    unreachable
214;
215  %ptr = call i32* @ret_null()
216  %a = cmpxchg i32* %ptr, i32 2, i32 3 acq_rel monotonic
217  ret void
218}
219
220; -- Conditional branching tests --
221
222; Note: The unreachable on %t and %e is _not_ from AAUndefinedBehavior
223
224define i32 @cond_br_on_undef() {
225; CHECK-LABEL: define {{[^@]+}}@cond_br_on_undef()
226; CHECK-NEXT:    unreachable
227; CHECK:       t:
228; CHECK-NEXT:    unreachable
229; CHECK:       e:
230; CHECK-NEXT:    unreachable
231;
232  br i1 undef, label %t, label %e
233t:
234  ret i32 1
235e:
236  ret i32 2
237}
238
239; More complicated branching
240  ; Valid branch - verify that this is not converted
241  ; to unreachable.
242define void @cond_br_on_undef2(i1 %cond) {
243; CHECK-LABEL: define {{[^@]+}}@cond_br_on_undef2
244; CHECK-SAME: (i1 [[COND:%.*]])
245; CHECK-NEXT:    br i1 [[COND]], label [[T1:%.*]], label [[E1:%.*]]
246; CHECK:       t1:
247; CHECK-NEXT:    unreachable
248; CHECK:       t2:
249; CHECK-NEXT:    unreachable
250; CHECK:       e2:
251; CHECK-NEXT:    unreachable
252; CHECK:       e1:
253; CHECK-NEXT:    ret void
254;
255  br i1 %cond, label %t1, label %e1
256t1:
257  br i1 undef, label %t2, label %e2
258t2:
259  ret void
260e2:
261  ret void
262e1:
263  ret void
264}
265
266define i1 @ret_undef() {
267; CHECK-LABEL: define {{[^@]+}}@ret_undef()
268; CHECK-NEXT:    ret i1 undef
269;
270  ret i1 undef
271}
272
273define void @cond_br_on_undef_interproc() {
274; CHECK-LABEL: define {{[^@]+}}@cond_br_on_undef_interproc()
275; CHECK-NEXT:    unreachable
276; CHECK:       t:
277; CHECK-NEXT:    unreachable
278; CHECK:       e:
279; CHECK-NEXT:    unreachable
280;
281  %cond = call i1 @ret_undef()
282  br i1 %cond, label %t, label %e
283t:
284  ret void
285e:
286  ret void
287}
288
289define i1 @ret_undef2() {
290; CHECK-LABEL: define {{[^@]+}}@ret_undef2()
291; CHECK-NEXT:    br i1 true, label [[T:%.*]], label [[E:%.*]]
292; CHECK:       t:
293; CHECK-NEXT:    ret i1 undef
294; CHECK:       e:
295; CHECK-NEXT:    unreachable
296;
297  br i1 true, label %t, label %e
298t:
299  ret i1 undef
300e:
301  ret i1 undef
302}
303
304; More complicated interproc deduction of undef
305define void @cond_br_on_undef_interproc2() {
306; CHECK-LABEL: define {{[^@]+}}@cond_br_on_undef_interproc2()
307; CHECK-NEXT:    unreachable
308; CHECK:       t:
309; CHECK-NEXT:    unreachable
310; CHECK:       e:
311; CHECK-NEXT:    unreachable
312;
313  %cond = call i1 @ret_undef2()
314  br i1 %cond, label %t, label %e
315t:
316  ret void
317e:
318  ret void
319}
320
321; Branch on undef that depends on propagation of
322; undef of a previous instruction.
323define i32 @cond_br_on_undef3() {
324; CHECK-LABEL: define {{[^@]+}}@cond_br_on_undef3()
325; CHECK-NEXT:    br label [[T:%.*]]
326; CHECK:       t:
327; CHECK-NEXT:    ret i32 1
328; CHECK:       e:
329; CHECK-NEXT:    unreachable
330;
331  %cond = icmp ne i32 1, undef
332  br i1 %cond, label %t, label %e
333t:
334  ret i32 1
335e:
336  ret i32 2
337}
338
339; Branch on undef because of uninitialized value.
340; FIXME: Currently it doesn't propagate the undef.
341define i32 @cond_br_on_undef_uninit() {
342; CHECK-LABEL: define {{[^@]+}}@cond_br_on_undef_uninit()
343; CHECK-NEXT:    [[ALLOC:%.*]] = alloca i1
344; CHECK-NEXT:    [[COND:%.*]] = load i1, i1* [[ALLOC]], align 1
345; CHECK-NEXT:    br i1 [[COND]], label [[T:%.*]], label [[E:%.*]]
346; CHECK:       t:
347; CHECK-NEXT:    ret i32 1
348; CHECK:       e:
349; CHECK-NEXT:    ret i32 2
350;
351  %alloc = alloca i1
352  %cond = load i1, i1* %alloc
353  br i1 %cond, label %t, label %e
354t:
355  ret i32 1
356e:
357  ret i32 2
358}
359
360; Note that the `load` has UB (so it will be changed to unreachable)
361; and the branch is a terminator that can be constant-folded.
362; We want to test that doing both won't cause a segfault.
363; MODULE-NOT: @callee(
364define internal i32 @callee(i1 %C, i32* %A) {
365;
366; IS__CGSCC____-LABEL: define {{[^@]+}}@callee()
367; IS__CGSCC____-NEXT:  entry:
368; IS__CGSCC____-NEXT:    unreachable
369; IS__CGSCC____:       T:
370; IS__CGSCC____-NEXT:    unreachable
371; IS__CGSCC____:       F:
372; IS__CGSCC____-NEXT:    ret i32 1
373;
374entry:
375  %A.0 = load i32, i32* null
376  br i1 %C, label %T, label %F
377
378T:
379  ret i32 %A.0
380
381F:
382  ret i32 1
383}
384
385define i32 @foo() {
386; CHECK-LABEL: define {{[^@]+}}@foo()
387; CHECK-NEXT:    ret i32 1
388;
389  %X = call i32 @callee(i1 false, i32* null)
390  ret i32 %X
391}
392