mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-12-14 23:29:51 +00:00
Re-commit: Mark values as trivially dead when their only use is a start or end lifetime intrinsic.
Summary: If the only use of a value is a start or end lifetime intrinsic then mark the intrinsic as trivially dead. This should allow for that value to then be removed as well. Currently, this only works for allocas, globals, and arguments. Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D79355
This commit is contained in:
parent
12d1fc6516
commit
03bc2a070d
@ -403,9 +403,22 @@ bool llvm::wouldInstructionBeTriviallyDead(Instruction *I,
|
|||||||
II->getIntrinsicID() == Intrinsic::launder_invariant_group)
|
II->getIntrinsicID() == Intrinsic::launder_invariant_group)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Lifetime intrinsics are dead when their right-hand is undef.
|
if (II->isLifetimeStartOrEnd()) {
|
||||||
if (II->isLifetimeStartOrEnd())
|
auto *Arg = II->getArgOperand(1);
|
||||||
return isa<UndefValue>(II->getArgOperand(1));
|
// Lifetime intrinsics are dead when their right-hand is undef.
|
||||||
|
if (isa<UndefValue>(Arg))
|
||||||
|
return true;
|
||||||
|
// If the right-hand is an alloc, global, or argument and the only uses
|
||||||
|
// are lifetime intrinsics then the intrinsics are dead.
|
||||||
|
if (isa<AllocaInst>(Arg) || isa<GlobalValue>(Arg) || isa<Argument>(Arg))
|
||||||
|
return llvm::all_of(Arg->uses(), [](Use &Use) {
|
||||||
|
if (IntrinsicInst *IntrinsicUse =
|
||||||
|
dyn_cast<IntrinsicInst>(Use.getUser()))
|
||||||
|
return IntrinsicUse->isLifetimeStartOrEnd();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Assumptions are dead if their condition is trivially true. Guards on
|
// Assumptions are dead if their condition is trivially true. Guards on
|
||||||
// true are operationally no-ops. In the future we can consider more
|
// true are operationally no-ops. In the future we can consider more
|
||||||
|
@ -80,7 +80,6 @@ define void @test3a(i8* %P, i8 %X) {
|
|||||||
|
|
||||||
%P2 = getelementptr i8, i8* %P, i32 2
|
%P2 = getelementptr i8, i8* %P, i32 2
|
||||||
store i8 %Y, i8* %P2
|
store i8 %Y, i8* %P2
|
||||||
; CHECK-NEXT: call void @llvm.lifetime.end
|
|
||||||
call void @llvm.lifetime.end.p0i8(i64 10, i8* %P)
|
call void @llvm.lifetime.end.p0i8(i64 10, i8* %P)
|
||||||
ret void
|
ret void
|
||||||
; CHECK-NEXT: ret void
|
; CHECK-NEXT: ret void
|
||||||
|
@ -386,11 +386,11 @@ define void @callerD2() {
|
|||||||
store i8 0, i8* %unknown
|
store i8 0, i8* %unknown
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
; CHECK: Function Attrs: argmemonly nounwind willreturn
|
|
||||||
|
; CHECK: Function Attrs: nofree {{(norecurse )?}}nosync nounwind readnone willreturn
|
||||||
define void @callerE(i8* %arg) {
|
define void @callerE(i8* %arg) {
|
||||||
; CHECK-LABEL: define {{[^@]+}}@callerE
|
; CHECK-LABEL: define {{[^@]+}}@callerE
|
||||||
; CHECK-SAME: (i8* nocapture [[ARG:%.*]])
|
; CHECK-SAME: (i8* nocapture nofree readnone [[ARG:%.*]])
|
||||||
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nocapture [[ARG]])
|
|
||||||
; CHECK-NEXT: ret void
|
; CHECK-NEXT: ret void
|
||||||
;
|
;
|
||||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* %arg)
|
call void @llvm.lifetime.start.p0i8(i64 4, i8* %arg)
|
||||||
|
@ -11,5 +11,63 @@ define void @test() {
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) nounwind
|
||||||
|
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) nounwind
|
||||||
|
|
||||||
|
; CHECK-LABEL: @test_lifetime_alloca
|
||||||
|
define i32 @test_lifetime_alloca() {
|
||||||
|
; Check that lifetime intrinsics are removed along with the pointer.
|
||||||
|
; CHECK-NEXT: @llvm.dbg.value
|
||||||
|
; CHECK-NEXT: ret i32 0
|
||||||
|
; CHECK-NOT: llvm.lifetime.start
|
||||||
|
; CHECK-NOT: llvm.lifetime.end
|
||||||
|
%i = alloca i8, align 4
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 -1, i8* %i)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 -1, i8* %i)
|
||||||
|
ret i32 0
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: @test_lifetime_arg
|
||||||
|
define i32 @test_lifetime_arg(i8*) {
|
||||||
|
; Check that lifetime intrinsics are removed along with the pointer.
|
||||||
|
; CHECK-NEXT: llvm.dbg.value
|
||||||
|
; CHECK-NEXT: ret i32 0
|
||||||
|
; CHECK-NOT: llvm.lifetime.start
|
||||||
|
; CHECK-NOT: llvm.lifetime.end
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 -1, i8* %0)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 -1, i8* %0)
|
||||||
|
ret i32 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@glob = global i8 1
|
||||||
|
|
||||||
|
; CHECK-LABEL: @test_lifetime_global
|
||||||
|
define i32 @test_lifetime_global() {
|
||||||
|
; Check that lifetime intrinsics are removed along with the pointer.
|
||||||
|
; CHECK-NEXT: llvm.dbg.value
|
||||||
|
; CHECK-NEXT: ret i32 0
|
||||||
|
; CHECK-NOT: llvm.lifetime.start
|
||||||
|
; CHECK-NOT: llvm.lifetime.end
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 -1, i8* @glob)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 -1, i8* @glob)
|
||||||
|
ret i32 0
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: @test_lifetime_bitcast
|
||||||
|
define i32 @test_lifetime_bitcast(i32*) {
|
||||||
|
; Check that lifetime intrinsics are NOT removed when the pointer is a bitcast.
|
||||||
|
; It's not uncommon for two bitcasts to be made: one for lifetime, one for use.
|
||||||
|
; TODO: Support the above case.
|
||||||
|
; CHECK-NEXT: bitcast
|
||||||
|
; CHECK-NEXT: llvm.dbg.value
|
||||||
|
; CHECK-NEXT: llvm.lifetime.start
|
||||||
|
; CHECK-NEXT: llvm.lifetime.end
|
||||||
|
; CHECK-NEXT: ret i32 0
|
||||||
|
%2 = bitcast i32* %0 to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 -1, i8* %2)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 -1, i8* %2)
|
||||||
|
ret i32 0
|
||||||
|
}
|
||||||
|
|
||||||
; CHECK: [[add]] = !DILocalVariable
|
; CHECK: [[add]] = !DILocalVariable
|
||||||
; CHECK: [[sub]] = !DILocalVariable
|
; CHECK: [[sub]] = !DILocalVariable
|
||||||
|
@ -12,7 +12,7 @@ define void @test1() {
|
|||||||
|
|
||||||
store i8 0, i8* %A ;; Written to by memset
|
store i8 0, i8* %A ;; Written to by memset
|
||||||
call void @llvm.lifetime.end.p0i8(i64 1, i8* %A)
|
call void @llvm.lifetime.end.p0i8(i64 1, i8* %A)
|
||||||
; CHECK: lifetime.end
|
; CHECK-NOT: lifetime.end
|
||||||
|
|
||||||
call void @llvm.memset.p0i8.i8(i8* %A, i8 0, i8 -1, i1 false)
|
call void @llvm.memset.p0i8.i8(i8* %A, i8 0, i8 -1, i1 false)
|
||||||
; CHECK-NOT: memset
|
; CHECK-NOT: memset
|
||||||
@ -26,11 +26,9 @@ define void @test2(i32* %P) {
|
|||||||
%Q = getelementptr i32, i32* %P, i32 1
|
%Q = getelementptr i32, i32* %P, i32 1
|
||||||
%R = bitcast i32* %Q to i8*
|
%R = bitcast i32* %Q to i8*
|
||||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* %R)
|
call void @llvm.lifetime.start.p0i8(i64 4, i8* %R)
|
||||||
; CHECK: lifetime.start
|
|
||||||
store i32 0, i32* %Q ;; This store is dead.
|
store i32 0, i32* %Q ;; This store is dead.
|
||||||
; CHECK-NOT: store
|
; CHECK-NOT: store
|
||||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* %R)
|
call void @llvm.lifetime.end.p0i8(i64 4, i8* %R)
|
||||||
; CHECK: lifetime.end
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
; RUN: opt < %s -instcombine -instcombine-infinite-loop-threshold=2 -S | FileCheck %s
|
; RUN: opt < %s -instcombine -instcombine-infinite-loop-threshold=3 -S | FileCheck %s
|
||||||
|
|
||||||
%struct.__va_list = type { i8*, i8*, i8*, i32, i32 }
|
%struct.__va_list = type { i8*, i8*, i8*, i32, i32 }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user