https://github.com/NewSigma updated https://github.com/llvm/llvm-project/pull/144319
>From 6322fa52e33cad77a59b685bf6cf8e6f218eb7e8 Mon Sep 17 00:00:00 2001 From: NewSigma <newsi...@163.com> Date: Fri, 25 Jul 2025 21:13:04 +0800 Subject: [PATCH 1/2] Rebase --- clang/test/CodeGenCoroutines/pr56919.cpp | 6 +- llvm/docs/Coroutines.rst | 6 +- llvm/include/llvm/IR/Intrinsics.td | 6 +- .../llvm/Transforms/Utils/LifetimeMove.h | 23 ++ llvm/lib/Passes/PassBuilder.cpp | 1 + llvm/lib/Passes/PassBuilderPipelines.cpp | 5 + llvm/lib/Passes/PassRegistry.def | 1 + llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 87 ----- llvm/lib/Transforms/Utils/CMakeLists.txt | 1 + llvm/lib/Transforms/Utils/LifetimeMove.cpp | 341 ++++++++++++++++++ llvm/test/Other/new-pm-defaults.ll | 1 + llvm/test/Other/new-pm-pgo-preinline.ll | 1 + .../Other/new-pm-thinlto-postlink-defaults.ll | 1 + .../new-pm-thinlto-postlink-pgo-defaults.ll | 1 + ...-pm-thinlto-postlink-samplepgo-defaults.ll | 1 + .../Other/new-pm-thinlto-prelink-defaults.ll | 1 + .../new-pm-thinlto-prelink-pgo-defaults.ll | 2 + ...w-pm-thinlto-prelink-samplepgo-defaults.ll | 1 + .../Transforms/Coroutines/coro-alloca-07.ll | 4 +- .../Coroutines/coro-split-rise-lifetime-01.ll | 39 ++ .../Coroutines/coro-split-sink-lifetime-01.ll | 7 +- .../Coroutines/coro-split-sink-lifetime-02.ll | 5 +- .../Coroutines/coro-split-sink-lifetime-03.ll | 5 +- .../Coroutines/coro-split-sink-lifetime-04.ll | 7 +- llvm/test/Transforms/LifetimeMove/erase.ll | 62 ++++ llvm/test/Transforms/LifetimeMove/escape.ll | 58 +++ .../Transforms/LifetimeMove/indirect_start.ll | 23 ++ llvm/test/Transforms/LifetimeMove/loop.ll | 84 +++++ .../Transforms/LifetimeMove/multi_critical.ll | 79 ++++ 29 files changed, 748 insertions(+), 111 deletions(-) create mode 100644 llvm/include/llvm/Transforms/Utils/LifetimeMove.h create mode 100644 llvm/lib/Transforms/Utils/LifetimeMove.cpp create mode 100644 llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll create mode 100644 llvm/test/Transforms/LifetimeMove/erase.ll create mode 100644 llvm/test/Transforms/LifetimeMove/escape.ll create mode 100644 llvm/test/Transforms/LifetimeMove/indirect_start.ll create mode 100644 llvm/test/Transforms/LifetimeMove/loop.ll create mode 100644 llvm/test/Transforms/LifetimeMove/multi_critical.ll diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp b/clang/test/CodeGenCoroutines/pr56919.cpp index baa8c27ce6649..e709cecf6d93a 100644 --- a/clang/test/CodeGenCoroutines/pr56919.cpp +++ b/clang/test/CodeGenCoroutines/pr56919.cpp @@ -111,15 +111,15 @@ Task<void> Bar() { co_await Baz(); } // CHECK: _Z3Quxv.destroy:{{.*}} // CHECK-NEXT: # -// CHECK-NEXT: movl $40, %esi +// CHECK-NEXT: movl $32, %esi // CHECK-NEXT: jmp _ZdlPvm@PLT // CHECK: _Z3Bazv.destroy:{{.*}} // CHECK-NEXT: # -// CHECK-NEXT: movl $80, %esi +// CHECK-NEXT: movl $64, %esi // CHECK-NEXT: jmp _ZdlPvm // CHECK: _Z3Barv.destroy:{{.*}} // CHECK-NEXT: # -// CHECK-NEXT: movl $120, %esi +// CHECK-NEXT: movl $96, %esi // CHECK-NEXT: jmp _ZdlPvm diff --git a/llvm/docs/Coroutines.rst b/llvm/docs/Coroutines.rst index dde73c9c3cc23..e6788141f79e0 100644 --- a/llvm/docs/Coroutines.rst +++ b/llvm/docs/Coroutines.rst @@ -1863,7 +1863,7 @@ executes a call to ``llvm.coro.suspend.retcon`` after resuming in any way. :: declare void @llvm.coro.await.suspend.void( - ptr <awaiter>, + ptr captures(none) <awaiter>, ptr <handle>, ptr <await_suspend_function>) @@ -1945,7 +1945,7 @@ Example: :: declare i1 @llvm.coro.await.suspend.bool( - ptr <awaiter>, + ptr captures(none) <awaiter>, ptr <handle>, ptr <await_suspend_function>) @@ -2035,7 +2035,7 @@ Example: :: declare void @llvm.coro.await.suspend.handle( - ptr <awaiter>, + ptr captures(none) <awaiter>, ptr <handle>, ptr <await_suspend_function>) diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index e0ee12391b31d..46ac0d95740d0 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1808,15 +1808,15 @@ def int_coro_promise : Intrinsic<[llvm_ptr_ty], def int_coro_await_suspend_void : Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty], - [Throws]>; + [Throws, NoCapture<ArgIndex<0>>]>; def int_coro_await_suspend_bool : Intrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty], - [Throws]>; + [Throws, NoCapture<ArgIndex<0>>]>; def int_coro_await_suspend_handle : Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty], - [Throws]>; + [Throws, NoCapture<ArgIndex<0>>]>; // Coroutine Lowering Intrinsics. Used internally by coroutine passes. diff --git a/llvm/include/llvm/Transforms/Utils/LifetimeMove.h b/llvm/include/llvm/Transforms/Utils/LifetimeMove.h new file mode 100644 index 0000000000000..f9a690433cb77 --- /dev/null +++ b/llvm/include/llvm/Transforms/Utils/LifetimeMove.h @@ -0,0 +1,23 @@ +//===- LifetimeMove.h - Narrowing lifetimes ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H +#define LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +struct LifetimeMovePass : PassInfoMixin<LifetimeMovePass> { + /// Run the pass over the function. + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index f810368a84940..389e10fe258c6 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -353,6 +353,7 @@ #include "llvm/Transforms/Utils/InjectTLIMappings.h" #include "llvm/Transforms/Utils/InstructionNamer.h" #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h" +#include "llvm/Transforms/Utils/LifetimeMove.h" #include "llvm/Transforms/Utils/LoopSimplify.h" #include "llvm/Transforms/Utils/LoopVersioning.h" #include "llvm/Transforms/Utils/LowerGlobalDtors.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index 98821bb1408a7..4740e9be08948 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -137,6 +137,7 @@ #include "llvm/Transforms/Utils/ExtraPassManager.h" #include "llvm/Transforms/Utils/InjectTLIMappings.h" #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h" +#include "llvm/Transforms/Utils/LifetimeMove.h" #include "llvm/Transforms/Utils/Mem2Reg.h" #include "llvm/Transforms/Utils/MoveAutoInit.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h" @@ -557,6 +558,7 @@ PassBuilder::buildO1FunctionSimplificationPipeline(OptimizationLevel Level, FPM.addPass(ADCEPass()); FPM.addPass( SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true))); + FPM.addPass(LifetimeMovePass()); FPM.addPass(InstCombinePass()); invokePeepholeEPCallbacks(FPM, Level); @@ -777,6 +779,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level, .convertSwitchRangeToICmp(true) .hoistCommonInsts(true) .sinkCommonInsts(true))); + FPM.addPass(LifetimeMovePass()); FPM.addPass(InstCombinePass()); invokePeepholeEPCallbacks(FPM, Level); @@ -813,6 +816,7 @@ void PassBuilder::addPreInlinerPasses(ModulePassManager &MPM, FPM.addPass(EarlyCSEPass()); // Catch trivial redundancies. FPM.addPass(SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp( true))); // Merge & remove basic blocks. + FPM.addPass(LifetimeMovePass()); FPM.addPass(InstCombinePass()); // Combine silly sequences. invokePeepholeEPCallbacks(FPM, Level); @@ -1356,6 +1360,7 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level, /*UseBlockFrequencyInfo=*/true)); ExtraPasses.addPass( SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true))); + ExtraPasses.addPass(LifetimeMovePass()); ExtraPasses.addPass(InstCombinePass()); FPM.addPass(std::move(ExtraPasses)); } diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 1b111dc20d35c..ec58773be397d 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -457,6 +457,7 @@ FUNCTION_PASS("kcfi", KCFIPass()) FUNCTION_PASS("kernel-info", KernelInfoPrinter(TM)) FUNCTION_PASS("lcssa", LCSSAPass()) FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrinkWrapPass()) +FUNCTION_PASS("lifetime-move", LifetimeMovePass()) FUNCTION_PASS("load-store-vectorizer", LoadStoreVectorizerPass()) FUNCTION_PASS("loop-data-prefetch", LoopDataPrefetchPass()) FUNCTION_PASS("loop-distribute", LoopDistributePass()) diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp index b775c43460190..95a6d708bb78e 100644 --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -1751,89 +1751,6 @@ static void eliminateSwiftError(Function &F, coro::Shape &Shape) { } } -/// For each local variable that all of its user are only used inside one of -/// suspended region, we sink their lifetime.start markers to the place where -/// after the suspend block. Doing so minimizes the lifetime of each variable, -/// hence minimizing the amount of data we end up putting on the frame. -static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape, - SuspendCrossingInfo &Checker, - const DominatorTree &DT) { - if (F.hasOptNone()) - return; - - // Collect all possible basic blocks which may dominate all uses of allocas. - SmallPtrSet<BasicBlock *, 4> DomSet; - DomSet.insert(&F.getEntryBlock()); - for (auto *CSI : Shape.CoroSuspends) { - BasicBlock *SuspendBlock = CSI->getParent(); - assert(coro::isSuspendBlock(SuspendBlock) && - SuspendBlock->getSingleSuccessor() && - "should have split coro.suspend into its own block"); - DomSet.insert(SuspendBlock->getSingleSuccessor()); - } - - for (Instruction &I : instructions(F)) { - AllocaInst* AI = dyn_cast<AllocaInst>(&I); - if (!AI) - continue; - - for (BasicBlock *DomBB : DomSet) { - bool Valid = true; - SmallVector<Instruction *, 1> Lifetimes; - - auto isLifetimeStart = [](Instruction* I) { - if (auto* II = dyn_cast<IntrinsicInst>(I)) - return II->getIntrinsicID() == Intrinsic::lifetime_start; - return false; - }; - - auto collectLifetimeStart = [&](Instruction *U, AllocaInst *AI) { - if (isLifetimeStart(U)) { - Lifetimes.push_back(U); - return true; - } - if (!U->hasOneUse() || U->stripPointerCasts() != AI) - return false; - if (isLifetimeStart(U->user_back())) { - Lifetimes.push_back(U->user_back()); - return true; - } - return false; - }; - - for (User *U : AI->users()) { - Instruction *UI = cast<Instruction>(U); - // For all users except lifetime.start markers, if they are all - // dominated by one of the basic blocks and do not cross - // suspend points as well, then there is no need to spill the - // instruction. - if (!DT.dominates(DomBB, UI->getParent()) || - Checker.isDefinitionAcrossSuspend(DomBB, UI)) { - // Skip lifetime.start, GEP and bitcast used by lifetime.start - // markers. - if (collectLifetimeStart(UI, AI)) - continue; - Valid = false; - break; - } - } - // Sink lifetime.start markers to dominate block when they are - // only used outside the region. - if (Valid && Lifetimes.size() != 0) { - auto *NewLifetime = Lifetimes[0]->clone(); - NewLifetime->replaceUsesOfWith(NewLifetime->getOperand(0), AI); - NewLifetime->insertBefore(DomBB->getTerminator()->getIterator()); - - // All the outsided lifetime.start markers are no longer necessary. - for (Instruction *S : Lifetimes) - S->eraseFromParent(); - - break; - } - } - } -} - static std::optional<std::pair<Value &, DIExpression &>> salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap, bool UseEntryValue, Function *F, Value *Storage, @@ -2012,10 +1929,6 @@ void coro::BaseABI::buildCoroutineFrame(bool OptimizeFrame) { doRematerializations(F, Checker, IsMaterializable); const DominatorTree DT(F); - if (Shape.ABI != coro::ABI::Async && Shape.ABI != coro::ABI::Retcon && - Shape.ABI != coro::ABI::RetconOnce) - sinkLifetimeStartMarkers(F, Shape, Checker, DT); - // All values (that are not allocas) that needs to be spilled to the frame. coro::SpillInfo Spills; // All values defined as allocas that need to live in the frame. diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt index e411d68570096..3c211b6f7a4dc 100644 --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -41,6 +41,7 @@ add_llvm_component_library(LLVMTransformUtils IRNormalizer.cpp LCSSA.cpp LibCallsShrinkWrap.cpp + LifetimeMove.cpp Local.cpp LoopConstrainer.cpp LoopPeel.cpp diff --git a/llvm/lib/Transforms/Utils/LifetimeMove.cpp b/llvm/lib/Transforms/Utils/LifetimeMove.cpp new file mode 100644 index 0000000000000..f23f7f3ff4568 --- /dev/null +++ b/llvm/lib/Transforms/Utils/LifetimeMove.cpp @@ -0,0 +1,341 @@ +//===- LifetimeMove.cpp - Narrowing lifetimes -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The LifetimeMovePass identifies the precise lifetime range of allocas and +// repositions lifetime markers to stricter positions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/LifetimeMove.h" +#include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/CaptureTracking.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/Analysis/PtrUseVisitor.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Transforms/Coroutines/CoroInstr.h" + +#define DEBUG_TYPE "lifetime-move" + +namespace llvm { +namespace { +class LifetimeMover : public PtrUseVisitor<LifetimeMover> { + using This = LifetimeMover; + using Base = PtrUseVisitor<LifetimeMover>; + + const DominatorTree &DT; + const LoopInfo &LI; + + SmallVector<AllocaInst *, 4> Allocas; + // Critical points are instructions where the crossing of a variable's + // lifetime makes a difference. We attempt to rise lifetime.end + // before critical points and sink lifetime.start after them. + SmallVector<Instruction *, 4> CriticalPoints; + + SmallVector<Instruction *, 2> LifetimeStarts; + SmallVector<Instruction *, 2> LifetimeEnds; + SmallVector<Instruction *, 8> OtherUsers; + SmallPtrSet<BasicBlock *, 2> LifetimeStartBBs; + SmallPtrSet<BasicBlock *, 2> UserBBs; + +public: + LifetimeMover(Function &F, const DominatorTree &DT, const LoopInfo &LI); + + bool run(); + + void visitInstruction(Instruction &I); + void visitPHINode(PHINode &I); + void visitSelectInst(SelectInst &I); + void visitStoreInst(StoreInst &SI); + void visitIntrinsicInst(IntrinsicInst &II); + void visitMemIntrinsic(MemIntrinsic &I); + void visitCallBase(CallBase &CB); + +private: + bool sinkLifetimeStartMarkers(AllocaInst *AI); + bool riseLifetimeEndMarkers(); + void reset(); +}; +} // namespace + +LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT, + const LoopInfo &LI) + : Base(F.getDataLayout()), DT(DT), LI(LI) { + for (Instruction &I : instructions(F)) { + if (auto *AI = dyn_cast<AllocaInst>(&I)) + Allocas.push_back(AI); + else if (isa<LifetimeIntrinsic>(I)) + continue; + else if (isa<AnyCoroSuspendInst>(I)) + CriticalPoints.push_back(&I); + else if (isa<CallInst>(I)) + CriticalPoints.push_back(&I); + else if (isa<InvokeInst>(I)) + CriticalPoints.push_back(&I); + } +} + +bool LifetimeMover::run() { + bool Changed = false; + for (auto *AI : Allocas) { + reset(); + Base::visitPtr(*AI); + + if (!LifetimeStarts.empty()) + Changed |= sinkLifetimeStartMarkers(AI); + + // Do not move lifetime.end if alloca escapes + if (!LifetimeEnds.empty() && !PI.isEscaped()) + Changed |= riseLifetimeEndMarkers(); + } + return Changed; +} + +void LifetimeMover::visitInstruction(Instruction &I) { + OtherUsers.push_back(&I); + UserBBs.insert(I.getParent()); +} + +void LifetimeMover::visitPHINode(PHINode &I) { enqueueUsers(I); } + +void LifetimeMover::visitSelectInst(SelectInst &I) { enqueueUsers(I); } + +void LifetimeMover::visitStoreInst(StoreInst &SI) { + if (SI.getPointerOperand() == U->get()) + return InstVisitor<This>::visitStoreInst(SI); + + // We are storing the pointer into a memory location, potentially escaping. + // As an optimization, we try to detect simple cases where it doesn't + // actually escape, for example: + // %ptr = alloca .. + // %addr = alloca .. + // store %ptr, %addr + // %x = load %addr + // .. + // If %addr is only used by loading from it, we could simply treat %x as + // another alias of %ptr, and not considering %ptr being escaped. + auto IsSimpleStoreThenLoad = [&]() { + auto *AI = dyn_cast<AllocaInst>(SI.getPointerOperand()); + // If the memory location we are storing to is not an alloca, it + // could be an alias of some other memory locations, which is difficult + // to analyze. + if (!AI) + return false; + // StoreAliases contains aliases of the memory location stored into. + SmallVector<Instruction *, 4> StoreAliases = {AI}; + while (!StoreAliases.empty()) { + Instruction *I = StoreAliases.pop_back_val(); + for (User *U : I->users()) { + // If we are loading from the memory location, we are creating an + // alias of the original pointer. + if (auto *LI = dyn_cast<LoadInst>(U)) { + enqueueUsers(*LI); + continue; + } + // If we are overriding the memory location, the pointer certainly + // won't escape. + if (auto *S = dyn_cast<StoreInst>(U)) + if (S->getPointerOperand() == I) + continue; + if (isa<LifetimeIntrinsic>(U)) + continue; + // BitCastInst creats aliases of the memory location being stored + // into. + if (auto *BI = dyn_cast<BitCastInst>(U)) { + StoreAliases.push_back(BI); + continue; + } + return false; + } + } + + return true; + }; + + if (!IsSimpleStoreThenLoad()) + PI.setEscaped(&SI); + InstVisitor<This>::visitStoreInst(SI); +} + +void LifetimeMover::visitIntrinsicInst(IntrinsicInst &II) { + // When we found the lifetime markers refers to a + // subrange of the original alloca, ignore the lifetime + // markers to avoid misleading the analysis. + if (!IsOffsetKnown || !Offset.isZero()) + return Base::visitIntrinsicInst(II); + + // lifetime markers are not actual uses + switch (II.getIntrinsicID()) { + case Intrinsic::lifetime_start: + LifetimeStarts.push_back(&II); + LifetimeStartBBs.insert(II.getParent()); + break; + case Intrinsic::lifetime_end: + LifetimeEnds.push_back(&II); + break; + default: + Base::visitIntrinsicInst(II); + } +} + +void LifetimeMover::visitMemIntrinsic(MemIntrinsic &I) { visitInstruction(I); } + +void LifetimeMover::visitCallBase(CallBase &CB) { + for (unsigned Op = 0, OpCount = CB.arg_size(); Op < OpCount; ++Op) + if (U->get() == CB.getArgOperand(Op) && !CB.doesNotCapture(Op)) + PI.setEscaped(&CB); + InstVisitor<This>::visitCallBase(CB); +} +/// For each local variable that all of its user are dominated by one of the +/// critical point, we sink their lifetime.start markers to the place where +/// after the critical point. Doing so minimizes the lifetime of each variable. +bool LifetimeMover::sinkLifetimeStartMarkers(AllocaInst *AI) { + auto Update = [this](Instruction *Old, Instruction *New) { + // Reject the new proposal if it lengthens lifetime + if (DT.dominates(New, Old)) + return Old; + + if (LI.getLoopFor(New->getParent())) + return Old; + + bool DomAll = llvm::all_of(UserBBs, [this, New](BasicBlock *UserBB) { + // Instruction level analysis if lifetime and users share a common BB + BasicBlock *NewBB = New->getParent(); + if (UserBB == NewBB) { + return llvm::all_of(OtherUsers, [New, UserBB](Instruction *I) { + return UserBB != I->getParent() || New->comesBefore(I); + }); + } + // Otherwise, BB level analysis is enough + return DT.dominates(New, UserBB); + }); + return DomAll ? New : Old; + }; + + // AllocaInst is a trivial critical point + Instruction *DomPoint = AI; + for (auto *P : CriticalPoints) + DomPoint = Update(DomPoint, P); + + // Sink lifetime.start markers to dominate block when they are + // only used outside the region. + if (DomPoint != AI) { + // If existing position is better, do nothing + for (auto *P : LifetimeStarts) { + if (P == Update(DomPoint, P)) + return false; + } + + auto *NewStart = LifetimeStarts[0]->clone(); + NewStart->replaceUsesOfWith(NewStart->getOperand(1), AI); + if (DomPoint->isTerminator()) + NewStart->insertBefore( + cast<InvokeInst>(DomPoint)->getNormalDest()->getFirstNonPHIIt()); + else + NewStart->insertAfter(DomPoint->getIterator()); + + // All the outsided lifetime.start markers are no longer necessary. + for (auto *I : LifetimeStarts) { + if (LI.getLoopFor(I->getParent())) + continue; + + bool Restart = llvm::any_of(LifetimeEnds, [this, I](Instruction *End) { + return isPotentiallyReachable(End, I, &LifetimeStartBBs, &DT, &LI); + }); + + if (!Restart) { + LifetimeStartBBs.erase(I->getParent()); + I->eraseFromParent(); + } + } + return true; + } + return false; +} +// Find the critical point that is dominated by all users of alloca, +// we will rise lifetime.end markers before the critical point. +bool LifetimeMover::riseLifetimeEndMarkers() { + auto Update = [this](Instruction *Old, Instruction *New) { + if (Old != nullptr && DT.dominates(Old, New)) + return Old; + + if (LI.getLoopFor(New->getParent())) + return Old; + + bool DomAll = llvm::all_of(UserBBs, [this, New](BasicBlock *UserBB) { + BasicBlock *NewBB = New->getParent(); + if (UserBB == NewBB) { + return llvm::all_of(OtherUsers, [New, UserBB](Instruction *I) { + return UserBB != I->getParent() || I->comesBefore(New); + }); + } + + if (auto *L = LI.getLoopFor(UserBB)) { + SmallVector<BasicBlock *, 2> EBs; + L->getOutermostLoop()->getExitingBlocks(EBs); + return llvm::all_of(EBs, [this, NewBB](BasicBlock *EB) { + return DT.dominates(EB, NewBB); + }); + } + + return DT.dominates(UserBB, NewBB); + }); + return DomAll ? New : Old; + }; + + Instruction *DomPoint = nullptr; + for (auto *P : CriticalPoints) + DomPoint = Update(DomPoint, P); + + if (DomPoint != nullptr) { + for (auto *P : LifetimeEnds) { + if (P == Update(DomPoint, P)) + return false; + } + + auto *NewEnd = LifetimeEnds[0]->clone(); + NewEnd->insertBefore(DomPoint->getIterator()); + + for (auto *I : LifetimeEnds) + if (!LI.getLoopFor(I->getParent())) + I->eraseFromParent(); + return true; + } + return false; +} + +void LifetimeMover::reset() { + PI.reset(); + Worklist.clear(); + VisitedUses.clear(); + + LifetimeStarts.clear(); + LifetimeEnds.clear(); + OtherUsers.clear(); + LifetimeStartBBs.clear(); + UserBBs.clear(); +} + +PreservedAnalyses LifetimeMovePass::run(Function &F, + FunctionAnalysisManager &AM) { + // FIXME: Only enable by default for coroutines for now + if (!F.isPresplitCoroutine()) + return PreservedAnalyses::all(); + + const DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(F); + const LoopInfo &LI = AM.getResult<LoopAnalysis>(F); + LifetimeMover Mover(F, DT, LI); + if (!Mover.run()) + return PreservedAnalyses::all(); + + PreservedAnalyses PA; + PA.preserveSet<CFGAnalyses>(); + return PA; +} +} // namespace llvm diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll index c554fdbf4c799..c0d53b5d6af46 100644 --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -225,6 +225,7 @@ ; CHECK-O23SZ-NEXT: Running pass: CoroElidePass ; CHECK-EP-SCALAR-LATE-NEXT: Running pass: NoOpFunctionPass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass +; CHECK-O-NEXT: Running pass: LifetimeMovePass ; CHECK-O-NEXT: Running pass: InstCombinePass ; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass diff --git a/llvm/test/Other/new-pm-pgo-preinline.ll b/llvm/test/Other/new-pm-pgo-preinline.ll index f07a3728ba3d4..38259274328e0 100644 --- a/llvm/test/Other/new-pm-pgo-preinline.ll +++ b/llvm/test/Other/new-pm-pgo-preinline.ll @@ -12,6 +12,7 @@ ; CHECK-Osz-NEXT: Running pass: SROAPass on foo ; CHECK-Osz-NEXT: Running pass: EarlyCSEPass on foo ; CHECK-Osz-NEXT: Running pass: SimplifyCFGPass on foo +; CHECK-Osz-NEXT: Running pass: LifetimeMovePass on foo ; CHECK-Osz-NEXT: Running pass: InstCombinePass on foo ; CHECK-Osz-NEXT: Invalidating analysis: InlineAdvisorAnalysis ; CHECK-Osz-NEXT: Running pass: GlobalDCEPass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll index 62bb02d9b3c40..6230befa0930f 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll @@ -149,6 +149,7 @@ ; CHECK-O23SZ-NEXT: Running pass: LICMPass on loop ; CHECK-O23SZ-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass +; CHECK-O-NEXT: Running pass: LifetimeMovePass ; CHECK-O-NEXT: Running pass: InstCombinePass ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll index 0da7a9f73bdce..0bdf7d18d7362 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll @@ -133,6 +133,7 @@ ; CHECK-O23SZ-NEXT: Running pass: LICMPass ; CHECK-O23SZ-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass +; CHECK-O-NEXT: Running pass: LifetimeMovePass ; CHECK-O-NEXT: Running pass: InstCombinePass ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll index 38b7890682783..7dfca26864b90 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll @@ -142,6 +142,7 @@ ; CHECK-O23SZ-NEXT: Running pass: LICMPass ; CHECK-O23SZ-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass +; CHECK-O-NEXT: Running pass: LifetimeMovePass ; CHECK-O-NEXT: Running pass: InstCombinePass ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll index 5aacd26def2be..3961315c89ba7 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll @@ -181,6 +181,7 @@ ; CHECK-O23SZ-NEXT: Running pass: LICMPass on loop ; CHECK-O23SZ-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass +; CHECK-O-NEXT: Running pass: LifetimeMovePass ; CHECK-O-NEXT: Running pass: InstCombinePass ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis diff --git a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll index f6a9406596803..3b503f338e3b2 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll @@ -69,6 +69,7 @@ ; CHECK-O-NEXT: Running pass: SROAPass on foo ; CHECK-O-NEXT: Running pass: EarlyCSEPass on foo ; CHECK-O-NEXT: Running pass: SimplifyCFGPass on foo +; CHECK-O-NEXT: Running pass: LifetimeMovePass on foo ; CHECK-O-NEXT: Running pass: InstCombinePass on foo ; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis ; CHECK-O-NEXT: Running pass: GlobalDCEPass @@ -182,6 +183,7 @@ ; CHECK-O23SZ-NEXT: Running pass: LICMPass ; CHECK-O23SZ-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass +; CHECK-O-NEXT: Running pass: LifetimeMovePass ; CHECK-O-NEXT: Running pass: InstCombinePass ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis diff --git a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll index 48a9433d24999..545bdc9154ab1 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll @@ -146,6 +146,7 @@ ; CHECK-O23SZ-NEXT: Running pass: LICMPass ; CHECK-O23SZ-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass +; CHECK-O-NEXT: Running pass: LifetimeMovePass ; CHECK-O-NEXT: Running pass: InstCombinePass ; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ShouldNotRunFunctionPassesAnalysis diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-07.ll b/llvm/test/Transforms/Coroutines/coro-alloca-07.ll index 8bfb8cfabbd27..adf2b19935df1 100644 --- a/llvm/test/Transforms/Coroutines/coro-alloca-07.ll +++ b/llvm/test/Transforms/Coroutines/coro-alloca-07.ll @@ -1,6 +1,6 @@ ; Tests that CoroSplit can succesfully determine allocas should live on the frame ; if their aliases are used across suspension points through PHINode. -; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s +; RUN: opt < %s -passes='cgscc(lifetime-move,coro-split),simplifycfg,early-cse' -S | FileCheck %s define ptr @f(i1 %n) presplitcoroutine { entry: @@ -31,6 +31,8 @@ resume: br label %cleanup cleanup: + call void @llvm.lifetime.end.p0(i64 8, ptr %x) + call void @llvm.lifetime.end.p0(i64 8, ptr %y) %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) call void @free(ptr %mem) br label %suspend diff --git a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll new file mode 100644 index 0000000000000..967221baf57cd --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll @@ -0,0 +1,39 @@ +; Test lifetime-move and coro-split correctly optimize allocas that do not cross suspension points +; RUN: opt < %s -passes='cgscc(lifetime-move,coro-split),early-cse' -S | FileCheck %s + +; CHECK: %large.alloca = alloca [500 x i8], align 16 +; CHECK-NOT: %large.alloca.reload.addr + +define void @f() presplitcoroutine { +entry: + %large.alloca = alloca [500 x i8], align 16 + %id = call token @llvm.coro.id(i32 16, ptr null, ptr null, ptr null) + %size = call i32 @llvm.coro.size.i32() + %mem = call ptr @malloc(i32 %size) + %hdl = call ptr @llvm.coro.begin(token %id, ptr %mem) + call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca) + %value = load i8, ptr %large.alloca, align 1 + call void @consume(i8 %value) + %0 = call i8 @llvm.coro.suspend(token none, i1 false) + switch i8 %0, label %exit [ + i8 0, label %suspend + i8 1, label %cleanup + ] + +suspend: + br label %cleanup + +cleanup: + call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca) + %1 = call ptr @llvm.coro.free(token %id, ptr %mem) + call void @free(ptr %mem) + br label %exit + +exit: + %2 = call i1 @llvm.coro.end(ptr null, i1 false, token none) + ret void +} + +declare void @consume(i8) +declare ptr @malloc(i32) +declare void @free(ptr) diff --git a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-01.ll b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-01.ll index a5a2bcf2ecb81..ea2dffa39eed8 100644 --- a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-01.ll +++ b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-01.ll @@ -1,6 +1,5 @@ -; Tests that coro-split will optimize the lifetime.start maker of each local variable, -; sink them to the places after the suspend block. -; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s +; Test lifetime-move and coro-split correctly optimize allocas that do not cross suspension points +; RUN: opt < %s -passes='cgscc(lifetime-move,coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s ; CHECK: %a.Frame = type { ptr, ptr, i1 } ; CHECK: %a_optnone.Frame = type { ptr, ptr, i32, i1 } @@ -46,8 +45,8 @@ exit: ; CHECK: call void @llvm.lifetime.start.p0(ptr %testval) ; CHECK-NEXT: %val = load i32, ptr %ref.tmp7 ; CHECK-NEXT: %test = load i32, ptr %testval -; CHECK-NEXT: call void @print(i32 %test) ; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %testval) +; CHECK-NEXT: call void @print(i32 %test) ; CHECK-NEXT: call void @print(i32 %val) ; CHECK-NEXT: ret void diff --git a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-02.ll b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-02.ll index abc91c3b11c6b..71b15f2331a0f 100644 --- a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-02.ll +++ b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-02.ll @@ -1,6 +1,5 @@ -; Tests that coro-split will optimize the lifetime.start maker of each local variable, -; sink them to the places after the suspend block. -; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s +; Test lifetime-move and coro-split correctly optimize allocas that do not cross suspension points +; RUN: opt < %s -passes='cgscc(lifetime-move,coro-split),simplifycfg,early-cse' -S | FileCheck %s %"struct.std::coroutine_handle" = type { ptr } %"struct.std::coroutine_handle.0" = type { %"struct.std::coroutine_handle" } diff --git a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-03.ll b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-03.ll index efd1adfc54b53..fb5e1b2054aa0 100644 --- a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-03.ll +++ b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-03.ll @@ -1,6 +1,5 @@ -; Corresponding to coro-split-sink-lifetime-01.ll. This file tests that whether the CoroFrame -; pass knows the operand of lifetime.start intrinsic may be GEP as well. -; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s +; Test lifetime-move and coro-split correctly optimize allocas that do not cross suspension points +; RUN: opt < %s -passes='cgscc(lifetime-move,coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s %"struct.std::coroutine_handle" = type { ptr } %"struct.std::coroutine_handle.0" = type { %"struct.std::coroutine_handle" } diff --git a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-04.ll b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-04.ll index af5aa8ade0b65..e2db64f0a5729 100644 --- a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-04.ll +++ b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-04.ll @@ -1,6 +1,5 @@ -; Tests that coro-split will optimize the lifetime.start maker of each local variable, -; sink them to the places after the suspend block. -; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s +; Test lifetime-move and coro-split correctly optimize allocas that do not cross suspension points +; RUN: opt < %s -passes='cgscc(lifetime-move,coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s %"struct.std::coroutine_handle" = type { ptr } %"struct.std::coroutine_handle.0" = type { %"struct.std::coroutine_handle" } @@ -44,8 +43,8 @@ exit: ; CHECK: call void @llvm.lifetime.start.p0(ptr %testval) ; CHECK-NEXT: %val = load i32, ptr %ref.tmp7 ; CHECK-NEXT: %test = load i8, ptr %testval -; CHECK-NEXT: call void @consume.i8(i8 %test) ; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %testval) +; CHECK-NEXT: call void @consume.i8(i8 %test) ; CHECK-NEXT: call void @print(i32 %val) ; CHECK-NEXT: ret void diff --git a/llvm/test/Transforms/LifetimeMove/erase.ll b/llvm/test/Transforms/LifetimeMove/erase.ll new file mode 100644 index 0000000000000..dafaadb89acc5 --- /dev/null +++ b/llvm/test/Transforms/LifetimeMove/erase.ll @@ -0,0 +1,62 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=lifetime-move -S | FileCheck %s + +; Do not erase lifetime.start markers that are reachable from either lifetime.end markers. +define void @fn1() presplitcoroutine { +; CHECK-LABEL: define void @fn1( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[VALUE:%.*]] = alloca i64, align 8 +; CHECK-NEXT: %unused = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr nonnull [[VALUE]]) +; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[VALUE]], align 8 +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr nonnull [[VALUE]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr nonnull [[VALUE]]) +; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[VALUE]], align 8 +; CHECK-NEXT: ret void +; +entry: + %value = alloca i64, align 8 + call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %value) + %unused = call i8 @llvm.coro.suspend(token none, i1 false) + %0 = load ptr, ptr %value, align 8 + call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %value) + call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %value) + %1 = load ptr, ptr %value, align 8 + ret void +} + +; Do not erase lifetime markers inside a loop +define void @fn2(i1 %cond) presplitcoroutine { +; CHECK-LABEL: define void @fn2( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[VALUE:%.*]] = alloca i8, align 8 +; CHECK-NEXT: %unused1 = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[VALUE]]) +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[VALUE]]) +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[VALUE]], align 8 +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[VALUE]]) +; CHECK-NEXT: br i1 %cond, label %[[LOOP]], label %[[END:.*]] +; CHECK: [[END]]: +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[VALUE]]) +; CHECK-NEXT: %unused2 = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: ret void +; +entry: + %value = alloca i8, align 8 + %unused1 = call i8 @llvm.coro.suspend(token none, i1 false) + br label %loop + +loop: + call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %value) + %0 = load i8, ptr %value, align 8 + call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %value) + br i1 %cond, label %loop, label %end + +end: + %unused2 = call i8 @llvm.coro.suspend(token none, i1 false) + ret void +} diff --git a/llvm/test/Transforms/LifetimeMove/escape.ll b/llvm/test/Transforms/LifetimeMove/escape.ll new file mode 100644 index 0000000000000..8cfe13646f647 --- /dev/null +++ b/llvm/test/Transforms/LifetimeMove/escape.ll @@ -0,0 +1,58 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; Test we do not rise lifetime.end markers for allocas that may escape +; RUN: opt < %s -passes='lifetime-move' -S | FileCheck %s + +define void @fn() presplitcoroutine { +; CHECK-LABEL: define void @fn( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[ESCAPE_GEP:%.*]] = alloca [500 x i8], align 16 +; CHECK-NEXT: [[GEP_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[ESCAPE_GEP]], i64 8 +; CHECK-NEXT: [[ESCAPE_STORE:%.*]] = alloca [500 x i8], align 16 +; CHECK-NEXT: [[STORE_PTR:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[ESCAPE_STORE]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[STORE_PTR]]) +; CHECK-NEXT: store ptr [[ESCAPE_STORE]], ptr [[STORE_PTR]], align 8 +; CHECK-NEXT: [[ESCAPE_CALL:%.*]] = alloca [500 x i8], align 16 +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[ESCAPE_CALL]]) +; CHECK-NEXT: call void @capture(ptr [[ESCAPE_CALL]]) +; CHECK-NEXT: [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[ESCAPE_GEP]]) +; CHECK-NEXT: call void @capture(ptr [[GEP_PTR]]) +; CHECK-NEXT: call void @capture(ptr [[STORE_PTR]]) +; CHECK-NEXT: br label %[[END:.*]] +; CHECK: [[END]]: +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[ESCAPE_GEP]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[ESCAPE_STORE]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[ESCAPE_CALL]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[STORE_PTR]]) +; CHECK-NEXT: ret void +; +entry: + %escape.gep = alloca [500 x i8], align 16 + call void @llvm.lifetime.start.p0(i64 500, ptr %escape.gep) + %gep.ptr = getelementptr inbounds nuw i8, ptr %escape.gep, i64 8 + + %escape.store = alloca [500 x i8], align 16 + %store.ptr = alloca ptr, align 8 + call void @llvm.lifetime.start.p0(i64 500, ptr %escape.store) + call void @llvm.lifetime.start.p0(i64 8, ptr %store.ptr) + store ptr %escape.store, ptr %store.ptr, align 8 + + %escape.call = alloca [500 x i8], align 16 + call void @llvm.lifetime.start.p0(i64 500, ptr %escape.call) + call void @capture(ptr %escape.call) + + %unused = call i8 @llvm.coro.suspend(token none, i1 false) + call void @capture(ptr %gep.ptr) + call void @capture(ptr %store.ptr) + br label %end +end: + call void @llvm.lifetime.end.p0(i64 500, ptr %escape.gep) + call void @llvm.lifetime.end.p0(i64 500, ptr %escape.store) + call void @llvm.lifetime.end.p0(i64 500, ptr %escape.call) + call void @llvm.lifetime.end.p0(i64 8, ptr %store.ptr) + ret void +} + +declare void @capture(ptr) diff --git a/llvm/test/Transforms/LifetimeMove/indirect_start.ll b/llvm/test/Transforms/LifetimeMove/indirect_start.ll new file mode 100644 index 0000000000000..c67348a48614e --- /dev/null +++ b/llvm/test/Transforms/LifetimeMove/indirect_start.ll @@ -0,0 +1,23 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; Test that lifetime-move knows the operand of lifetime.start intrinsic may be GEP as well. +; RUN: opt < %s -passes=lifetime-move -S | FileCheck %s + +define void @indirect() presplitcoroutine { +; CHECK-LABEL: define void @indirect( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[ARR:%.*]] = alloca [500 x i8], align 16 +; CHECK-NEXT: [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 0 +; CHECK-NEXT: [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[ARR]]) +; CHECK-NEXT: [[VALUE:%.*]] = load i8, ptr [[ARR]], align 1 +; CHECK-NEXT: ret void +; +entry: + %arr = alloca [500 x i8], align 16 + %p = getelementptr inbounds nuw i8, ptr %arr, i64 0 + call void @llvm.lifetime.start.p0(i64 500, ptr %p) + %unused = call i8 @llvm.coro.suspend(token none, i1 false) + %value = load i8, ptr %arr, align 1 + ret void +} diff --git a/llvm/test/Transforms/LifetimeMove/loop.ll b/llvm/test/Transforms/LifetimeMove/loop.ll new file mode 100644 index 0000000000000..c1d82c1d8ec01 --- /dev/null +++ b/llvm/test/Transforms/LifetimeMove/loop.ll @@ -0,0 +1,84 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=lifetime-move -S | FileCheck %s + +; Test we correctly expose loop exiting blocks for lifetime.end +define void @fn1() presplitcoroutine { +; CHECK-LABEL: define void @fn1( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[TESTVAL:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr nonnull [[TESTVAL]]) +; CHECK-NEXT: br label %[[FOR_COND:.*]] +; CHECK: [[FOR_COND]]: +; CHECK-NEXT: [[STOREMERGE:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[FOR_BODY:.*]] ] +; CHECK-NEXT: store i32 [[STOREMERGE]], ptr [[I]], align 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[STOREMERGE]], 500 +; CHECK-NEXT: br i1 [[CMP]], label %[[FOR_BODY]], label %[[FOR_COND_CLEANUP:.*]] +; CHECK: [[FOR_BODY]]: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[TESTVAL]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[I]], align 4 +; CHECK-NEXT: [[INC]] = add nsw i32 [[TMP1]], 1 +; CHECK-NEXT: br label %[[FOR_COND]] +; CHECK: [[FOR_COND_CLEANUP]]: +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr nonnull [[TESTVAL]]) +; CHECK-NEXT: [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: br label %[[EXIT:.*]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + %testval = alloca i32, align 4 + %i = alloca i32, align 4 + call void @llvm.lifetime.start.p0(i64 500, ptr nonnull %testval) + br label %for.cond + +for.cond: ; preds = %for.body, %coro.init + %storemerge = phi i32 [ 0, %entry ], [ %inc, %for.body ] + store i32 %storemerge, ptr %i, align 4 + %cmp = icmp slt i32 %storemerge, 500 + br i1 %cmp, label %for.body, label %for.cond.cleanup + +for.body: ; preds = %for.cond + %0 = load i32, ptr %testval, align 4 + %1 = load i32, ptr %i, align 4 + %inc = add nsw i32 %1, 1 + br label %for.cond + +for.cond.cleanup: + %unused = call i8 @llvm.coro.suspend(token none, i1 false) + br label %exit + +exit: + call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %testval) + ret void +} + +; Test do not move lifetime markers into a loop +define void @fn2(i1 %cond) presplitcoroutine { +; CHECK-LABEL: define void @fn2( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TESTVAL:%.*]] = alloca i32, align 4 +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr nonnull [[TESTVAL]]) +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: br i1 %cond, label %[[EXIT:.*]], label %[[LOOP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr nonnull [[TESTVAL]]) +; CHECK-NEXT: ret void +; +entry: + %testval = alloca i32, align 4 + call void @llvm.lifetime.start.p0(i64 500, ptr nonnull %testval) + br label %loop + +loop: + %unused = call i8 @llvm.coro.suspend(token none, i1 false) + br i1 %cond, label %exit, label %loop + +exit: + call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %testval) + ret void +} diff --git a/llvm/test/Transforms/LifetimeMove/multi_critical.ll b/llvm/test/Transforms/LifetimeMove/multi_critical.ll new file mode 100644 index 0000000000000..724b3274f36c1 --- /dev/null +++ b/llvm/test/Transforms/LifetimeMove/multi_critical.ll @@ -0,0 +1,79 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; Test that we rise lifetime markers to the correct place if there are many critical points. +; RUN: opt < %s -passes=lifetime-move -S | FileCheck %s + +define void @many_critical1() presplitcoroutine { +; CHECK-LABEL: define void @many_critical1( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[LARGE_ALLOCA:%.*]] = alloca [500 x i8], align 16 +; CHECK-NEXT: [[SP1:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: [[SP2:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[LARGE_ALLOCA]]) +; CHECK-NEXT: [[VALUE:%.*]] = load i8, ptr [[LARGE_ALLOCA]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[LARGE_ALLOCA]]) +; CHECK-NEXT: [[SP3:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: [[SP4:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: ret void +; +entry: + %large.alloca = alloca [500 x i8], align 16 + call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca) + %sp1 = call i8 @llvm.coro.suspend(token none, i1 false) + %sp2 = call i8 @llvm.coro.suspend(token none, i1 false) + %value = load i8, ptr %large.alloca, align 1 + %sp3 = call i8 @llvm.coro.suspend(token none, i1 false) + %sp4 = call i8 @llvm.coro.suspend(token none, i1 false) + call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca) + ret void +} + +define void @many_critical2() presplitcoroutine { +; CHECK-LABEL: define void @many_critical2( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[LARGE_ALLOCA:%.*]] = alloca [500 x i8], align 16 +; CHECK-NEXT: br label %[[LABEL1:.*]] +; CHECK: [[LABEL1]]: +; CHECK-NEXT: [[SP1:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: br label %[[LABEL2:.*]] +; CHECK: [[LABEL2]]: +; CHECK-NEXT: [[SP2:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[LARGE_ALLOCA]]) +; CHECK-NEXT: br label %[[USE:.*]] +; CHECK: [[USE]]: +; CHECK-NEXT: [[VALUE:%.*]] = load i8, ptr [[LARGE_ALLOCA]], align 1 +; CHECK-NEXT: br label %[[LABEL3:.*]] +; CHECK: [[LABEL3]]: +; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[LARGE_ALLOCA]]) +; CHECK-NEXT: [[SP3:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: br label %[[LABEL4:.*]] +; CHECK: [[LABEL4]]: +; CHECK-NEXT: [[SP4:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) +; CHECK-NEXT: br label %[[END:.*]] +; CHECK: [[END]]: +; CHECK-NEXT: ret void +; +entry: + %large.alloca = alloca [500 x i8], align 16 + call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca) + br label %label1 +label1: + %sp1 = call i8 @llvm.coro.suspend(token none, i1 false) + br label %label2 +label2: + %sp2 = call i8 @llvm.coro.suspend(token none, i1 false) + br label %use +use: + %value = load i8, ptr %large.alloca, align 1 + br label %label3 +label3: + %sp3 = call i8 @llvm.coro.suspend(token none, i1 false) + br label %label4 +label4: + %sp4 = call i8 @llvm.coro.suspend(token none, i1 false) + br label %end +end: + call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca) + ret void +} >From c45d7086e8a51c0c469d4ca8f712b9fe228cbc20 Mon Sep 17 00:00:00 2001 From: NewSigma <newsi...@163.com> Date: Sun, 10 Aug 2025 10:21:37 +0800 Subject: [PATCH 2/2] Remove size argument of lifetime markers --- llvm/lib/Transforms/Utils/LifetimeMove.cpp | 8 +---- .../Transforms/Coroutines/coro-alloca-07.ll | 4 +-- .../Coroutines/coro-split-rise-lifetime-01.ll | 4 +-- llvm/test/Transforms/LifetimeMove/erase.ll | 24 +++++++------- llvm/test/Transforms/LifetimeMove/escape.ll | 32 +++++++++---------- .../Transforms/LifetimeMove/indirect_start.ll | 23 ------------- llvm/test/Transforms/LifetimeMove/loop.ll | 16 +++++----- .../Transforms/LifetimeMove/multi_critical.ll | 16 +++++----- 8 files changed, 49 insertions(+), 78 deletions(-) delete mode 100644 llvm/test/Transforms/LifetimeMove/indirect_start.ll diff --git a/llvm/lib/Transforms/Utils/LifetimeMove.cpp b/llvm/lib/Transforms/Utils/LifetimeMove.cpp index f23f7f3ff4568..4051bcdd0147b 100644 --- a/llvm/lib/Transforms/Utils/LifetimeMove.cpp +++ b/llvm/lib/Transforms/Utils/LifetimeMove.cpp @@ -164,12 +164,6 @@ void LifetimeMover::visitStoreInst(StoreInst &SI) { } void LifetimeMover::visitIntrinsicInst(IntrinsicInst &II) { - // When we found the lifetime markers refers to a - // subrange of the original alloca, ignore the lifetime - // markers to avoid misleading the analysis. - if (!IsOffsetKnown || !Offset.isZero()) - return Base::visitIntrinsicInst(II); - // lifetime markers are not actual uses switch (II.getIntrinsicID()) { case Intrinsic::lifetime_start: @@ -233,7 +227,7 @@ bool LifetimeMover::sinkLifetimeStartMarkers(AllocaInst *AI) { } auto *NewStart = LifetimeStarts[0]->clone(); - NewStart->replaceUsesOfWith(NewStart->getOperand(1), AI); + NewStart->replaceUsesOfWith(NewStart->getOperand(0), AI); if (DomPoint->isTerminator()) NewStart->insertBefore( cast<InvokeInst>(DomPoint)->getNormalDest()->getFirstNonPHIIt()); diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-07.ll b/llvm/test/Transforms/Coroutines/coro-alloca-07.ll index adf2b19935df1..b689bf08b9993 100644 --- a/llvm/test/Transforms/Coroutines/coro-alloca-07.ll +++ b/llvm/test/Transforms/Coroutines/coro-alloca-07.ll @@ -31,8 +31,8 @@ resume: br label %cleanup cleanup: - call void @llvm.lifetime.end.p0(i64 8, ptr %x) - call void @llvm.lifetime.end.p0(i64 8, ptr %y) + call void @llvm.lifetime.end.p0(ptr %x) + call void @llvm.lifetime.end.p0(ptr %y) %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) call void @free(ptr %mem) br label %suspend diff --git a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll index 967221baf57cd..f6ac20a249164 100644 --- a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll +++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll @@ -11,7 +11,7 @@ entry: %size = call i32 @llvm.coro.size.i32() %mem = call ptr @malloc(i32 %size) %hdl = call ptr @llvm.coro.begin(token %id, ptr %mem) - call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca) + call void @llvm.lifetime.start.p0(ptr %large.alloca) %value = load i8, ptr %large.alloca, align 1 call void @consume(i8 %value) %0 = call i8 @llvm.coro.suspend(token none, i1 false) @@ -24,7 +24,7 @@ suspend: br label %cleanup cleanup: - call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca) + call void @llvm.lifetime.end.p0(ptr %large.alloca) %1 = call ptr @llvm.coro.free(token %id, ptr %mem) call void @free(ptr %mem) br label %exit diff --git a/llvm/test/Transforms/LifetimeMove/erase.ll b/llvm/test/Transforms/LifetimeMove/erase.ll index dafaadb89acc5..4094f08ed1d27 100644 --- a/llvm/test/Transforms/LifetimeMove/erase.ll +++ b/llvm/test/Transforms/LifetimeMove/erase.ll @@ -8,20 +8,20 @@ define void @fn1() presplitcoroutine { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[VALUE:%.*]] = alloca i64, align 8 ; CHECK-NEXT: %unused = call i8 @llvm.coro.suspend(token none, i1 false) -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr nonnull [[VALUE]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VALUE]]) ; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[VALUE]], align 8 -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr nonnull [[VALUE]]) -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr nonnull [[VALUE]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VALUE]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VALUE]]) ; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[VALUE]], align 8 ; CHECK-NEXT: ret void ; entry: %value = alloca i64, align 8 - call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %value) + call void @llvm.lifetime.start.p0(ptr %value) %unused = call i8 @llvm.coro.suspend(token none, i1 false) %0 = load ptr, ptr %value, align 8 - call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %value) - call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %value) + call void @llvm.lifetime.end.p0(ptr %value) + call void @llvm.lifetime.start.p0(ptr %value) %1 = load ptr, ptr %value, align 8 ret void } @@ -33,15 +33,15 @@ define void @fn2(i1 %cond) presplitcoroutine { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[VALUE:%.*]] = alloca i8, align 8 ; CHECK-NEXT: %unused1 = call i8 @llvm.coro.suspend(token none, i1 false) -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[VALUE]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VALUE]]) ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[VALUE]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VALUE]]) ; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[VALUE]], align 8 -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[VALUE]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VALUE]]) ; CHECK-NEXT: br i1 %cond, label %[[LOOP]], label %[[END:.*]] ; CHECK: [[END]]: -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[VALUE]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VALUE]]) ; CHECK-NEXT: %unused2 = call i8 @llvm.coro.suspend(token none, i1 false) ; CHECK-NEXT: ret void ; @@ -51,9 +51,9 @@ entry: br label %loop loop: - call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %value) + call void @llvm.lifetime.start.p0(ptr %value) %0 = load i8, ptr %value, align 8 - call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %value) + call void @llvm.lifetime.end.p0(ptr %value) br i1 %cond, label %loop, label %end end: diff --git a/llvm/test/Transforms/LifetimeMove/escape.ll b/llvm/test/Transforms/LifetimeMove/escape.ll index 8cfe13646f647..81ed90f5c9def 100644 --- a/llvm/test/Transforms/LifetimeMove/escape.ll +++ b/llvm/test/Transforms/LifetimeMove/escape.ll @@ -10,37 +10,37 @@ define void @fn() presplitcoroutine { ; CHECK-NEXT: [[GEP_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[ESCAPE_GEP]], i64 8 ; CHECK-NEXT: [[ESCAPE_STORE:%.*]] = alloca [500 x i8], align 16 ; CHECK-NEXT: [[STORE_PTR:%.*]] = alloca ptr, align 8 -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[ESCAPE_STORE]]) -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[STORE_PTR]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[ESCAPE_STORE]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[STORE_PTR]]) ; CHECK-NEXT: store ptr [[ESCAPE_STORE]], ptr [[STORE_PTR]], align 8 ; CHECK-NEXT: [[ESCAPE_CALL:%.*]] = alloca [500 x i8], align 16 -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[ESCAPE_CALL]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[ESCAPE_CALL]]) ; CHECK-NEXT: call void @capture(ptr [[ESCAPE_CALL]]) ; CHECK-NEXT: [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[ESCAPE_GEP]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[ESCAPE_GEP]]) ; CHECK-NEXT: call void @capture(ptr [[GEP_PTR]]) ; CHECK-NEXT: call void @capture(ptr [[STORE_PTR]]) ; CHECK-NEXT: br label %[[END:.*]] ; CHECK: [[END]]: -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[ESCAPE_GEP]]) -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[ESCAPE_STORE]]) -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[ESCAPE_CALL]]) -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[STORE_PTR]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[ESCAPE_GEP]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[ESCAPE_STORE]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[ESCAPE_CALL]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[STORE_PTR]]) ; CHECK-NEXT: ret void ; entry: %escape.gep = alloca [500 x i8], align 16 - call void @llvm.lifetime.start.p0(i64 500, ptr %escape.gep) + call void @llvm.lifetime.start.p0(ptr %escape.gep) %gep.ptr = getelementptr inbounds nuw i8, ptr %escape.gep, i64 8 %escape.store = alloca [500 x i8], align 16 %store.ptr = alloca ptr, align 8 - call void @llvm.lifetime.start.p0(i64 500, ptr %escape.store) - call void @llvm.lifetime.start.p0(i64 8, ptr %store.ptr) + call void @llvm.lifetime.start.p0(ptr %escape.store) + call void @llvm.lifetime.start.p0(ptr %store.ptr) store ptr %escape.store, ptr %store.ptr, align 8 %escape.call = alloca [500 x i8], align 16 - call void @llvm.lifetime.start.p0(i64 500, ptr %escape.call) + call void @llvm.lifetime.start.p0(ptr %escape.call) call void @capture(ptr %escape.call) %unused = call i8 @llvm.coro.suspend(token none, i1 false) @@ -48,10 +48,10 @@ entry: call void @capture(ptr %store.ptr) br label %end end: - call void @llvm.lifetime.end.p0(i64 500, ptr %escape.gep) - call void @llvm.lifetime.end.p0(i64 500, ptr %escape.store) - call void @llvm.lifetime.end.p0(i64 500, ptr %escape.call) - call void @llvm.lifetime.end.p0(i64 8, ptr %store.ptr) + call void @llvm.lifetime.end.p0(ptr %escape.gep) + call void @llvm.lifetime.end.p0(ptr %escape.store) + call void @llvm.lifetime.end.p0(ptr %escape.call) + call void @llvm.lifetime.end.p0(ptr %store.ptr) ret void } diff --git a/llvm/test/Transforms/LifetimeMove/indirect_start.ll b/llvm/test/Transforms/LifetimeMove/indirect_start.ll deleted file mode 100644 index c67348a48614e..0000000000000 --- a/llvm/test/Transforms/LifetimeMove/indirect_start.ll +++ /dev/null @@ -1,23 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 -; Test that lifetime-move knows the operand of lifetime.start intrinsic may be GEP as well. -; RUN: opt < %s -passes=lifetime-move -S | FileCheck %s - -define void @indirect() presplitcoroutine { -; CHECK-LABEL: define void @indirect( -; CHECK-SAME: ) #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[ARR:%.*]] = alloca [500 x i8], align 16 -; CHECK-NEXT: [[P:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 0 -; CHECK-NEXT: [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[ARR]]) -; CHECK-NEXT: [[VALUE:%.*]] = load i8, ptr [[ARR]], align 1 -; CHECK-NEXT: ret void -; -entry: - %arr = alloca [500 x i8], align 16 - %p = getelementptr inbounds nuw i8, ptr %arr, i64 0 - call void @llvm.lifetime.start.p0(i64 500, ptr %p) - %unused = call i8 @llvm.coro.suspend(token none, i1 false) - %value = load i8, ptr %arr, align 1 - ret void -} diff --git a/llvm/test/Transforms/LifetimeMove/loop.ll b/llvm/test/Transforms/LifetimeMove/loop.ll index c1d82c1d8ec01..0af8943cab474 100644 --- a/llvm/test/Transforms/LifetimeMove/loop.ll +++ b/llvm/test/Transforms/LifetimeMove/loop.ll @@ -8,7 +8,7 @@ define void @fn1() presplitcoroutine { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[TESTVAL:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr nonnull [[TESTVAL]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[TESTVAL]]) ; CHECK-NEXT: br label %[[FOR_COND:.*]] ; CHECK: [[FOR_COND]]: ; CHECK-NEXT: [[STOREMERGE:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[FOR_BODY:.*]] ] @@ -21,7 +21,7 @@ define void @fn1() presplitcoroutine { ; CHECK-NEXT: [[INC]] = add nsw i32 [[TMP1]], 1 ; CHECK-NEXT: br label %[[FOR_COND]] ; CHECK: [[FOR_COND_CLEANUP]]: -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr nonnull [[TESTVAL]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TESTVAL]]) ; CHECK-NEXT: [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) ; CHECK-NEXT: br label %[[EXIT:.*]] ; CHECK: [[EXIT]]: @@ -30,7 +30,7 @@ define void @fn1() presplitcoroutine { entry: %testval = alloca i32, align 4 %i = alloca i32, align 4 - call void @llvm.lifetime.start.p0(i64 500, ptr nonnull %testval) + call void @llvm.lifetime.start.p0(ptr nonnull %testval) br label %for.cond for.cond: ; preds = %for.body, %coro.init @@ -50,7 +50,7 @@ for.cond.cleanup: br label %exit exit: - call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %testval) + call void @llvm.lifetime.end.p0(ptr nonnull %testval) ret void } @@ -60,18 +60,18 @@ define void @fn2(i1 %cond) presplitcoroutine { ; CHECK-SAME: ) #[[ATTR0]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[TESTVAL:%.*]] = alloca i32, align 4 -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr nonnull [[TESTVAL]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[TESTVAL]]) ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) ; CHECK-NEXT: br i1 %cond, label %[[EXIT:.*]], label %[[LOOP]] ; CHECK: [[EXIT]]: -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr nonnull [[TESTVAL]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[TESTVAL]]) ; CHECK-NEXT: ret void ; entry: %testval = alloca i32, align 4 - call void @llvm.lifetime.start.p0(i64 500, ptr nonnull %testval) + call void @llvm.lifetime.start.p0(ptr nonnull %testval) br label %loop loop: @@ -79,6 +79,6 @@ loop: br i1 %cond, label %exit, label %loop exit: - call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %testval) + call void @llvm.lifetime.end.p0(ptr nonnull %testval) ret void } diff --git a/llvm/test/Transforms/LifetimeMove/multi_critical.ll b/llvm/test/Transforms/LifetimeMove/multi_critical.ll index 724b3274f36c1..07bc4c16fa9ef 100644 --- a/llvm/test/Transforms/LifetimeMove/multi_critical.ll +++ b/llvm/test/Transforms/LifetimeMove/multi_critical.ll @@ -9,22 +9,22 @@ define void @many_critical1() presplitcoroutine { ; CHECK-NEXT: [[LARGE_ALLOCA:%.*]] = alloca [500 x i8], align 16 ; CHECK-NEXT: [[SP1:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) ; CHECK-NEXT: [[SP2:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[LARGE_ALLOCA]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[LARGE_ALLOCA]]) ; CHECK-NEXT: [[VALUE:%.*]] = load i8, ptr [[LARGE_ALLOCA]], align 1 -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[LARGE_ALLOCA]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[LARGE_ALLOCA]]) ; CHECK-NEXT: [[SP3:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) ; CHECK-NEXT: [[SP4:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) ; CHECK-NEXT: ret void ; entry: %large.alloca = alloca [500 x i8], align 16 - call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca) + call void @llvm.lifetime.start.p0(ptr %large.alloca) %sp1 = call i8 @llvm.coro.suspend(token none, i1 false) %sp2 = call i8 @llvm.coro.suspend(token none, i1 false) %value = load i8, ptr %large.alloca, align 1 %sp3 = call i8 @llvm.coro.suspend(token none, i1 false) %sp4 = call i8 @llvm.coro.suspend(token none, i1 false) - call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca) + call void @llvm.lifetime.end.p0(ptr %large.alloca) ret void } @@ -39,13 +39,13 @@ define void @many_critical2() presplitcoroutine { ; CHECK-NEXT: br label %[[LABEL2:.*]] ; CHECK: [[LABEL2]]: ; CHECK-NEXT: [[SP2:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) -; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 500, ptr [[LARGE_ALLOCA]]) +; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[LARGE_ALLOCA]]) ; CHECK-NEXT: br label %[[USE:.*]] ; CHECK: [[USE]]: ; CHECK-NEXT: [[VALUE:%.*]] = load i8, ptr [[LARGE_ALLOCA]], align 1 ; CHECK-NEXT: br label %[[LABEL3:.*]] ; CHECK: [[LABEL3]]: -; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 500, ptr [[LARGE_ALLOCA]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[LARGE_ALLOCA]]) ; CHECK-NEXT: [[SP3:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false) ; CHECK-NEXT: br label %[[LABEL4:.*]] ; CHECK: [[LABEL4]]: @@ -56,7 +56,7 @@ define void @many_critical2() presplitcoroutine { ; entry: %large.alloca = alloca [500 x i8], align 16 - call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca) + call void @llvm.lifetime.start.p0(ptr %large.alloca) br label %label1 label1: %sp1 = call i8 @llvm.coro.suspend(token none, i1 false) @@ -74,6 +74,6 @@ label4: %sp4 = call i8 @llvm.coro.suspend(token none, i1 false) br label %end end: - call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca) + call void @llvm.lifetime.end.p0(ptr %large.alloca) ret void } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits