Skip to content

Commit

Permalink
Re-commit: Mark values as trivially dead when their only use is a sta…
Browse files Browse the repository at this point in the history
…rt 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
  • Loading branch information
zoecarver committed May 8, 2020
1 parent 1aa8cef commit f65f566
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 11 deletions.
19 changes: 16 additions & 3 deletions llvm/lib/Transforms/Utils/Local.cpp
Expand Up @@ -403,9 +403,22 @@ bool llvm::wouldInstructionBeTriviallyDead(Instruction *I,
II->getIntrinsicID() == Intrinsic::launder_invariant_group)
return true;

// Lifetime intrinsics are dead when their right-hand is undef.
if (II->isLifetimeStartOrEnd())
return isa<UndefValue>(II->getArgOperand(1));
if (II->isLifetimeStartOrEnd()) {
auto *Arg = 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
// true are operationally no-ops. In the future we can consider more
Expand Down
1 change: 0 additions & 1 deletion llvm/test/Analysis/BasicAA/modref.ll
Expand Up @@ -80,7 +80,6 @@ define void @test3a(i8* %P, i8 %X) {

%P2 = getelementptr i8, i8* %P, i32 2
store i8 %Y, i8* %P2
; CHECK-NEXT: call void @llvm.lifetime.end
call void @llvm.lifetime.end.p0i8(i64 10, i8* %P)
ret void
; CHECK-NEXT: ret void
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/Transforms/Attributor/memory_locations.ll
Expand Up @@ -386,11 +386,11 @@ define void @callerD2() {
store i8 0, i8* %unknown
ret void
}
; CHECK: Function Attrs: argmemonly nounwind willreturn

; CHECK: Function Attrs: nofree {{(norecurse )?}}nosync nounwind readnone willreturn
define void @callerE(i8* %arg) {
; CHECK-LABEL: define {{[^@]+}}@callerE
; CHECK-SAME: (i8* nocapture [[ARG:%.*]])
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nocapture [[ARG]])
; CHECK-SAME: (i8* nocapture nofree readnone [[ARG:%.*]])
; CHECK-NEXT: ret void
;
call void @llvm.lifetime.start.p0i8(i64 4, i8* %arg)
Expand Down
58 changes: 58 additions & 0 deletions llvm/test/Transforms/DCE/basic.ll
Expand Up @@ -11,5 +11,63 @@ define void @test() {
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: [[sub]] = !DILocalVariable
4 changes: 1 addition & 3 deletions llvm/test/Transforms/DeadStoreElimination/lifetime.ll
Expand Up @@ -12,7 +12,7 @@ define void @test1() {

store i8 0, i8* %A ;; Written to by memset
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)
; CHECK-NOT: memset
Expand All @@ -26,11 +26,9 @@ define void @test2(i32* %P) {
%Q = getelementptr i32, i32* %P, i32 1
%R = bitcast i32* %Q to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %R)
; CHECK: lifetime.start
store i32 0, i32* %Q ;; This store is dead.
; CHECK-NOT: store
call void @llvm.lifetime.end.p0i8(i64 4, i8* %R)
; CHECK: lifetime.end
ret void
}

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/InstCombine/vararg.ll
@@ -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 }

Expand Down

0 comments on commit f65f566

Please sign in to comment.