forked from OSchip/llvm-project
[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:
parent
8f592ed333
commit
5d964e262f
|
@ -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;
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue