Files
clang-p2996/llvm/test/Analysis/StackSafetyAnalysis/local.ll
Eli Friedman 7ac1c7bead 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-06 12:16:05 -07:00

615 lines
16 KiB
LLVM

; RUN: opt -S -analyze -stack-safety-local < %s -enable-new-pm=0 | FileCheck %s --check-prefixes=CHECK,LOCAL
; RUN: opt -S -passes="print<stack-safety-local>" -disable-output < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,LOCAL
; RUN: opt -S -analyze -stack-safety < %s -enable-new-pm=0 | FileCheck %s --check-prefixes=CHECK,GLOBAL
; 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)
; Address leaked.
define void @LeakAddress() {
; CHECK-LABEL: @LeakAddress dso_preemptable{{$}}
; CHECK-NEXT: args uses:
; CHECK-NEXT: allocas uses:
; CHECK-NEXT: x[4]: full-set{{$}}
; 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){{$}}
; 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 @StoreInBounds2() {
; CHECK-LABEL: @StoreInBounds2 dso_preemptable{{$}}
; CHECK-NEXT: args uses:
; CHECK-NEXT: allocas uses:
; CHECK-NEXT: x[4]: [0,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){{$}}
; 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{{$}}
; 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 dso_local void @WriteMinMax(i8* %p) {
; CHECK-LABEL: @WriteMinMax{{$}}
; CHECK-NEXT: args uses:
; CHECK-NEXT: p[]: full-set
; CHECK-NEXT: allocas uses:
; 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:
; 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){{$}}
; 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
}
; 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){{$}}
; 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){{$}}
; 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{{$}}
; 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)){{$}}
; 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{{$}}
; 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){{$}}
; 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){{$}}
; 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){{$}}
; 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){{$}}
; 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){{$}}
; 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){{$}}
; 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{{$}}
; 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){{$}}
; 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){{$}}
; 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){{$}}
; 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){{$}}
; 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){{$}}
; 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){{$}}
; 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
; 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
; 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:
; 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)
; 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)
; 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:
; 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)){{$}}
; 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{{$}}
; 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{{$}}
; 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){{$}}
; 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{{$}}
; 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
}
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)