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