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