|  | ; RUN: opt -S -passes="print<stack-safety-local>" -disable-output < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,LOCAL | 
|  | ; RUN: opt -S -passes="print-stack-safety" -disable-output < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL | 
|  |  | 
|  | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | 
|  | target triple = "x86_64-unknown-linux-gnu" | 
|  |  | 
|  | @sink = global ptr null, align 8 | 
|  |  | 
|  | declare void @llvm.memset.p0.i32(ptr %dest, i8 %val, i32 %len, i1 %isvolatile) | 
|  | declare void @llvm.memcpy.p0.p0.i32(ptr %dest, ptr %src, i32 %len, i1 %isvolatile) | 
|  | declare void @llvm.memmove.p0.p0.i32(ptr %dest, ptr %src, i32 %len, i1 %isvolatile) | 
|  | declare void @llvm.memset.p0.i64(ptr %dest, i8 %val, i64 %len, i1 %isvolatile) | 
|  |  | 
|  | declare void @unknown_call(ptr %dest) | 
|  | declare void @unknown_call_int(i64 %i) | 
|  | declare ptr @retptr(ptr returned) | 
|  |  | 
|  | ; Address leaked. | 
|  | define void @LeakAddress() { | 
|  | ; CHECK-LABEL: @LeakAddress dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | store ptr %x, ptr @sink, align 8 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreInBounds() { | 
|  | ; CHECK-LABEL: @StoreInBounds dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [0,1){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 0, ptr %x, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | store i8 0, ptr %x, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreInBoundsCond(i64 %i) { | 
|  | ; CHECK-LABEL: @StoreInBoundsCond dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 0, ptr %x2, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %c1 = icmp sge i64 %i, 0 | 
|  | %c2 = icmp slt i64 %i, 4 | 
|  | br i1 %c1, label %c1.true, label %false | 
|  |  | 
|  | c1.true: | 
|  | br i1 %c2, label %c2.true, label %false | 
|  |  | 
|  | c2.true: | 
|  | %x2 = getelementptr i8, ptr %x, i64 %i | 
|  | store i8 0, ptr %x2, align 1 | 
|  | br label %false | 
|  |  | 
|  | false: | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreInBoundsMinMax(i64 %i) { | 
|  | ; CHECK-LABEL: @StoreInBoundsMinMax dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 0, ptr %x2, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %c1 = icmp sge i64 %i, 0 | 
|  | %i1 = select i1 %c1, i64 %i, i64 0 | 
|  | %c2 = icmp slt i64 %i1, 3 | 
|  | %i2 = select i1 %c2, i64 %i1, i64 3 | 
|  | %x2 = getelementptr i8, ptr %x, i64 %i2 | 
|  | store i8 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreInBounds2() { | 
|  | ; CHECK-LABEL: @StoreInBounds2 dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i32 0, ptr %x, align 4 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | store i32 0, ptr %x, align 4 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreInBounds3() { | 
|  | ; CHECK-LABEL: @StoreInBounds3 dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [2,3){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 0, ptr %x2, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 2 | 
|  | store i8 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; FIXME: ScalarEvolution does not look through ptrtoint/inttoptr. | 
|  | define void @StoreInBounds4() { | 
|  | ; CHECK-LABEL: @StoreInBounds4 dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %x1 = ptrtoint ptr %x to i64 | 
|  | %x2 = add i64 %x1, 2 | 
|  | %x3 = inttoptr i64 %x2 to ptr | 
|  | store i8 0, ptr %x3, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreInBounds6() { | 
|  | ; CHECK-LABEL: @StoreInBounds6 dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: x[4]: full-set, @retptr(arg0, [0,1)){{$}} | 
|  | ; LOCAL-NEXT: x[4]: [0,1), @retptr(arg0, [0,1)){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 0, ptr %x2, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %x2 = call ptr @retptr(ptr %x) | 
|  | store i8 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define dso_local void @WriteMinMax(ptr %p) { | 
|  | ; CHECK-LABEL: @WriteMinMax{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: p[]: full-set | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 0, ptr %p1, align 1 | 
|  | ; GLOBAL-NEXT: store i8 0, ptr %p2, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %p1 = getelementptr i8, ptr %p, i64 9223372036854775805 | 
|  | store i8 0, ptr %p1, align 1 | 
|  | %p2 = getelementptr i8, ptr %p, i64 -9223372036854775805 | 
|  | store i8 0, ptr %p2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define dso_local void @WriteMax(ptr %p) { | 
|  | ; CHECK-LABEL: @WriteMax{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: p[]: [-9223372036854775807,9223372036854775806) | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memset.p0.i64(ptr %p, i8 1, i64 9223372036854775806, i1 false) | 
|  | ; GLOBAL-NEXT: call void @llvm.memset.p0.i64(ptr %p2, i8 1, i64 9223372036854775806, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | call void @llvm.memset.p0.i64(ptr %p, i8 1, i64 9223372036854775806, i1 0) | 
|  | %p2 = getelementptr i8, ptr %p, i64 -9223372036854775807 | 
|  | call void @llvm.memset.p0.i64(ptr %p2, i8 1, i64 9223372036854775806, i1 0) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreOutOfBounds() { | 
|  | ; CHECK-LABEL: @StoreOutOfBounds dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [2,6){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 2 | 
|  | store i32 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreOutOfBoundsCond(i64 %i) { | 
|  | ; CHECK-LABEL: @StoreOutOfBoundsCond dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %c1 = icmp sge i64 %i, 0 | 
|  | %c2 = icmp slt i64 %i, 5 | 
|  | br i1 %c1, label %c1.true, label %false | 
|  |  | 
|  | c1.true: | 
|  | br i1 %c2, label %c2.true, label %false | 
|  |  | 
|  | c2.true: | 
|  | %x2 = getelementptr i8, ptr %x, i64 %i | 
|  | store i8 0, ptr %x2, align 1 | 
|  | br label %false | 
|  |  | 
|  | false: | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreOutOfBoundsCond2(i64 %i) { | 
|  | ; CHECK-LABEL: @StoreOutOfBoundsCond2 dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %c2 = icmp slt i64 %i, 5 | 
|  | br i1 %c2, label %c2.true, label %false | 
|  |  | 
|  | c2.true: | 
|  | %x2 = getelementptr i8, ptr %x, i64 %i | 
|  | store i8 0, ptr %x2, align 1 | 
|  | br label %false | 
|  |  | 
|  | false: | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreOutOfBounds2() { | 
|  | ; CHECK-LABEL: @StoreOutOfBounds2 dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: x[4]: full-set, @retptr(arg0, [2,3)){{$}} | 
|  | ; LOCAL-NEXT: x[4]: [2,6), @retptr(arg0, [2,3)){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 2 | 
|  | %x3 = call ptr @retptr(ptr %x2) | 
|  | store i32 0, ptr %x3, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; There is no difference in load vs store handling. | 
|  | define void @LoadInBounds() { | 
|  | ; CHECK-LABEL: @LoadInBounds dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [0,1){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: %v = load i8, ptr %x, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %v = load i8, ptr %x, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @LoadOutOfBounds() { | 
|  | ; CHECK-LABEL: @LoadOutOfBounds dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [2,6){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 2 | 
|  | %v = load i32, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Leak through ret. | 
|  | define ptr @Ret() { | 
|  | ; CHECK-LABEL: @Ret dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 2 | 
|  | ret ptr %x2 | 
|  | } | 
|  |  | 
|  | declare void @Foo(ptr %p) | 
|  |  | 
|  | define void @DirectCall() { | 
|  | ; CHECK-LABEL: @DirectCall dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; LOCAL-NEXT: x[8]: empty-set, @Foo(arg0, [2,3)){{$}} | 
|  | ; GLOBAL-NEXT: x[8]: full-set, @Foo(arg0, [2,3)){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i64, align 4 | 
|  | %x2 = getelementptr i16, ptr %x, i64 1 | 
|  | call void @Foo(ptr %x2); | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Indirect calls can not be analyzed (yet). | 
|  | ; FIXME: %p[]: full-set looks invalid | 
|  | define void @IndirectCall(ptr %p) { | 
|  | ; CHECK-LABEL: @IndirectCall dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: p[]: full-set{{$}} | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | call void %p(ptr %x); | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @NonConstantOffset(i1 zeroext %z) { | 
|  | ; CHECK-LABEL: @NonConstantOffset dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; FIXME: SCEV can't look through selects. | 
|  | ; CHECK-NEXT: x[4]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 0, ptr %x2, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %idx = select i1 %z, i64 1, i64 2 | 
|  | %x2 = getelementptr i8, ptr %x, i64 %idx | 
|  | store i8 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @NegativeOffset() { | 
|  | ; CHECK-LABEL: @NegativeOffset dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[40]: [-1600000000000,-1599999999996){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, i32 10, align 4 | 
|  | %x2 = getelementptr i32, ptr %x, i64 -400000000000 | 
|  | store i32 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @PossiblyNegativeOffset(i16 %z) { | 
|  | ; CHECK-LABEL: @PossiblyNegativeOffset dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[40]: [-131072,131072){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, i32 10, align 4 | 
|  | %x2 = getelementptr i32, ptr %x, i16 %z | 
|  | store i32 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @NonConstantOffsetOOB(i1 zeroext %z) { | 
|  | ; CHECK-LABEL: @NonConstantOffsetOOB dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [0,6){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | %idx = select i1 %z, i64 1, i64 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 %idx | 
|  | store i8 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @ArrayAlloca() { | 
|  | ; CHECK-LABEL: @ArrayAlloca dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[40]: [36,40){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i32 0, ptr %x2, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, i32 10, align 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 36 | 
|  | store i32 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @ArrayAllocaOOB() { | 
|  | ; CHECK-LABEL: @ArrayAllocaOOB dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[40]: [37,41){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, i32 10, align 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 37 | 
|  | store i32 0, ptr %x2, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @DynamicAllocaUnused(i64 %size) { | 
|  | ; CHECK-LABEL: @DynamicAllocaUnused dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[0]: empty-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, i64 %size, align 16 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Dynamic alloca with unknown size. | 
|  | define void @DynamicAlloca(i64 %size) { | 
|  | ; CHECK-LABEL: @DynamicAlloca dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[0]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, i64 %size, align 16 | 
|  | store i32 0, ptr %x, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Dynamic alloca with limited size. | 
|  | ; FIXME: could be proved safe. Implement. | 
|  | define void @DynamicAllocaFiniteSizeRange(i1 zeroext %z) { | 
|  | ; CHECK-LABEL: @DynamicAllocaFiniteSizeRange dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[0]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %size = select i1 %z, i64 3, i64 5 | 
|  | %x = alloca i32, i64 %size, align 16 | 
|  | store i32 0, ptr %x, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define signext i8 @SimpleLoop() { | 
|  | ; CHECK-LABEL: @SimpleLoop dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[10]: [0,10){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: %load = load volatile i8, ptr %p.09, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca [10 x i8], align 1 | 
|  | %lftr.limit = getelementptr inbounds [10 x i8], ptr %x, i64 0, i64 10 | 
|  | br label %for.body | 
|  |  | 
|  | for.body: | 
|  | %sum.010 = phi i8 [ 0, %entry ], [ %add, %for.body ] | 
|  | %p.09 = phi ptr [ %x, %entry ], [ %incdec.ptr, %for.body ] | 
|  | %incdec.ptr = getelementptr inbounds i8, ptr %p.09, i64 1 | 
|  | %load = load volatile i8, ptr %p.09, align 1 | 
|  | %add = add i8 %load, %sum.010 | 
|  | %exitcond = icmp eq ptr %incdec.ptr, %lftr.limit | 
|  | br i1 %exitcond, label %for.cond.cleanup, label %for.body | 
|  |  | 
|  | for.cond.cleanup: | 
|  | ret i8 %add | 
|  | } | 
|  |  | 
|  | ; OOB in a loop. | 
|  | define signext i8 @SimpleLoopOOB() { | 
|  | ; CHECK-LABEL: @SimpleLoopOOB dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[10]: [0,11){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca [10 x i8], align 1 | 
|  | ; 11 iterations | 
|  | %lftr.limit = getelementptr inbounds [10 x i8], ptr %x, i64 0, i64 11 | 
|  | br label %for.body | 
|  |  | 
|  | for.body: | 
|  | %sum.010 = phi i8 [ 0, %entry ], [ %add, %for.body ] | 
|  | %p.09 = phi ptr [ %x, %entry ], [ %incdec.ptr, %for.body ] | 
|  | %incdec.ptr = getelementptr inbounds i8, ptr %p.09, i64 1 | 
|  | %load = load volatile i8, ptr %p.09, align 1 | 
|  | %add = add i8 %load, %sum.010 | 
|  | %exitcond = icmp eq ptr %incdec.ptr, %lftr.limit | 
|  | br i1 %exitcond, label %for.cond.cleanup, label %for.body | 
|  |  | 
|  | for.cond.cleanup: | 
|  | ret i8 %add | 
|  | } | 
|  |  | 
|  | define dso_local void @SizeCheck(i32 %sz) { | 
|  | ; CHECK-LABEL: @SizeCheck{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x1[128]: [0,4294967295){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x1 = alloca [128 x i8], align 16 | 
|  | %cmp = icmp slt i32 %sz, 129 | 
|  | br i1 %cmp, label %if.then, label %if.end | 
|  |  | 
|  | if.then: | 
|  | call void @llvm.memset.p0.i32(ptr nonnull align 16 %x1, i8 0, i32 %sz, i1 false) | 
|  | br label %if.end | 
|  |  | 
|  | if.end: | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; FIXME: scalable allocas are considered to be of size zero, and scalable accesses to be full-range. | 
|  | ; This effectively disables safety analysis for scalable allocations. | 
|  | define void @Scalable(ptr %p, ptr %unused, <vscale x 4 x i32> %v) { | 
|  | ; CHECK-LABEL: @Scalable dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT:   p[]: full-set | 
|  | ; CHECK-NEXT:   unused[]: empty-set | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT:   x[0]: [0,1){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store <vscale x 4 x i32> %v, ptr %p, align 4 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca <vscale x 4 x i32>, align 4 | 
|  | store i8 0, ptr %x, align 1 | 
|  | store <vscale x 4 x i32> %v, ptr %p, align 4 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | %zerosize_type = type {} | 
|  |  | 
|  | define void @ZeroSize(ptr %p)  { | 
|  | ; CHECK-LABEL: @ZeroSize dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT:   p[]: empty-set | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT:   x[0]: empty-set | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store %zerosize_type undef, ptr %x, align 4 | 
|  | ; GLOBAL-NEXT: store %zerosize_type undef, ptr undef, align 4 | 
|  | ; GLOBAL-NEXT: load %zerosize_type, ptr %p, align | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca %zerosize_type, align 4 | 
|  | store %zerosize_type undef, ptr %x, align 4 | 
|  | store %zerosize_type undef, ptr undef, align 4 | 
|  | %val = load %zerosize_type, ptr %p, align 4 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @OperandBundle() { | 
|  | ; CHECK-LABEL: @OperandBundle dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT:   a[4]: full-set | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | call void @LeakAddress() ["unknown"(ptr %a)] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @ByVal(ptr byval(i16) %p) { | 
|  | ; CHECK-LABEL: @ByVal dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @TestByVal() { | 
|  | ; CHECK-LABEL: @TestByVal dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[2]: [0,2) | 
|  | ; CHECK-NEXT: y[8]: [0,2) | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @ByVal(ptr byval(i16) %x) | 
|  | ; GLOBAL-NEXT: call void @ByVal(ptr byval(i16) %y) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i16, align 4 | 
|  | call void @ByVal(ptr byval(i16) %x) | 
|  |  | 
|  | %y = alloca i64, align 4 | 
|  | call void @ByVal(ptr byval(i16) %y) | 
|  |  | 
|  | ret void | 
|  | } | 
|  |  | 
|  | declare void @ByValArray(ptr byval([100000 x i64]) %p) | 
|  |  | 
|  | define void @TestByValArray() { | 
|  | ; CHECK-LABEL: @TestByValArray dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: z[800000]: [500000,1300000) | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %z = alloca [100000 x i64], align 4 | 
|  | %z2 = getelementptr i8, ptr %z, i64 500000 | 
|  | call void @ByValArray(ptr byval([100000 x i64]) %z2) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define dso_local i8 @LoadMinInt64(ptr %p) { | 
|  | ; CHECK-LABEL: @LoadMinInt64{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: p[]: [-9223372036854775808,-9223372036854775807){{$}} | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: load i8, ptr %p2, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | %p2 = getelementptr i8, ptr %p, i64 -9223372036854775808 | 
|  | %v = load i8, ptr %p2, align 1 | 
|  | ret i8 %v | 
|  | } | 
|  |  | 
|  | define void @Overflow() { | 
|  | ; CHECK-LABEL: @Overflow dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; LOCAL-NEXT: x[1]: empty-set, @LoadMinInt64(arg0, [-9223372036854775808,-9223372036854775807)){{$}} | 
|  | ; GLOBAL-NEXT: x[1]: full-set, @LoadMinInt64(arg0, [-9223372036854775808,-9223372036854775807)){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i8, align 4 | 
|  | %x2 = getelementptr i8, ptr %x, i64 -9223372036854775808 | 
|  | %v = call i8 @LoadMinInt64(ptr %x2) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @DeadBlock(ptr %p) { | 
|  | ; CHECK-LABEL: @DeadBlock dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: p[]: empty-set{{$}} | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[1]: empty-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 5, ptr %x | 
|  | ; GLOBAL-NEXT: store i64 -5, ptr %p | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i8, align 4 | 
|  | br label %end | 
|  |  | 
|  | dead: | 
|  | store i8 5, ptr %x | 
|  | store i64 -5, ptr %p | 
|  | br label %end | 
|  |  | 
|  | end: | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @LifeNotStarted() { | 
|  | ; CHECK-LABEL: @LifeNotStarted dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: x[1]: full-set{{$}} | 
|  | ; CHECK: y[1]: full-set{{$}} | 
|  | ; CHECK: z[1]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i8, align 4 | 
|  | %y = alloca i8, align 4 | 
|  | %z = alloca i8, align 4 | 
|  |  | 
|  | store i8 5, ptr %x | 
|  | %n = load i8, ptr %y | 
|  | call void @llvm.memset.p0.i32(ptr nonnull %z, i8 0, i32 1, i1 false) | 
|  |  | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %x) | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %y) | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %z) | 
|  |  | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @LifeOK() { | 
|  | ; CHECK-LABEL: @LifeOK dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: x[1]: [0,1){{$}} | 
|  | ; CHECK: y[1]: [0,1){{$}} | 
|  | ; CHECK: z[1]: [0,1){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i8 5, ptr %x | 
|  | ; GLOBAL-NEXT: %n = load i8, ptr %y | 
|  | ; GLOBAL-NEXT: call void @llvm.memset.p0.i32(ptr nonnull %z, i8 0, i32 1, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i8, align 4 | 
|  | %y = alloca i8, align 4 | 
|  | %z = alloca i8, align 4 | 
|  |  | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %x) | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %y) | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %z) | 
|  |  | 
|  | store i8 5, ptr %x | 
|  | %n = load i8, ptr %y | 
|  | call void @llvm.memset.p0.i32(ptr nonnull %z, i8 0, i32 1, i1 false) | 
|  |  | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @LifeEnded() { | 
|  | ; CHECK-LABEL: @LifeEnded dso_preemptable{{$}} | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: x[1]: full-set{{$}} | 
|  | ; CHECK: y[1]: full-set{{$}} | 
|  | ; CHECK: z[1]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i8, align 4 | 
|  | %y = alloca i8, align 4 | 
|  | %z = alloca i8, align 4 | 
|  |  | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %x) | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %y) | 
|  | call void @llvm.lifetime.start.p0(i64 1, ptr %z) | 
|  |  | 
|  | call void @llvm.lifetime.end.p0(i64 1, ptr %x) | 
|  | call void @llvm.lifetime.end.p0(i64 1, ptr %y) | 
|  | call void @llvm.lifetime.end.p0(i64 1, ptr %z) | 
|  |  | 
|  | store i8 5, ptr %x | 
|  | %n = load i8, ptr %y | 
|  | call void @llvm.memset.p0.i32(ptr nonnull %z, i8 0, i32 1, i1 false) | 
|  |  | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @TwoAllocasOK() { | 
|  | ; CHECK-LABEL: @TwoAllocasOK | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: [0,1){{$}} | 
|  | ; CHECK: y[1]: [0,1){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %y, ptr %a, i32 1, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | %y = alloca i8, align 4 | 
|  | call void @llvm.memcpy.p0.p0.i32(ptr %y, ptr %a, i32 1, i1 false) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @TwoAllocasOOBDest() { | 
|  | ; CHECK-LABEL: @TwoAllocasOOBDest | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: [0,4){{$}} | 
|  | ; CHECK: y[1]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | %y = alloca i8, align 4 | 
|  | call void @llvm.memcpy.p0.p0.i32(ptr %y, ptr %a, i32 4, i1 false) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @TwoAllocasOOBSource() { | 
|  | ; CHECK-LABEL: @TwoAllocasOOBSource | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: [0,4){{$}} | 
|  | ; CHECK: y[1]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | %y = alloca i8, align 4 | 
|  | call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %y, i32 4, i1 false) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @TwoAllocasOOBBoth() { | 
|  | ; CHECK-LABEL: @TwoAllocasOOBBoth | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: [0,5){{$}} | 
|  | ; CHECK: y[1]: [0,5){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | %y = alloca i8, align 4 | 
|  | call void @llvm.memcpy.p0.p0.i32(ptr %y, ptr %a, i32 5, i1 false) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @MixedAccesses() { | 
|  | ; CHECK-LABEL: @MixedAccesses | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: [0,5){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 5, i1 false) | 
|  | call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @MixedAccesses2() { | 
|  | ; CHECK-LABEL: @MixedAccesses2 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: [0,8){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: load i32, ptr %a, align 4 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | %n1 = load i64, ptr %a, align 4 | 
|  | %n2 = load i32, ptr %a, align 4 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @MixedAccesses3(ptr %func) { | 
|  | ; CHECK-LABEL: @MixedAccesses3 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: func[]: full-set | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: load i32, ptr %a, align 4 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | %n2 = load i32, ptr %a, align 4 | 
|  | call void %func(ptr %a) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @MixedAccesses4() { | 
|  | ; CHECK-LABEL: @MixedAccesses4 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: full-set{{$}} | 
|  | ; CHECK: a1[8]: [0,8){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: load i32, ptr %a, align 4 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | %a1 = alloca ptr, align 4 | 
|  | %n2 = load i32, ptr %a, align 4 | 
|  | store ptr %a, ptr %a1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define ptr @MixedAccesses5(i1 %x, ptr %y) { | 
|  | ; CHECK-LABEL: @MixedAccesses5 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK: y[]: full-set | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: load i32, ptr %a, align 4 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | br i1 %x, label %tlabel, label %flabel | 
|  | flabel: | 
|  | %n = load i32, ptr %a, align 4 | 
|  | ret ptr %y | 
|  | tlabel: | 
|  | ret ptr %a | 
|  | } | 
|  |  | 
|  | define void @MixedAccesses6(ptr %arg) { | 
|  | ; CHECK-LABEL: @MixedAccesses6 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: arg[]: [0,4) | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: [0,4) | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %arg, i32 4, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %arg, i32 4, i1 false) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @MixedAccesses7(i1 %cond, ptr %arg) { | 
|  | ; SECV doesn't support select, so we consider this non-stack-safe, even through | 
|  | ; it is. | 
|  | ; | 
|  | ; CHECK-LABEL: @MixedAccesses7 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: arg[]: full-set | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: full-set | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | %x1 = select i1 %cond, ptr %arg, ptr %a | 
|  | call void @llvm.memcpy.p0.p0.i32(ptr %x1, ptr %arg, i32 4, i1 false) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @NoStackAccess(ptr %arg1, ptr %arg2) { | 
|  | ; CHECK-LABEL: @NoStackAccess | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: arg1[]: [0,4) | 
|  | ; CHECK-NEXT: arg2[]: [0,4) | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: empty-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %arg1, ptr %arg2, i32 4, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | call void @llvm.memcpy.p0.p0.i32(ptr %arg1, ptr %arg2, i32 4, i1 false) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @DoubleLifetime() { | 
|  | ; CHECK-LABEL: @DoubleLifetime | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | call void @llvm.lifetime.start.p0(i64 4, ptr %a) | 
|  | call void @llvm.lifetime.end.p0(i64 4, ptr %a) | 
|  | call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 true) | 
|  |  | 
|  | call void @llvm.lifetime.start.p0(i64 4, ptr %a) | 
|  | call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | call void @llvm.lifetime.end.p0(i64 4, ptr %a) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @DoubleLifetime2() { | 
|  | ; CHECK-LABEL: @DoubleLifetime2 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | call void @llvm.lifetime.start.p0(i64 4, ptr %a) | 
|  | call void @llvm.lifetime.end.p0(i64 4, ptr %a) | 
|  | %n = load i32, ptr %a | 
|  |  | 
|  | call void @llvm.lifetime.start.p0(i64 4, ptr %a) | 
|  | call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | call void @llvm.lifetime.end.p0(i64 4, ptr %a) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @DoubleLifetime3() { | 
|  | ; CHECK-LABEL: @DoubleLifetime3 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | call void @llvm.lifetime.start.p0(i64 4, ptr %a) | 
|  | call void @llvm.lifetime.end.p0(i64 4, ptr %a) | 
|  | store i32 5, ptr %a | 
|  |  | 
|  | call void @llvm.lifetime.start.p0(i64 4, ptr %a) | 
|  | call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | call void @llvm.lifetime.end.p0(i64 4, ptr %a) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @DoubleLifetime4() { | 
|  | ; CHECK-LABEL: @DoubleLifetime4 | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK: a[4]: full-set{{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %a = alloca i32, align 4 | 
|  | call void @llvm.lifetime.start.p0(i64 4, ptr %a) | 
|  | call void @llvm.memset.p0.i32(ptr %a, i8 1, i32 4, i1 false) | 
|  | call void @llvm.lifetime.end.p0(i64 4, ptr %a) | 
|  | call void @unknown_call(ptr %a) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @Cmpxchg4Arg(ptr %p) { | 
|  | ; CHECK-LABEL: @Cmpxchg4Arg | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: p[]: [0,4){{$}} | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: cmpxchg ptr %p, i32 0, i32 1 monotonic monotonic, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | cmpxchg ptr %p, i32 0, i32 1 monotonic monotonic, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @AtomicRMW4Arg(ptr %p) { | 
|  | ; CHECK-LABEL: @AtomicRMW4Arg | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: p[]: [0,4){{$}} | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: atomicrmw add ptr %p, i32 1 monotonic, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | atomicrmw add ptr %p, i32 1 monotonic, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @Cmpxchg4Alloca() { | 
|  | ; CHECK-LABEL: @Cmpxchg4Alloca | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: cmpxchg ptr %x, i32 0, i32 1 monotonic monotonic, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | cmpxchg ptr %x, i32 0, i32 1 monotonic monotonic, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @AtomicRMW4Alloca() { | 
|  | ; CHECK-LABEL: @AtomicRMW4Alloca | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; CHECK-NEXT: x[4]: [0,4){{$}} | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: atomicrmw add ptr %x, i32 1 monotonic, align 1 | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | %x = alloca i32, align 4 | 
|  | atomicrmw add ptr %x, i32 1 monotonic, align 1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @StoreArg(ptr %p) { | 
|  | ; CHECK-LABEL: @StoreArg | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; CHECK-NEXT: p[]: [0,4){{$}} | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; GLOBAL-NEXT: store i32 1, ptr %p | 
|  | ; CHECK-EMPTY: | 
|  | entry: | 
|  | store i32 1, ptr %p | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @NonPointer(ptr %p) { | 
|  | ; CHECK-LABEL: @NonPointer | 
|  | ; CHECK-NEXT: args uses: | 
|  | ; LOCAL-NEXT: p[]: empty-set, @unknown_call_int(arg0, full-set) | 
|  | ; GLOBAL-NEXT: p[]: full-set, @unknown_call_int(arg0, full-set) | 
|  | ; CHECK-NEXT: allocas uses: | 
|  | ; GLOBAL-NEXT: safe accesses: | 
|  | ; CHECK-EMPTY: | 
|  | %int = ptrtoint ptr %p to i64 | 
|  | call void @unknown_call_int(i64 %int) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | @ifunc = dso_local ifunc i64 (ptr), ptr @ifunc_resolver | 
|  |  | 
|  | define dso_local void @CallIfunc(ptr noundef %uaddr) local_unnamed_addr { | 
|  | ; CHECK-LABEL: @CallIfunc | 
|  | ; CHECK-NEXT:  args uses: | 
|  | ; CHECK-NEXT:    uaddr[]: full-set | 
|  | entry: | 
|  | tail call i64 @ifunc(ptr noundef %uaddr) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define dso_local ptr @ifunc_resolver() { | 
|  | entry: | 
|  | ret ptr null | 
|  | } | 
|  |  | 
|  | declare void @llvm.lifetime.start.p0(i64, ptr nocapture) | 
|  | declare void @llvm.lifetime.end.p0(i64, ptr nocapture) |