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
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)
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
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
%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
}