From d7f3b6720f6a9014101f9adbac56f9cf7d18b4a8 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Sun, 15 Jun 2025 17:55:38 +0800 Subject: [PATCH] Preserve lifetime marker's order --- clang/lib/CodeGen/CGCall.cpp | 10 ++++---- .../lib/Transforms/Scalar/MemCpyOptimizer.cpp | 1 + llvm/lib/Transforms/Scalar/SROA.cpp | 6 ++++- llvm/lib/Transforms/Utils/InlineFunction.cpp | 23 +++++++++++-------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index a06455d25b1ef..9badbfc7a68c7 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -6057,6 +6057,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, Builder.CreateStore(errorResult, swiftErrorArg); } + // Explicitly call CallLifetimeEnd::Emit just to re-use the code even though + // we can't use the full cleanup mechanism. + for (CallLifetimeEnd &LifetimeEnd : reverse(CallLifetimeEndAfterCall)) + LifetimeEnd.Emit(*this, /*Flags=*/{}); + // Emit any call-associated writebacks immediately. Arguably this // should happen after any return-value munging. if (CallArgs.hasWritebacks()) @@ -6194,11 +6199,6 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, AllocAlignAttrEmitter.EmitAsAnAssumption(Loc, RetTy, Ret); } - // Explicitly call CallLifetimeEnd::Emit just to re-use the code even though - // we can't use the full cleanup mechanism. - for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall) - LifetimeEnd.Emit(*this, /*Flags=*/{}); - if (!ReturnValue.isExternallyDestructed() && RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct) pushDestroy(QualType::DK_nontrivial_c_struct, Ret.getAggregateAddress(), diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp index 1c4ec6aa08b43..05ccd74195bcc 100644 --- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -836,6 +836,7 @@ bool MemCpyOptPass::processMemSet(MemSetInst *MSI, BasicBlock::iterator &BBI) { /// Takes a memcpy and a call that it depends on, /// and checks for the possibility of a call slot optimization by having /// the call write its result directly into the destination of the memcpy. +/// FIXME: it breaks the order of lifetime markers. bool MemCpyOptPass::performCallSlotOptzn(Instruction *cpyLoad, Instruction *cpyStore, Value *cpyDest, Value *cpySrc, TypeSize cpySize, diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp index 42d1d9a437bb2..ba0bc66e36b33 100644 --- a/llvm/lib/Transforms/Scalar/SROA.cpp +++ b/llvm/lib/Transforms/Scalar/SROA.cpp @@ -2832,7 +2832,11 @@ class AllocaSliceRewriter : public InstVisitor { OldPtr = cast(OldUse->get()); Instruction *OldUserI = cast(OldUse->getUser()); - IRB.SetInsertPoint(OldUserI); + auto *OldUserII = dyn_cast(OldUserI); + if (OldUserII && OldUserII->getIntrinsicID() == Intrinsic::lifetime_end) + IRB.SetInsertPoint(OldUserI->getNextNode()); + else + IRB.SetInsertPoint(OldUserI); IRB.SetCurrentDebugLocation(OldUserI->getDebugLoc()); IRB.getInserter().SetNamePrefix(Twine(NewAI.getName()) + "." + Twine(BeginOffset) + "."); diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp index f47c467d15140..866ebb9321145 100644 --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -2977,6 +2977,7 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI, if ((InsertLifetime || Caller->isPresplitCoroutine()) && !IFI.StaticAllocas.empty()) { IRBuilder<> builder(&*FirstNewBlock, FirstNewBlock->begin()); + SmallVector> AllocasWithLifetimeEnd; for (AllocaInst *AI : IFI.StaticAllocas) { // Don't mark swifterror allocas. They can't have bitcast uses. if (AI->isSwiftError()) @@ -3012,17 +3013,19 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI, } builder.CreateLifetimeStart(AI, AllocaSize); - for (ReturnInst *RI : Returns) { - // Don't insert llvm.lifetime.end calls between a musttail or deoptimize - // call and a return. The return kills all local allocas. - if (InlinedMustTailCalls && - RI->getParent()->getTerminatingMustTailCall()) - continue; - if (InlinedDeoptimizeCalls && - RI->getParent()->getTerminatingDeoptimizeCall()) - continue; + AllocasWithLifetimeEnd.emplace_back(AI, AllocaSize); + } + + for (ReturnInst *RI : Returns) { + // Don't insert llvm.lifetime.end calls between a musttail or deoptimize + // call and a return. The return kills all local allocas. + if (InlinedMustTailCalls && RI->getParent()->getTerminatingMustTailCall()) + continue; + if (InlinedDeoptimizeCalls && + RI->getParent()->getTerminatingDeoptimizeCall()) + continue; + for (auto [AI, AllocaSize] : reverse(AllocasWithLifetimeEnd)) IRBuilder<>(RI).CreateLifetimeEnd(AI, AllocaSize); - } } }