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