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