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
9 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
10 target triple = "x86_64-unknown-linux-gnu"
12 declare void @Write1(i8* %p)
13 declare void @Write4(i8* %p)
14 declare void @Write4_2(i8* %p, i8* %q)
15 declare void @Write8(i8* %p)
16 declare dso_local i8* @WriteAndReturn8(i8* %p)
17 declare dso_local void @ExternalCall(i8* %p)
18 declare void @PreemptableWrite1(i8* %p)
19 declare void @InterposableWrite1(i8* %p)
20 declare i8* @ReturnDependent(i8* %p)
21 declare void @Rec2(i8* %p)
22 declare void @RecursiveNoOffset(i32* %p, i32 %size, i32* %acc)
23 declare void @RecursiveWithOffset(i32 %size, i32* %acc)
25 ; Basic out-of-bounds.
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)){{$}}
34 %x = alloca i32, align 4
35 %x1 = bitcast i32* %x to i8*
36 call void @Write8(i8* %x1)
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)){{$}}
49 %x = alloca i32, align 4
50 %x1 = bitcast i32* %x to i8*
51 call void @Write1(i8* %x1)
55 ; Another basic in-bounds.
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)){{$}}
64 %x = alloca i32, align 4
65 %x1 = bitcast i32* %x to i8*
66 call void @Write4(i8* %x1)
70 ; In-bounds with offset.
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)){{$}}
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)
86 ; Out-of-bounds with offset.
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)){{$}}
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)
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)){{$}}
111 %x = alloca i32, align 4
112 %x1 = bitcast i32* %x to i8*
113 call void @ExternalCall(i8* %x1)
117 ; Call to dso_preemptable function
118 define 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)){{$}}
126 %x = alloca i32, align 4
127 %x1 = bitcast i32* %x to i8*
128 call void @PreemptableWrite1(i8* %x1)
132 ; Call to function with interposable linkage
133 define 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)){{$}}
141 %x = alloca i32, align 4
142 %x1 = bitcast i32* %x to i8*
143 call void @InterposableWrite1(i8* %x1)
147 ; Call to function with private linkage
148 define 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)){{$}}
156 %x = alloca i32, align 4
157 %x1 = bitcast i32* %x to i8*
158 call void @PrivateWrite1(i8* %x1)
162 define 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:
169 store i8 0, i8* %p, align 1
173 ; Caller returns a dependent value.
174 ; FIXME: alloca considered unsafe even if the return value is unused.
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)){{$}}
183 %x = alloca i32, align 4
184 %x1 = bitcast i32* %x to i8*
185 %x2 = call i8* @ReturnDependent(i8* %x1)
189 define 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)){{$}}
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)
205 define 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)){{$}}
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)
221 define 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)){{$}}
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)
237 define 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)){{$}}
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)
253 define 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)){{$}}
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)
268 define 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)){{$}}
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)
283 define 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)){{$}}
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)
299 define 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)){{$}}
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)
315 define 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)){{$}}
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
333 define 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)){{$}}
342 %sum = alloca i32, i64 16, align 4
343 call void @RecursiveWithOffset(i32 %size, i32* %sum)
347 ; FIXME: IPA should detect that access is safe
348 define 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)){{$}}
356 %x = alloca i8, i64 16, align 4
357 %0 = call i8* @WriteAndReturn8(i8* %x)
361 ; The rest is from Inputs/ipa.ll
363 ; CHECK-LABEL: @Write1{{$}}
364 ; CHECK-NEXT: args uses:
365 ; CHECK-NEXT: p[]: [0,1){{$}}
366 ; CHECK-NEXT: allocas uses:
369 ; CHECK-LABEL: @Write4{{$}}
370 ; CHECK-NEXT: args uses:
371 ; CHECK-NEXT: p[]: [0,4){{$}}
372 ; CHECK-NEXT: allocas uses:
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:
382 ; CHECK-LABEL: @Write8{{$}}
383 ; CHECK-NEXT: args uses:
384 ; CHECK-NEXT: p[]: [0,8){{$}}
385 ; CHECK-NEXT: allocas uses:
388 ; CHECK-LABEL: @WriteAndReturn8{{$}}
389 ; CHECK-NEXT: args uses:
390 ; CHECK-NEXT: p[]: full-set{{$}}
391 ; CHECK-NEXT: allocas uses:
394 ; CHECK-LABEL: @PreemptableWrite1 dso_preemptable{{$}}
395 ; CHECK-NEXT: args uses:
396 ; CHECK-NEXT: p[]: [0,1){{$}}
397 ; CHECK-NEXT: allocas uses:
400 ; CHECK-LABEL: @InterposableWrite1 interposable{{$}}
401 ; CHECK-NEXT: args uses:
402 ; CHECK-NEXT: p[]: [0,1){{$}}
403 ; CHECK-NEXT: allocas uses:
406 ; CHECK-LABEL: @ReturnDependent{{$}}
407 ; CHECK-NEXT: args uses:
408 ; CHECK-NEXT: p[]: full-set{{$}}
409 ; CHECK-NEXT: allocas uses:
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:
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:
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:
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:
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: