1; REQUIRES: aarch64-registered-target
2
3; RUN: llvm-as %s -o %t0.bc
4; RUN: llvm-as %S/Inputs/ipa.ll -o %t1.bc
5; RUN: llvm-link -disable-lazy-loading %t0.bc %t1.bc -o %t.combined.bc
6
7; RUN: opt -S -analyze -stack-safety-local %t.combined.bc | FileCheck %s --check-prefixes=CHECK,LOCAL
8; RUN: opt -S -passes="print<stack-safety-local>" -disable-output %t.combined.bc 2>&1 | FileCheck %s --check-prefixes=CHECK,LOCAL
9
10; RUN: opt -S -analyze -stack-safety %t.combined.bc | FileCheck %s --check-prefixes=CHECK,GLOBAL,NOLTO
11; RUN: opt -S -passes="print-stack-safety" -disable-output %t.combined.bc 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL,NOLTO
12
13target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
14target triple = "aarch64-unknown-linux"
15
16attributes #0 = { noinline sanitize_memtag "target-features"="+mte,+neon" }
17
18declare void @Write1(i8* %p)
19declare void @Write4(i8* %p)
20declare void @Write4_2(i8* %p, i8* %q)
21declare void @Write8(i8* %p)
22declare dso_local i8* @WriteAndReturn8(i8* %p)
23declare dso_local void @ExternalCall(i8* %p)
24declare void @PreemptableWrite1(i8* %p)
25declare void @InterposableWrite1(i8* %p)
26declare i8* @ReturnDependent(i8* %p)
27declare void @Rec2(i8* %p)
28declare void @RecursiveNoOffset(i32* %p, i32 %size, i32* %acc)
29declare void @RecursiveWithOffset(i32 %size, i32* %acc)
30
31; Basic out-of-bounds.
32define void @f1() #0 {
33; CHECK-LABEL: @f1 dso_preemptable{{$}}
34; CHECK-NEXT: args uses:
35; CHECK-NEXT: allocas uses:
36; LOCAL-NEXT: x[4]: empty-set, @Write8(arg0, [0,1)){{$}}
37; GLOBAL-NEXT: x[4]: [0,8), @Write8(arg0, [0,1)){{$}}
38; CHECK-NOT: ]:
39entry:
40  %x = alloca i32, align 4
41  %x1 = bitcast i32* %x to i8*
42  call void @Write8(i8* %x1)
43  ret void
44}
45
46; Basic in-bounds.
47define void @f2() #0 {
48; CHECK-LABEL: @f2 dso_preemptable{{$}}
49; CHECK-NEXT: args uses:
50; CHECK-NEXT: allocas uses:
51; LOCAL-NEXT: x[4]: empty-set, @Write1(arg0, [0,1)){{$}}
52; GLOBAL-NEXT: x[4]: [0,1), @Write1(arg0, [0,1)){{$}}
53; CHECK-NOT: ]:
54entry:
55  %x = alloca i32, align 4
56  %x1 = bitcast i32* %x to i8*
57  call void @Write1(i8* %x1)
58  ret void
59}
60
61; Another basic in-bounds.
62define void @f3() #0 {
63; CHECK-LABEL: @f3 dso_preemptable{{$}}
64; CHECK-NEXT: args uses:
65; CHECK-NEXT: allocas uses:
66; LOCAL-NEXT: x[4]: empty-set, @Write4(arg0, [0,1)){{$}}
67; GLOBAL-NEXT: x[4]: [0,4), @Write4(arg0, [0,1)){{$}}
68; CHECK-NOT: ]:
69entry:
70  %x = alloca i32, align 4
71  %x1 = bitcast i32* %x to i8*
72  call void @Write4(i8* %x1)
73  ret void
74}
75
76; In-bounds with offset.
77define void @f4() #0 {
78; CHECK-LABEL: @f4 dso_preemptable{{$}}
79; CHECK-NEXT: args uses:
80; CHECK-NEXT: allocas uses:
81; LOCAL-NEXT: x[4]: empty-set, @Write1(arg0, [1,2)){{$}}
82; GLOBAL-NEXT: x[4]: [1,2), @Write1(arg0, [1,2)){{$}}
83; CHECK-NOT: ]:
84entry:
85  %x = alloca i32, align 4
86  %x1 = bitcast i32* %x to i8*
87  %x2 = getelementptr i8, i8* %x1, i64 1
88  call void @Write1(i8* %x2)
89  ret void
90}
91
92; Out-of-bounds with offset.
93define void @f5() #0 {
94; CHECK-LABEL: @f5 dso_preemptable{{$}}
95; CHECK-NEXT: args uses:
96; CHECK-NEXT: allocas uses:
97; LOCAL-NEXT: empty-set, @Write4(arg0, [1,2)){{$}}
98; GLOBAL-NEXT: [1,5), @Write4(arg0, [1,2)){{$}}
99; CHECK-NOT: ]:
100entry:
101  %x = alloca i32, align 4
102  %x1 = bitcast i32* %x to i8*
103  %x2 = getelementptr i8, i8* %x1, i64 1
104  call void @Write4(i8* %x2)
105  ret void
106}
107
108; External call.
109define void @f6() #0 {
110; CHECK-LABEL: @f6 dso_preemptable{{$}}
111; CHECK-NEXT: args uses:
112; CHECK-NEXT: allocas uses:
113; LOCAL-NEXT: x[4]: empty-set, @ExternalCall(arg0, [0,1)){{$}}
114; GLOBAL-NEXT: x[4]: full-set, @ExternalCall(arg0, [0,1)){{$}}
115; CHECK-NOT: ]:
116entry:
117  %x = alloca i32, align 4
118  %x1 = bitcast i32* %x to i8*
119  call void @ExternalCall(i8* %x1)
120  ret void
121}
122
123; Call to dso_preemptable function
124define void @PreemptableCall() #0 {
125; CHECK-LABEL: @PreemptableCall dso_preemptable{{$}}
126; CHECK-NEXT: args uses:
127; CHECK-NEXT: allocas uses:
128; LOCAL-NEXT: x[4]: empty-set, @PreemptableWrite1(arg0, [0,1)){{$}}
129; GLOBAL-NEXT: x[4]: full-set, @PreemptableWrite1(arg0, [0,1)){{$}}
130; CHECK-NOT: ]:
131entry:
132  %x = alloca i32, align 4
133  %x1 = bitcast i32* %x to i8*
134  call void @PreemptableWrite1(i8* %x1)
135  ret void
136}
137
138; Call to function with interposable linkage
139define void @InterposableCall() #0 {
140; CHECK-LABEL: @InterposableCall dso_preemptable{{$}}
141; CHECK-NEXT: args uses:
142; CHECK-NEXT: allocas uses:
143; LOCAL-NEXT: x[4]: empty-set, @InterposableWrite1(arg0, [0,1)){{$}}
144; NOLTO-NEXT: x[4]: full-set, @InterposableWrite1(arg0, [0,1)){{$}}
145; CHECK-NOT: ]:
146entry:
147  %x = alloca i32, align 4
148  %x1 = bitcast i32* %x to i8*
149  call void @InterposableWrite1(i8* %x1)
150  ret void
151}
152
153; Call to function with private linkage
154define void @PrivateCall() #0 {
155; CHECK-LABEL: @PrivateCall dso_preemptable{{$}}
156; CHECK-NEXT: args uses:
157; CHECK-NEXT: allocas uses:
158; LOCAL-NEXT: x[4]: empty-set, @PrivateWrite1(arg0, [0,1)){{$}}
159; GLOBAL-NEXT: x[4]: [0,1), @PrivateWrite1(arg0, [0,1)){{$}}
160; CHECK-NOT: ]:
161entry:
162  %x = alloca i32, align 4
163  %x1 = bitcast i32* %x to i8*
164  call void @PrivateWrite1(i8* %x1)
165  ret void
166}
167
168define private void @PrivateWrite1(i8* %p) #0 {
169; CHECK-LABEL: @PrivateWrite1{{$}}
170; CHECK-NEXT: args uses:
171; CHECK-NEXT: p[]: [0,1){{$}}
172; CHECK-NEXT: allocas uses:
173; CHECK-NOT: ]:
174entry:
175  store i8 0, i8* %p, align 1
176  ret void
177}
178
179; Caller returns a dependent value.
180; FIXME: alloca considered unsafe even if the return value is unused.
181define void @f7() #0 {
182; CHECK-LABEL: @f7 dso_preemptable{{$}}
183; CHECK-NEXT: args uses:
184; CHECK-NEXT: allocas uses:
185; LOCAL-NEXT: x[4]: empty-set, @ReturnDependent(arg0, [0,1)){{$}}
186; GLOBAL-NEXT: x[4]: full-set, @ReturnDependent(arg0, [0,1)){{$}}
187; CHECK-NOT: ]:
188entry:
189  %x = alloca i32, align 4
190  %x1 = bitcast i32* %x to i8*
191  %x2 = call i8* @ReturnDependent(i8* %x1)
192  ret void
193}
194
195define void @f8left() #0 {
196; CHECK-LABEL: @f8left dso_preemptable{{$}}
197; CHECK-NEXT: args uses:
198; CHECK-NEXT: allocas uses:
199; LOCAL-NEXT: x[8]: empty-set, @Rec2(arg0, [2,3)){{$}}
200; GLOBAL-NEXT: x[8]: [0,4), @Rec2(arg0, [2,3)){{$}}
201; CHECK-NOT: ]:
202entry:
203  %x = alloca i64, align 4
204  %x1 = bitcast i64* %x to i8*
205  %x2 = getelementptr i8, i8* %x1, i64 2
206; 2 + [-2, 2) = [0, 4) => OK
207  call void @Rec2(i8* %x2)
208  ret void
209}
210
211define void @f8right() #0 {
212; CHECK-LABEL: @f8right dso_preemptable{{$}}
213; CHECK-NEXT: args uses:
214; CHECK-NEXT: allocas uses:
215; LOCAL-NEXT: x[8]: empty-set, @Rec2(arg0, [6,7)){{$}}
216; GLOBAL-NEXT: x[8]: [4,8), @Rec2(arg0, [6,7)){{$}}
217; CHECK-NOT: ]:
218entry:
219  %x = alloca i64, align 4
220  %x1 = bitcast i64* %x to i8*
221  %x2 = getelementptr i8, i8* %x1, i64 6
222; 6 + [-2, 2) = [4, 8) => OK
223  call void @Rec2(i8* %x2)
224  ret void
225}
226
227define void @f8oobleft() #0 {
228; CHECK-LABEL: @f8oobleft dso_preemptable{{$}}
229; CHECK-NEXT: args uses:
230; CHECK-NEXT: allocas uses:
231; LOCAL-NEXT: x[8]: empty-set, @Rec2(arg0, [1,2)){{$}}
232; GLOBAL-NEXT: x[8]: [-1,3), @Rec2(arg0, [1,2)){{$}}
233; CHECK-NOT: ]:
234entry:
235  %x = alloca i64, align 4
236  %x1 = bitcast i64* %x to i8*
237  %x2 = getelementptr i8, i8* %x1, i64 1
238; 1 + [-2, 2) = [-1, 3) => NOT OK
239  call void @Rec2(i8* %x2)
240  ret void
241}
242
243define void @f8oobright() #0 {
244; CHECK-LABEL: @f8oobright dso_preemptable{{$}}
245; CHECK-NEXT: args uses:
246; CHECK-NEXT: allocas uses:
247; LOCAL-NEXT: x[8]: empty-set, @Rec2(arg0, [7,8)){{$}}
248; GLOBAL-NEXT: x[8]: [5,9), @Rec2(arg0, [7,8)){{$}}
249; CHECK-NOT: ]:
250entry:
251  %x = alloca i64, align 4
252  %x1 = bitcast i64* %x to i8*
253  %x2 = getelementptr i8, i8* %x1, i64 7
254; 7 + [-2, 2) = [5, 9) => NOT OK
255  call void @Rec2(i8* %x2)
256  ret void
257}
258
259define void @TwoArguments() #0 {
260; CHECK-LABEL: @TwoArguments dso_preemptable{{$}}
261; CHECK-NEXT: args uses:
262; CHECK-NEXT: allocas uses:
263; LOCAL-NEXT: x[8]: empty-set, @Write4_2(arg1, [0,1)), @Write4_2(arg0, [4,5)){{$}}
264; GLOBAL-NEXT: x[8]: [0,8), @Write4_2(arg1, [0,1)), @Write4_2(arg0, [4,5)){{$}}
265; CHECK-NOT: ]:
266entry:
267  %x = alloca i64, align 4
268  %x1 = bitcast i64* %x to i8*
269  %x2 = getelementptr i8, i8* %x1, i64 4
270  call void @Write4_2(i8* %x2, i8* %x1)
271  ret void
272}
273
274define void @TwoArgumentsOOBOne() {
275; CHECK-LABEL: @TwoArgumentsOOBOne dso_preemptable{{$}}
276; CHECK-NEXT: args uses:
277; CHECK-NEXT: allocas uses:
278; LOCAL-NEXT: x[8]: empty-set, @Write4_2(arg1, [0,1)), @Write4_2(arg0, [5,6)){{$}}
279; GLOBAL-NEXT: x[8]: [0,9), @Write4_2(arg1, [0,1)), @Write4_2(arg0, [5,6)){{$}}
280; CHECK-NOT: ]:
281entry:
282  %x = alloca i64, align 4
283  %x1 = bitcast i64* %x to i8*
284  %x2 = getelementptr i8, i8* %x1, i64 5
285  call void @Write4_2(i8* %x2, i8* %x1)
286  ret void
287}
288
289define void @TwoArgumentsOOBOther() #0 {
290; CHECK-LABEL: @TwoArgumentsOOBOther dso_preemptable{{$}}
291; CHECK-NEXT: args uses:
292; CHECK-NEXT: allocas uses:
293; LOCAL-NEXT: x[8]: empty-set, @Write4_2(arg1, [-1,0)), @Write4_2(arg0, [4,5)){{$}}
294; GLOBAL-NEXT: x[8]: [-1,8), @Write4_2(arg1, [-1,0)), @Write4_2(arg0, [4,5)){{$}}
295; CHECK-NOT: ]:
296entry:
297  %x = alloca i64, align 4
298  %x0 = bitcast i64* %x to i8*
299  %x1 = getelementptr i8, i8* %x0, i64 -1
300  %x2 = getelementptr i8, i8* %x0, i64 4
301  call void @Write4_2(i8* %x2, i8* %x1)
302  ret void
303}
304
305define void @TwoArgumentsOOBBoth() #0 {
306; CHECK-LABEL: @TwoArgumentsOOBBoth dso_preemptable{{$}}
307; CHECK-NEXT: args uses:
308; CHECK-NEXT: allocas uses:
309; LOCAL-NEXT: x[8]: empty-set, @Write4_2(arg1, [-1,0)), @Write4_2(arg0, [5,6)){{$}}
310; GLOBAL-NEXT: x[8]: [-1,9), @Write4_2(arg1, [-1,0)), @Write4_2(arg0, [5,6)){{$}}
311; CHECK-NOT: ]:
312entry:
313  %x = alloca i64, align 4
314  %x0 = bitcast i64* %x to i8*
315  %x1 = getelementptr i8, i8* %x0, i64 -1
316  %x2 = getelementptr i8, i8* %x0, i64 5
317  call void @Write4_2(i8* %x2, i8* %x1)
318  ret void
319}
320
321define i32 @TestRecursiveNoOffset(i32* %p, i32 %size) {
322; CHECK-LABEL: @TestRecursiveNoOffset dso_preemptable{{$}}
323; CHECK-NEXT: args uses:
324; LOCAL-NEXT: p[]: empty-set, @RecursiveNoOffset(arg0, [0,1)){{$}}
325; GLOBAL-NEXT: p[]: full-set, @RecursiveNoOffset(arg0, [0,1)){{$}}
326; CHECK-NEXT: allocas uses:
327; CHECK-NEXT: sum[4]: [0,4), @RecursiveNoOffset(arg2, [0,1)){{$}}
328; CHECK-NOT: ]:
329entry:
330  %sum = alloca i32, align 4
331  %0 = bitcast i32* %sum to i8*
332  store i32 0, i32* %sum, align 4
333  call void @RecursiveNoOffset(i32* %p, i32 %size, i32* %sum)
334  %1 = load i32, i32* %sum, align 4
335  ret i32 %1
336}
337
338define void @TestRecursiveWithOffset(i32 %size) #0 {
339; CHECK-LABEL: @TestRecursiveWithOffset dso_preemptable{{$}}
340; CHECK-NEXT: args uses:
341; CHECK-NEXT: allocas uses:
342; LOCAL-NEXT: sum[64]: empty-set, @RecursiveWithOffset(arg1, [0,1)){{$}}
343; GLOBAL-NEXT: sum[64]: full-set, @RecursiveWithOffset(arg1, [0,1)){{$}}
344; CHECK-NOT: ]:
345entry:
346  %sum = alloca i32, i64 16, align 4
347  call void @RecursiveWithOffset(i32 %size, i32* %sum)
348  ret void
349}
350
351; FIXME: IPA should detect that access is safe
352define void @TestUpdateArg() #0 {
353; CHECK-LABEL: @TestUpdateArg dso_preemptable{{$}}
354; CHECK-NEXT: args uses:
355; CHECK-NEXT: allocas uses:
356; LOCAL-NEXT: x[16]: empty-set, @WriteAndReturn8(arg0, [0,1)){{$}}
357; GLOBAL-NEXT: x[16]: full-set, @WriteAndReturn8(arg0, [0,1)){{$}}
358; CHECK-NOT: ]:
359entry:
360  %x = alloca i8, i64 16, align 4
361  %0 = call i8* @WriteAndReturn8(i8* %x)
362  ret void
363}
364
365; The rest is from Inputs/ipa.ll
366
367; CHECK-LABEL: @Write1{{$}}
368; CHECK-NEXT: args uses:
369; CHECK-NEXT: p[]: [0,1){{$}}
370; CHECK-NEXT: allocas uses:
371; CHECK-NOT: ]:
372
373; CHECK-LABEL: @Write4{{$}}
374; CHECK-NEXT: args uses:
375; CHECK-NEXT: p[]: [0,4){{$}}
376; CHECK-NEXT: allocas uses:
377; CHECK-NOT: ]:
378
379; CHECK-LABEL: @Write4_2{{$}}
380; CHECK-NEXT: args uses:
381; CHECK-NEXT: p[]: [0,4){{$}}
382; CHECK-NEXT: q[]: [0,4){{$}}
383; CHECK-NEXT: allocas uses:
384; CHECK-NOT: ]:
385
386; CHECK-LABEL: @Write8{{$}}
387; CHECK-NEXT: args uses:
388; CHECK-NEXT: p[]: [0,8){{$}}
389; CHECK-NEXT: allocas uses:
390; CHECK-NOT: ]:
391
392; CHECK-LABEL: @WriteAndReturn8{{$}}
393; CHECK-NEXT: args uses:
394; CHECK-NEXT: p[]: full-set{{$}}
395; CHECK-NEXT: allocas uses:
396; CHECK-NOT: ]:
397
398; CHECK-LABEL: @PreemptableWrite1 dso_preemptable{{$}}
399; CHECK-NEXT: args uses:
400; CHECK-NEXT: p[]: [0,1){{$}}
401; CHECK-NEXT: allocas uses:
402; CHECK-NOT: ]:
403
404; CHECK-LABEL: @InterposableWrite1 interposable{{$}}
405; CHECK-NEXT: args uses:
406; CHECK-NEXT: p[]: [0,1){{$}}
407; CHECK-NEXT: allocas uses:
408; CHECK-NOT: ]:
409
410; CHECK-LABEL: @ReturnDependent{{$}}
411; CHECK-NEXT: args uses:
412; CHECK-NEXT: p[]: full-set{{$}}
413; CHECK-NEXT: allocas uses:
414; CHECK-NOT: ]:
415
416; CHECK-LABEL: @Rec0{{$}}
417; CHECK-NEXT: args uses:
418; LOCAL-NEXT: p[]: empty-set, @Write4(arg0, [2,3)){{$}}
419; GLOBAL-NEXT: p[]: [2,6), @Write4(arg0, [2,3)){{$}}
420; CHECK-NEXT: allocas uses:
421; CHECK-NOT: ]:
422
423; CHECK-LABEL: @Rec1{{$}}
424; CHECK-NEXT: args uses:
425; LOCAL-NEXT: p[]: empty-set, @Rec0(arg0, [1,2)){{$}}
426; GLOBAL-NEXT: p[]: [3,7), @Rec0(arg0, [1,2)){{$}}
427; CHECK-NEXT: allocas uses:
428; CHECK-NOT: ]:
429
430; CHECK-LABEL: @Rec2{{$}}
431; CHECK-NEXT: args uses:
432; LOCAL-NEXT: p[]: empty-set, @Rec1(arg0, [-5,-4)){{$}}
433; GLOBAL-NEXT: p[]: [-2,2), @Rec1(arg0, [-5,-4)){{$}}
434; CHECK-NEXT: allocas uses:
435; CHECK-NOT: ]:
436
437; CHECK-LABEL: @RecursiveNoOffset{{$}}
438; CHECK-NEXT: args uses:
439; LOCAL-NEXT: p[]: [0,4), @RecursiveNoOffset(arg0, [4,5)){{$}}
440; GLOBAL-NEXT: p[]: full-set, @RecursiveNoOffset(arg0, [4,5)){{$}}
441; CHECK-NEXT: acc[]: [0,4), @RecursiveNoOffset(arg2, [0,1)){{$}}
442; CHECK-NEXT: allocas uses:
443; CHECK-NOT: ]:
444
445; CHECK-LABEL: @RecursiveWithOffset{{$}}
446; CHECK-NEXT: args uses:
447; LOCAL-NEXT: acc[]: [0,4), @RecursiveWithOffset(arg1, [4,5)){{$}}
448; GLOBAL-NEXT: acc[]: full-set, @RecursiveWithOffset(arg1, [4,5)){{$}}
449; CHECK-NEXT: allocas uses:
450; CHECK-NOT: ]:
451
452; CHECK-LABEL: @ReturnAlloca
453; CHECK-NEXT: args uses:
454; CHECK-NEXT: allocas uses:
455; CHECK-NEXT: x[8]: full-set
456; CHECK-NOT: ]:
457