[StackSafety] Check variable lifetime

We can't consider variable safe if out-of-lifetime access is possible.
So if StackLifetime can't prove that the instruction always uses
the variable when it's still alive, we consider it unsafe.
This commit is contained in:
Vitaly Buka 2020-06-22 01:28:25 -07:00
parent 8f592ed333
commit 5d964e262f
2 changed files with 97 additions and 4 deletions

View File

@ -297,6 +297,7 @@ bool StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
SmallPtrSet<const Value *, 16> Visited;
SmallVector<const Value *, 8> WorkList;
WorkList.push_back(Ptr);
const AllocaInst *AI = dyn_cast<AllocaInst>(Ptr);
// A DFS search through all uses of the alloca in bitcasts/PHI/GEPs/etc.
while (!WorkList.empty()) {
@ -310,6 +311,10 @@ bool StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
switch (I->getOpcode()) {
case Instruction::Load: {
if (AI && !SL.isAliveAfter(AI, I)) {
US.updateRange(UnknownRange);
return false;
}
US.updateRange(
getAccessRange(UI, Ptr, DL.getTypeStoreSize(I->getType())));
break;
@ -324,6 +329,10 @@ bool StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
US.updateRange(UnknownRange);
return false;
}
if (AI && !SL.isAliveAfter(AI, I)) {
US.updateRange(UnknownRange);
return false;
}
US.updateRange(getAccessRange(
UI, Ptr, DL.getTypeStoreSize(I->getOperand(0)->getType())));
break;
@ -341,6 +350,11 @@ bool StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
if (I->isLifetimeStartOrEnd())
break;
if (AI && !SL.isAliveAfter(AI, I)) {
US.updateRange(UnknownRange);
return false;
}
if (const MemIntrinsic *MI = dyn_cast<MemIntrinsic>(I)) {
US.updateRange(getMemIntrinsicAccessRange(MI, UI, Ptr));
break;

View File

@ -476,8 +476,8 @@ define void @Overflow() {
; CHECK-LABEL: @Overflow dso_preemptable{{$}}
; CHECK-NEXT: args uses:
; CHECK-NEXT: allocas uses:
; LOCAL: x[1]: empty-set, @LoadMinInt64(arg0, [-9223372036854775808,-9223372036854775807)){{$}}
; GLOBAL: x[1]: full-set, @LoadMinInt64(arg0, [-9223372036854775808,-9223372036854775807)){{$}}
; LOCAL-NEXT: x[1]: empty-set, @LoadMinInt64(arg0, [-9223372036854775808,-9223372036854775807)){{$}}
; GLOBAL-NEXT: x[1]: full-set, @LoadMinInt64(arg0, [-9223372036854775808,-9223372036854775807)){{$}}
; CHECK-NOT: ]:
entry:
%x = alloca i8, align 4
@ -491,7 +491,7 @@ define void @DeadBlock(i64* %p) {
; CHECK-NEXT: args uses:
; CHECK-NEXT: p[]: empty-set{{$}}
; CHECK-NEXT: allocas uses:
; CHECK: x[1]: empty-set{{$}}
; CHECK-NEXT: x[1]: empty-set{{$}}
; CHECK-NOT: ]:
entry:
%x = alloca i8, align 4
@ -504,4 +504,83 @@ dead:
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-NOT: ]:
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-NOT: ]:
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-NOT: ]:
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)