; 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 i8* null, align 8 declare void @llvm.memset.p0i8.i32(i8* %dest, i8 %val, i32 %len, i1 %isvolatile) declare void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 %isvolatile) declare void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 %isvolatile) declare void @llvm.memset.p0i8.i64(i8* %dest, i8 %val, i64 %len, i1 %isvolatile) declare void @unknown_call(i8* %dest) declare i8* @retptr(i8* 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 %x1 = bitcast i32* %x to i8* store i8* %x1, i8** @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, i8* %x1, align 1 ; CHECK-EMPTY: entry: %x = alloca i32, align 4 %x1 = bitcast i32* %x to i8* store i8 0, i8* %x1, 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, i8* %x2, align 1 ; CHECK-EMPTY: entry: %x = alloca i32, align 4 %x1 = bitcast i32* %x to i8* %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, i8* %x1, i64 %i store i8 0, i8* %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, i8* %x2, align 1 ; CHECK-EMPTY: entry: %x = alloca i32, align 4 %x1 = bitcast i32* %x to i8* %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, i8* %x1, i64 %i2 store i8 0, i8* %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, i32* %x, align 4 ; CHECK-EMPTY: entry: %x = alloca i32, align 4 store i32 0, i32* %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, i8* %x2, align 1 ; CHECK-EMPTY: entry: %x = alloca i32, align 4 %x1 = bitcast i32* %x to i8* %x2 = getelementptr i8, i8* %x1, i64 2 store i8 0, i8* %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 i32* %x to i64 %x2 = add i64 %x1, 2 %x3 = inttoptr i64 %x2 to i8* store i8 0, i8* %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, i8* %x2, align 1 ; CHECK-EMPTY: entry: %x = alloca i32, align 4 %x1 = bitcast i32* %x to i8* %x2 = call i8* @retptr(i8* %x1) store i8 0, i8* %x2, align 1 ret void } define dso_local void @WriteMinMax(i8* %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, i8* %p1, align 1 ; GLOBAL-NEXT: store i8 0, i8* %p2, align 1 ; CHECK-EMPTY: entry: %p1 = getelementptr i8, i8* %p, i64 9223372036854775805 store i8 0, i8* %p1, align 1 %p2 = getelementptr i8, i8* %p, i64 -9223372036854775805 store i8 0, i8* %p2, align 1 ret void } define dso_local void @WriteMax(i8* %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.p0i8.i64(i8* %p, i8 1, i64 9223372036854775806, i1 false) ; GLOBAL-NEXT: call void @llvm.memset.p0i8.i64(i8* %p2, i8 1, i64 9223372036854775806, i1 false) ; CHECK-EMPTY: entry: call void @llvm.memset.p0i8.i64(i8* %p, i8 1, i64 9223372036854775806, i1 0) %p2 = getelementptr i8, i8* %p, i64 -9223372036854775807 call void @llvm.memset.p0i8.i64(i8* %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 %x1 = bitcast i32* %x to i8* %x2 = getelementptr i8, i8* %x1, i64 2 %x3 = bitcast i8* %x2 to i32* store i32 0, i32* %x3, 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 %x1 = bitcast i32* %x to i8* %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, i8* %x1, i64 %i store i8 0, i8* %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 %x1 = bitcast i32* %x to i8* %c2 = icmp slt i64 %i, 5 br i1 %c2, label %c2.true, label %false c2.true: %x2 = getelementptr i8, i8* %x1, i64 %i store i8 0, i8* %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 %x1 = bitcast i32* %x to i8* %x2 = getelementptr i8, i8* %x1, i64 2 %x3 = call i8* @retptr(i8* %x2) %x4 = bitcast i8* %x3 to i32* store i32 0, i32* %x4, 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, i8* %x1, align 1 ; CHECK-EMPTY: entry: %x = alloca i32, align 4 %x1 = bitcast i32* %x to i8* %v = load i8, i8* %x1, 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 %x1 = bitcast i32* %x to i8* %x2 = getelementptr i8, i8* %x1, i64 2 %x3 = bitcast i8* %x2 to i32* %v = load i32, i32* %x3, align 1 ret void } ; Leak through ret. define i8* @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 %x1 = bitcast i32* %x to i8* %x2 = getelementptr i8, i8* %x1, i64 2 ret i8* %x2 } declare void @Foo(i16* %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 %x1 = bitcast i64* %x to i16* %x2 = getelementptr i16, i16* %x1, i64 1 call void @Foo(i16* %x2); ret void } ; Indirect calls can not be analyzed (yet). ; FIXME: %p[]: full-set looks invalid define void @IndirectCall(void (i8*)* %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 %x1 = bitcast i32* %x to i8* call void %p(i8* %x1); 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, i8* %x2, align 1 ; CHECK-EMPTY: entry: %x = alloca i32, align 4 %x1 = bitcast i32* %x to i8* %idx = select i1 %z, i64 1, i64 2 %x2 = getelementptr i8, i8* %x1, i64 %idx store i8 0, i8* %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, i32* %x, i64 -400000000000 store i32 0, i32* %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, i32* %x, i16 %z store i32 0, i32* %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 %x1 = bitcast i32* %x to i8* %idx = select i1 %z, i64 1, i64 4 %x2 = getelementptr i8, i8* %x1, i64 %idx store i8 0, i8* %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, i32* %x3, align 1 ; CHECK-EMPTY: entry: %x = alloca i32, i32 10, align 4 %x1 = bitcast i32* %x to i8* %x2 = getelementptr i8, i8* %x1, i64 36 %x3 = bitcast i8* %x2 to i32* store i32 0, i32* %x3, 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 %x1 = bitcast i32* %x to i8* %x2 = getelementptr i8, i8* %x1, i64 37 %x3 = bitcast i8* %x2 to i32* store i32 0, i32* %x3, 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, i32* %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, i32* %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: %1 = load volatile i8, i8* %p.09, align 1 ; CHECK-EMPTY: entry: %x = alloca [10 x i8], align 1 %0 = getelementptr inbounds [10 x i8], [10 x i8]* %x, i64 0, i64 0 %lftr.limit = getelementptr inbounds [10 x i8], [10 x i8]* %x, i64 0, i64 10 br label %for.body for.body: %sum.010 = phi i8 [ 0, %entry ], [ %add, %for.body ] %p.09 = phi i8* [ %0, %entry ], [ %incdec.ptr, %for.body ] %incdec.ptr = getelementptr inbounds i8, i8* %p.09, i64 1 %1 = load volatile i8, i8* %p.09, align 1 %add = add i8 %1, %sum.010 %exitcond = icmp eq i8* %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 %0 = getelementptr inbounds [10 x i8], [10 x i8]* %x, i64 0, i64 0 ; 11 iterations %lftr.limit = getelementptr inbounds [10 x i8], [10 x i8]* %x, i64 0, i64 11 br label %for.body for.body: %sum.010 = phi i8 [ 0, %entry ], [ %add, %for.body ] %p.09 = phi i8* [ %0, %entry ], [ %incdec.ptr, %for.body ] %incdec.ptr = getelementptr inbounds i8, i8* %p.09, i64 1 %1 = load volatile i8, i8* %p.09, align 1 %add = add i8 %1, %sum.010 %exitcond = icmp eq i8* %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 %x1.sub = getelementptr inbounds [128 x i8], [128 x i8]* %x1, i64 0, i64 0 %cmp = icmp slt i32 %sz, 129 br i1 %cmp, label %if.then, label %if.end if.then: call void @llvm.memset.p0i8.i32(i8* nonnull align 16 %x1.sub, 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(<vscale x 4 x i32>* %p, <vscale x 4 x i32>* %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, <vscale x 4 x i32>* %p, align 4 ; CHECK-EMPTY: entry: %x = alloca <vscale x 4 x i32>, align 4 %x1 = bitcast <vscale x 4 x i32>* %x to i8* store i8 0, i8* %x1, align 1 store <vscale x 4 x i32> %v, <vscale x 4 x i32>* %p, align 4 ret void } %zerosize_type = type {} define void @ZeroSize(%zerosize_type *%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, %zerosize_type* %x, align 4 ; GLOBAL-NEXT: store %zerosize_type undef, %zerosize_type* undef, align 4 ; GLOBAL-NEXT: load %zerosize_type, %zerosize_type* %p, align ; CHECK-EMPTY: entry: %x = alloca %zerosize_type, align 4 store %zerosize_type undef, %zerosize_type* %x, align 4 store %zerosize_type undef, %zerosize_type* undef, align 4 %val = load %zerosize_type, %zerosize_type* %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"(i32* %a)] ret void } define void @ByVal(i16* 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(i16* byval(i16) %x) ; GLOBAL-NEXT: call void @ByVal(i16* byval(i16) %y1) ; CHECK-EMPTY: entry: %x = alloca i16, align 4 call void @ByVal(i16* byval(i16) %x) %y = alloca i64, align 4 %y1 = bitcast i64* %y to i16* call void @ByVal(i16* byval(i16) %y1) ret void } declare void @ByValArray([100000 x i64]* 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 %z1 = bitcast [100000 x i64]* %z to i8* %z2 = getelementptr i8, i8* %z1, i64 500000 %z3 = bitcast i8* %z2 to [100000 x i64]* call void @ByValArray([100000 x i64]* byval([100000 x i64]) %z3) ret void } define dso_local i8 @LoadMinInt64(i8* %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, i8* %p2, align 1 ; CHECK-EMPTY: %p2 = getelementptr i8, i8* %p, i64 -9223372036854775808 %v = load i8, i8* %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, i8* %x, i64 -9223372036854775808 %v = call i8 @LoadMinInt64(i8* %x2) ret void } define void @DeadBlock(i64* %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, i8* %x ; GLOBAL-NEXT: store i64 -5, i64* %p ; CHECK-EMPTY: entry: %x = alloca i8, align 4 br label %end dead: store i8 5, i8* %x store i64 -5, i64* %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, i8* %x %n = load i8, i8* %y call void @llvm.memset.p0i8.i32(i8* nonnull %z, i8 0, i32 1, i1 false) call void @llvm.lifetime.start.p0i8(i64 1, i8* %x) call void @llvm.lifetime.start.p0i8(i64 1, i8* %y) call void @llvm.lifetime.start.p0i8(i64 1, i8* %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, i8* %x ; GLOBAL-NEXT: %n = load i8, i8* %y ; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* 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.p0i8(i64 1, i8* %x) call void @llvm.lifetime.start.p0i8(i64 1, i8* %y) call void @llvm.lifetime.start.p0i8(i64 1, i8* %z) store i8 5, i8* %x %n = load i8, i8* %y call void @llvm.memset.p0i8.i32(i8* 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.p0i8(i64 1, i8* %x) call void @llvm.lifetime.start.p0i8(i64 1, i8* %y) call void @llvm.lifetime.start.p0i8(i64 1, i8* %z) call void @llvm.lifetime.end.p0i8(i64 1, i8* %x) call void @llvm.lifetime.end.p0i8(i64 1, i8* %y) call void @llvm.lifetime.end.p0i8(i64 1, i8* %z) store i8 5, i8* %x %n = load i8, i8* %y call void @llvm.memset.p0i8.i32(i8* 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.p0i8.p0i8.i32(i8* %y, i8* %x, i32 1, i1 false) ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* %y = alloca i8, align 4 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %y, i8* %x, 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 %x = bitcast i32* %a to i8* %y = alloca i8, align 4 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %y, i8* %x, 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 %x = bitcast i32* %a to i8* %y = alloca i8, align 4 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %x, i8* %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 %x = bitcast i32* %a to i8* %y = alloca i8, align 4 call void @llvm.memcpy.p0i8.p0i8.i32(i8* %y, i8* %x, 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.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 5, i1 false) call void @llvm.memset.p0i8.i32(i8* %x, 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, i32* %a, align 4 ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %a1 = bitcast i32* %a to i64* %n1 = load i64, i64* %a1, align 4 %n2 = load i32, i32* %a, align 4 ret void } define void @MixedAccesses3(void (i8*)* %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, i32* %a, align 4 ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* %n2 = load i32, i32* %a, align 4 call void %func(i8* %x) 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, i32* %a, align 4 ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %a1 = alloca i32*, align 4 %n2 = load i32, i32* %a, align 4 store i32* %a, i32** %a1 ret void } define i32* @MixedAccesses5(i1 %x, i32* %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, i32* %a, align 4 ; CHECK-EMPTY: entry: %a = alloca i32, align 4 br i1 %x, label %tlabel, label %flabel flabel: %n = load i32, i32* %a, align 4 ret i32* %y tlabel: ret i32* %a } define void @MixedAccesses6(i8* %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.p0i8.p0i8.i32(i8* %x, i8* %arg, i32 4, i1 false) ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* %x, i8* %arg, i32 4, i1 false) ret void } define void @MixedAccesses7(i1 %cond, i8* %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 %x = bitcast i32* %a to i8* %x1 = select i1 %cond, i8* %arg, i8* %x call void @llvm.memcpy.p0i8.p0i8.i32(i8* %x1, i8* %arg, i32 4, i1 false) ret void } define void @NoStackAccess(i8* %arg1, i8* %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.p0i8.p0i8.i32(i8* %arg1, i8* %arg2, i32 4, i1 false) ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* %arg1, i8* %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.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %x) call void @llvm.lifetime.end.p0i8(i64 4, i8* %x) call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 true) call void @llvm.lifetime.start.p0i8(i64 4, i8* %x) call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) call void @llvm.lifetime.end.p0i8(i64 4, i8* %x) 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.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %x) call void @llvm.lifetime.end.p0i8(i64 4, i8* %x) %n = load i32, i32* %a call void @llvm.lifetime.start.p0i8(i64 4, i8* %x) call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) call void @llvm.lifetime.end.p0i8(i64 4, i8* %x) 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.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %x) call void @llvm.lifetime.end.p0i8(i64 4, i8* %x) store i32 5, i32* %a call void @llvm.lifetime.start.p0i8(i64 4, i8* %x) call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) call void @llvm.lifetime.end.p0i8(i64 4, i8* %x) 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.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) ; CHECK-EMPTY: entry: %a = alloca i32, align 4 %x = bitcast i32* %a to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %x) call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false) call void @llvm.lifetime.end.p0i8(i64 4, i8* %x) call void @unknown_call(i8* %x) ret void } declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)