[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)
Weibo He via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 9 19:39:15 PDT 2025
https://github.com/NewSigma updated https://github.com/llvm/llvm-project/pull/144319
>From 6322fa52e33cad77a59b685bf6cf8e6f218eb7e8 Mon Sep 17 00:00:00 2001
From: NewSigma <NewSigma at 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 at 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 <NewSigma at 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
}
More information about the llvm-commits
mailing list