diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 28746bf9d05aa..5946a1814faf1 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -26658,14 +26658,17 @@ Arguments: The first argument is a constant integer, which is ignored and will be removed in the future. -The second argument is a pointer to an ``alloca`` instruction. +The second argument is either a pointer to an ``alloca`` instruction or +a ``poison`` value. Semantics: """""""""" -The stack-allocated object that ``ptr`` points to is initially marked as dead. -After '``llvm.lifetime.start``', the stack object is marked as alive and has an -uninitialized value. +If ``ptr`` is a ``poison`` value, the intrinsic has no effect. + +Otherwise, the stack-allocated object that ``ptr`` points to is initially +marked as dead. After '``llvm.lifetime.start``', the stack object is marked as +alive and has an uninitialized value. The stack object is marked as dead when either :ref:`llvm.lifetime.end ` to the alloca is executed or the function returns. @@ -26699,13 +26702,16 @@ Arguments: The first argument is a constant integer, which is ignored and will be removed in the future. -The second argument is a pointer to an ``alloca`` instruction. +The second argument is either a pointer to an ``alloca`` instruction or +a ``poison`` value. Semantics: """""""""" -The stack-allocated object that ``ptr`` points to becomes dead after the call -to this intrinsic. +If ``ptr`` is a ``poison`` value, the intrinsic has no effect. + +Otherwise, the stack-allocated object that ``ptr`` points to becomes dead after +the call to this intrinsic. Calling ``llvm.lifetime.end`` on an already dead alloca is no-op. diff --git a/llvm/lib/Analysis/StackLifetime.cpp b/llvm/lib/Analysis/StackLifetime.cpp index 34a7a0416d290..b3f999400f154 100644 --- a/llvm/lib/Analysis/StackLifetime.cpp +++ b/llvm/lib/Analysis/StackLifetime.cpp @@ -63,7 +63,10 @@ bool StackLifetime::isAliveAfter(const AllocaInst *AI, // markers has the same size and points to the alloca start. static const AllocaInst *findMatchingAlloca(const IntrinsicInst &II, const DataLayout &DL) { - const AllocaInst *AI = cast(II.getArgOperand(1)); + const AllocaInst *AI = dyn_cast(II.getArgOperand(1)); + if (!AI) + return nullptr; + auto AllocaSize = AI->getAllocationSize(DL); if (!AllocaSize) return nullptr; diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp index fd38c308003b7..ab6fb3082ab7d 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -2189,8 +2189,8 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID, unsigned Op = ID == Intrinsic::lifetime_start ? TargetOpcode::LIFETIME_START : TargetOpcode::LIFETIME_END; - const AllocaInst *AI = cast(CI.getArgOperand(1)); - if (!AI->isStaticAlloca()) + const AllocaInst *AI = dyn_cast(CI.getArgOperand(1)); + if (!AI || !AI->isStaticAlloca()) return true; MIRBuilder.buildInstr(Op).addFrameIndex(getOrCreateFrameIndex(*AI)); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 306e068f1c1da..ac0440fef5f60 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -7598,7 +7598,9 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, if (TM.getOptLevel() == CodeGenOptLevel::None) return; - const AllocaInst *LifetimeObject = cast(I.getArgOperand(1)); + const AllocaInst *LifetimeObject = dyn_cast(I.getArgOperand(1)); + if (!LifetimeObject) + return; // First check that the Alloca is static, otherwise it won't have a // valid frame index. diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 3ff9895e161c4..ca3f148f881a4 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -6769,10 +6769,13 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) { break; } case Intrinsic::lifetime_start: - case Intrinsic::lifetime_end: - Check(isa(Call.getArgOperand(1)), - "llvm.lifetime.start/end can only be used on alloca", &Call); + case Intrinsic::lifetime_end: { + Value *Ptr = Call.getArgOperand(1); + Check(isa(Ptr) || isa(Ptr), + "llvm.lifetime.start/end can only be used on alloca or poison", + &Call); break; + } }; // Verify that there aren't any unmediated control transfers between funclets. diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index e87bee79a6a69..8da65c597116f 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -1222,9 +1222,9 @@ struct FunctionStackPoisoner : public InstVisitor { !ConstantInt::isValueValidForType(IntptrTy, SizeValue)) return; // Find alloca instruction that corresponds to llvm.lifetime argument. - AllocaInst *AI = cast(II.getArgOperand(1)); + AllocaInst *AI = dyn_cast(II.getArgOperand(1)); // We're interested only in allocas we can handle. - if (!ASan.isInterestingAlloca(*AI)) + if (!AI || !ASan.isInterestingAlloca(*AI)) return; bool DoPoison = (ID == Intrinsic::lifetime_end); AllocaPoisonCall APC = {&II, AI, SizeValue, DoPoison}; diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index 54d9a832d0f66..7d3c940c0065f 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -3301,8 +3301,9 @@ struct MemorySanitizerVisitor : public InstVisitor { void handleLifetimeStart(IntrinsicInst &I) { if (!PoisonStack) return; - AllocaInst *AI = cast(I.getArgOperand(1)); - LifetimeStartList.push_back(std::make_pair(&I, AI)); + AllocaInst *AI = dyn_cast(I.getArgOperand(1)); + if (AI) + LifetimeStartList.push_back(std::make_pair(&I, AI)); } void handleBswap(IntrinsicInst &I) { diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index babd7f6b3a058..3852f1aa40ac5 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -482,6 +482,9 @@ bool llvm::wouldInstructionBeTriviallyDead(const Instruction *I, if (II->isLifetimeStartOrEnd()) { auto *Arg = II->getArgOperand(1); + if (isa(Arg)) + return true; + // If the only uses of the alloca are lifetime intrinsics, then the // intrinsics are dead. return llvm::all_of(Arg->uses(), [](Use &Use) { diff --git a/llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp b/llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp index bea76d39bb216..472c03f7fc6ca 100644 --- a/llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp +++ b/llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp @@ -155,8 +155,9 @@ void StackInfoBuilder::visit(OptimizationRemarkEmitter &ORE, return; } if (auto *II = dyn_cast(&Inst)) { - AllocaInst *AI = cast(II->getArgOperand(1)); - if (getAllocaInterestingness(*AI) != AllocaInterestingness::kInteresting) + AllocaInst *AI = dyn_cast(II->getArgOperand(1)); + if (!AI || + getAllocaInterestingness(*AI) != AllocaInterestingness::kInteresting) return; if (II->getIntrinsicID() == Intrinsic::lifetime_start) Info.AllocasToInstrument[AI].LifetimeStart.push_back(II); diff --git a/llvm/test/CodeGen/AArch64/lifetime-poison.ll b/llvm/test/CodeGen/AArch64/lifetime-poison.ll new file mode 100644 index 0000000000000..e04530de528b5 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/lifetime-poison.ll @@ -0,0 +1,14 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -mtriple=aarch64 -global-isel=0 < %s | FileCheck %s +; RUN: llc -mtriple=aarch64 -global-isel=1 < %s | FileCheck %s + +; Check that lifetime.start/end with poison argument are ignored. + +define void @test() { +; CHECK-LABEL: test: +; CHECK: // %bb.0: +; CHECK-NEXT: ret + call void @llvm.lifetime.start.p0(i64 4, ptr poison) + call void @llvm.lifetime.end.p0(i64 4, ptr poison) + ret void +} diff --git a/llvm/test/Instrumentation/AddressSanitizer/lifetime.ll b/llvm/test/Instrumentation/AddressSanitizer/lifetime.ll index bbfe00bf9a0eb..959437001a039 100644 --- a/llvm/test/Instrumentation/AddressSanitizer/lifetime.ll +++ b/llvm/test/Instrumentation/AddressSanitizer/lifetime.ll @@ -334,6 +334,21 @@ entry: ret void } + +; Lifetimes on poison should be ignored. +define void @lifetime_poison(i64 %a) #0 { +; CHECK-LABEL: define void @lifetime_poison( +; CHECK-SAME: i64 [[A:%.*]]) { +; CHECK-NEXT: [[A_ADDR:%.*]] = alloca i64, align 8 +; CHECK-NEXT: store i64 [[A]], ptr [[A_ADDR]], align 8 +; CHECK-NEXT: ret void +; + %a.addr = alloca i64, align 8 + call void @llvm.lifetime.start.p0(i64 8, ptr poison) + store i64 %a, ptr %a.addr, align 8 + call void @llvm.lifetime.end.p0(i64 8, ptr poison) + ret void +} ;. ; CHECK-DEFAULT: [[PROF1]] = !{!"branch_weights", i32 1, i32 1048575} ;. diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll b/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll index e96ca91d139dd..60af551eb458b 100644 --- a/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll +++ b/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll @@ -395,6 +395,25 @@ entry: ret i32 0 } +; Check that lifetimes on poison are ignored. +define i32 @test_lifetime_poison(ptr %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_lifetime_poison + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_store + ; SAFETY-REMARKS: --- !Passed{{[[:space:]]}}Pass: hwasan{{[[:space:]]}}Name: safeAlloca{{[[:space:]]}}Function: test_lifetime_poison + ; SAFETY-REMARKS: --- !Passed{{[[:space:]]}}Pass: hwasan{{[[:space:]]}}Name: ignoreAccess{{[[:space:]]}}Function: test_lifetime_poison + %buf.sroa.0 = alloca i8, align 4 + call void @llvm.lifetime.start.p0(i64 1, ptr poison) + store volatile i8 0, ptr %buf.sroa.0, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0(i64 1, ptr poison) + ret i32 0 +} + ; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) diff --git a/llvm/test/Transforms/InstCombine/pr150338.ll b/llvm/test/Transforms/InstCombine/pr150338.ll deleted file mode 100644 index 2ad454ec60f13..0000000000000 --- a/llvm/test/Transforms/InstCombine/pr150338.ll +++ /dev/null @@ -1,16 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 -; RUN: opt -S -passes=instcombine < %s | FileCheck %s - -; Make sure this does not crash. -define void @test(ptr %arg) { -; CHECK-LABEL: define void @test( -; CHECK-SAME: ptr [[ARG:%.*]]) { -; CHECK-NEXT: store i1 true, ptr poison, align 1 -; CHECK-NEXT: ret void -; - %a = alloca i32 - store ptr %a, ptr %arg - store i1 true, ptr poison - call void @llvm.lifetime.end.p0(i64 4, ptr %a) - ret void -} diff --git a/llvm/test/Transforms/InstCombine/unreachable-alloca-lifetime-markers.ll b/llvm/test/Transforms/InstCombine/unreachable-alloca-lifetime-markers.ll new file mode 100644 index 0000000000000..ab744c6213e4b --- /dev/null +++ b/llvm/test/Transforms/InstCombine/unreachable-alloca-lifetime-markers.ll @@ -0,0 +1,51 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes=instcombine < %s | FileCheck %s + +; Make sure this does not crash. + +define void @pr150338(ptr %arg) { +; CHECK-LABEL: define void @pr150338( +; CHECK-SAME: ptr [[ARG:%.*]]) { +; CHECK-NEXT: store i1 true, ptr poison, align 1 +; CHECK-NEXT: ret void +; + %a = alloca i32 + store ptr %a, ptr %arg + store i1 true, ptr poison + call void @llvm.lifetime.end.p0(i64 4, ptr %a) + ret void +} + +define ptr @pr151119() { +; CHECK-LABEL: define ptr @pr151119() { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: store i1 false, ptr poison, align 1 +; CHECK-NEXT: br i1 false, label %[[BB1:.*]], label %[[BB2:.*]] +; CHECK: [[BB1]]: +; CHECK-NEXT: br label %[[BB2]] +; CHECK: [[BB2]]: +; CHECK-NEXT: br label %[[BB1]] +; +entry: + %a = alloca i32, align 4 + store i1 false, ptr poison + br i1 false, label %bb1, label %bb2 + +bb1: + %phi1 = phi ptr [ null, %entry ], [ %phi2, %bb2 ] + call void @llvm.lifetime.start.p0(i64 4, ptr %a) + br label %bb2 + +bb2: + %phi2 = phi ptr [ null, %entry ], [ %a, %bb1 ] + br label %bb1 +} + +define void @lifetime_poison() { +; CHECK-LABEL: define void @lifetime_poison() { +; CHECK-NEXT: ret void +; + call void @llvm.lifetime.start.p0(i64 4, ptr poison) + call void @llvm.lifetime.end.p0(i64 4, ptr poison) + ret void +}