llvm-project/llvm/test/Analysis/StackSafetyAnalysis/local.ll

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1089 lines
29 KiB
LLVM
Raw Normal View History

; 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:
Recommit [ScalarEvolution] Make getMinusSCEV() fail for unrelated pointers. As part of making ScalarEvolution's handling of pointers consistent, we want to forbid multiplying a pointer by -1 (or any other value). This means we can't blindly subtract pointers. There are a few ways we could deal with this: 1. We could completely forbid subtracting pointers in getMinusSCEV() 2. We could forbid subracting pointers with different pointer bases (this patch). 3. We could try to ptrtoint pointer operands. The option in this patch is more friendly to non-integral pointers: code that works with normal pointers will also work with non-integral pointers. And it seems like there are very few places that actually benefit from the third option. As a minimal patch, the ScalarEvolution implementation of getMinusSCEV still ends up subtracting pointers if they have the same base. This should eliminate the shared pointer base, but eventually we'll need to rewrite it to avoid negating the pointer base. I plan to do this as a separate step to allow measuring the compile-time impact. This doesn't cause obvious functional changes in most cases; the one case that is significantly affected is ICmpZero handling in LSR (which is the source of almost all the test changes). The resulting changes seem okay to me, but suggestions welcome. As an alternative, I tried explicitly ptrtoint'ing the operands, but the result doesn't seem obviously better. I deleted the test lsr-undef-in-binop.ll becuase I couldn't figure out how to repair it to test what it was actually trying to test. Recommitting with fix to MemoryDepChecker::isDependent. Differential Revision: https://reviews.llvm.org/D104806
2021-07-07 02:25:49 +08:00
; 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)