[clang] [llvm] [LLVM][Coro] Add LifetimeMovePass (PR #144319)
Weibo He via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 16 03:33:48 PDT 2025
https://github.com/NewSigma updated https://github.com/llvm/llvm-project/pull/144319
>From 584d47295f7719f96ee77d32a61111b4329f82be Mon Sep 17 00:00:00 2001
From: NewSigma <NewSigma at 163.com>
Date: Mon, 16 Jun 2025 15:36:53 +0800
Subject: [PATCH 1/4] Add LifetimeMovePass
---
clang/test/CodeGenCoroutines/pr56919.cpp | 6 +-
.../llvm/Transforms/Scalar/LifetimeMove.h | 23 ++
llvm/lib/Passes/PassBuilder.cpp | 1 +
llvm/lib/Passes/PassRegistry.def | 1 +
llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 88 +------
llvm/lib/Transforms/Scalar/CMakeLists.txt | 1 +
llvm/lib/Transforms/Scalar/LifetimeMove.cpp | 223 ++++++++++++++++++
.../Transforms/Coroutines/coro-alloca-07.ll | 4 +-
.../Coroutines/coro-split-rise-lifetime-01.ll | 39 +++
.../Coroutines/coro-split-rise-lifetime-02.ll | 61 +++++
.../Coroutines/coro-split-rise-lifetime-03.ll | 62 +++++
.../Coroutines/coro-split-sink-lifetime-01.ll | 2 +-
.../Coroutines/coro-split-sink-lifetime-02.ll | 2 +-
.../Coroutines/coro-split-sink-lifetime-03.ll | 2 +-
.../Coroutines/coro-split-sink-lifetime-04.ll | 2 +-
15 files changed, 422 insertions(+), 95 deletions(-)
create mode 100644 llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
create mode 100644 llvm/lib/Transforms/Scalar/LifetimeMove.cpp
create mode 100644 llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
create mode 100644 llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
create mode 100644 llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.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/include/llvm/Transforms/Scalar/LifetimeMove.h b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
new file mode 100644
index 0000000000000..f9a690433cb77
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Scalar/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 a6c59c1ca846e..03ed9c51c2b74 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -279,6 +279,7 @@
#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
#include "llvm/Transforms/Scalar/JumpThreading.h"
#include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
#include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"
#include "llvm/Transforms/Scalar/LoopBoundSplit.h"
#include "llvm/Transforms/Scalar/LoopDataPrefetch.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index fe6f13477bb12..c457efc307bb4 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -402,6 +402,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 53d78edda2e9f..a7d7e6ef6d710 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,6 +18,7 @@
#include "CoroInternal.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/StackLifetime.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
@@ -1767,89 +1768,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(1), 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,
@@ -2070,10 +1988,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/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt
index 84a5b02043d01..ea0ac853d2716 100644
--- a/llvm/lib/Transforms/Scalar/CMakeLists.txt
+++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt
@@ -27,6 +27,7 @@ add_llvm_component_library(LLVMScalarOpts
JumpThreading.cpp
JumpTableToSwitch.cpp
LICM.cpp
+ LifetimeMove.cpp
LoopAccessAnalysisPrinter.cpp
LoopBoundSplit.cpp
LoopSink.cpp
diff --git a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
new file mode 100644
index 0000000000000..08349c36d24fc
--- /dev/null
+++ b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
@@ -0,0 +1,223 @@
+//===- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
+#include "llvm/Analysis/CFG.h"
+#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/Transforms/Coroutines/CoroInstr.h"
+
+#define DEBUG_TYPE "lifetime-move"
+
+namespace llvm {
+static bool mayEscape(Value *V, User *U) {
+ if (V == U->stripInBoundsOffsets() || isa<PHINode>(U))
+ return true;
+
+ if (auto *SI = dyn_cast<StoreInst>(U))
+ return SI->getValueOperand() == V;
+
+ if (auto *CB = dyn_cast<CallBase>(U)) {
+ unsigned OpCount = CB->arg_size();
+ for (unsigned Op = 0; Op < OpCount; ++Op)
+ if (V == CB->getArgOperand(Op) && !CB->doesNotCapture(Op))
+ return true;
+ }
+ return false;
+}
+
+namespace {
+class LifetimeMover {
+ const DominatorTree &DT;
+
+ SmallVector<AllocaInst *, 4> Allocas;
+ // Critical points are instructions where the crossing of a variable's
+ // lifetime makes a difference. We attempt to move lifetime.end
+ // before critical points and lifetime.start after them.
+ SmallVector<Instruction *, 4> CriticalPoints;
+
+ SmallVector<Instruction *, 2> LifetimeStarts;
+ SmallVector<Instruction *, 2> LifetimeEnds;
+ SmallVector<Instruction *, 8> OtherUsers;
+ SmallPtrSet<BasicBlock *, 2> UserBBs;
+
+public:
+ LifetimeMover(Function &F, const DominatorTree &DT);
+
+ void run();
+
+private:
+ void sinkLifetimeStartMarkers(AllocaInst *AI);
+ void riseLifetimeEndMarkers();
+ bool collectLifetime(Instruction *I);
+ void reset();
+};
+} // namespace
+
+LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT) : DT(DT) {
+ for (Instruction &I : instructions(F)) {
+ if (auto *AI = dyn_cast<AllocaInst>(&I))
+ Allocas.push_back(AI);
+ else if (isa<AnyCoroSuspendInst>(I))
+ CriticalPoints.push_back(&I);
+ }
+}
+
+void LifetimeMover::run() {
+ for (auto *AI : Allocas) {
+ reset();
+
+ bool Escape = false;
+ for (User *U : AI->users()) {
+ auto *I = cast<Instruction>(U);
+ // lifetime markers are not actual uses
+ if (collectLifetime(I))
+ continue;
+
+ // GEP and bitcast used by lifetime markers
+ if (U->hasOneUse() && U->stripPointerCasts() == AI) {
+ auto *U1 = cast<Instruction>(U->user_back());
+ if (collectLifetime(U1))
+ continue;
+ }
+
+ Escape |= mayEscape(AI, U);
+ OtherUsers.push_back(I);
+ UserBBs.insert(I->getParent());
+ }
+
+ if (!LifetimeStarts.empty())
+ sinkLifetimeStartMarkers(AI);
+
+ // Do not move lifetime.end if alloca escapes
+ if (!LifetimeEnds.empty() && !Escape)
+ riseLifetimeEndMarkers();
+ }
+}
+/// 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.
+void LifetimeMover::sinkLifetimeStartMarkers(AllocaInst *AI) {
+ auto Update = [this](Instruction *Old, Instruction *New) {
+ // Reject if the new proposal lengthens the lifetime
+ if (DT.dominates(New, Old))
+ return Old;
+
+ bool DomAll = llvm::all_of(UserBBs, [this, New](BasicBlock *UserBB) {
+ // Instruction level analysis if lifetime and users share a common BB
+ if (UserBB == New->getParent()) {
+ return llvm::all_of(OtherUsers, [this, New, UserBB](Instruction *I) {
+ return UserBB != I->getParent() || DT.dominates(New, 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 (isPotentiallyReachable(DomPoint, P) && (P == Update(DomPoint, P)))
+ return;
+
+ auto *NewStart = LifetimeStarts[0]->clone();
+ NewStart->replaceUsesOfWith(NewStart->getOperand(1), AI);
+ NewStart->insertAfter(DomPoint->getIterator());
+
+ // All the outsided lifetime.start markers are no longer necessary.
+ for (auto *I : LifetimeStarts)
+ if (DT.dominates(I, DomPoint))
+ I->eraseFromParent();
+ }
+}
+// Find the critical point that is dominated by all users of alloca,
+// we will rise lifetime.end markers before the critical point.
+void LifetimeMover::riseLifetimeEndMarkers() {
+ auto Update = [this](Instruction *Old, Instruction *New) {
+ if (Old != nullptr && DT.dominates(Old, New))
+ return Old;
+
+ bool DomAll = llvm::all_of(UserBBs, [this, New](BasicBlock *UserBB) {
+ if (UserBB == New->getParent()) {
+ return llvm::all_of(OtherUsers, [this, New, UserBB](Instruction *I) {
+ return UserBB != I->getParent() || DT.dominates(I, New);
+ });
+ }
+ return DT.dominates(UserBB, New->getParent());
+ });
+ return DomAll ? New : Old;
+ };
+
+ Instruction *DomPoint = nullptr;
+ for (auto *P : CriticalPoints)
+ DomPoint = Update(DomPoint, P);
+
+ if (DomPoint != nullptr) {
+ for (auto *P : LifetimeEnds)
+ if (isPotentiallyReachable(P, DomPoint) && (P == Update(DomPoint, P)))
+ return;
+
+ auto *NewEnd = LifetimeEnds[0]->clone();
+ NewEnd->insertBefore(DomPoint->getIterator());
+
+ for (auto *I : LifetimeEnds)
+ if (DT.dominates(DomPoint, I))
+ I->eraseFromParent();
+ }
+}
+
+bool LifetimeMover::collectLifetime(Instruction *I) {
+ if (auto *II = dyn_cast<IntrinsicInst>(I)) {
+ auto ID = II->getIntrinsicID();
+ if (ID == Intrinsic::lifetime_start) {
+ LifetimeStarts.push_back(I);
+ return true;
+ }
+
+ if (ID == Intrinsic::lifetime_end) {
+ LifetimeEnds.push_back(I);
+ return true;
+ }
+ }
+ return false;
+}
+
+void LifetimeMover::reset() {
+ LifetimeStarts.clear();
+ LifetimeEnds.clear();
+ OtherUsers.clear();
+ UserBBs.clear();
+}
+
+PreservedAnalyses LifetimeMovePass::run(Function &F,
+ FunctionAnalysisManager &AM) {
+ // Works for coroutine for now
+ if (!F.isPresplitCoroutine())
+ PreservedAnalyses::all();
+
+ const DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(F);
+ LifetimeMover Mover(F, DT);
+ Mover.run();
+ return PreservedAnalyses::all();
+}
+} // namespace llvm
diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-07.ll b/llvm/test/Transforms/Coroutines/coro-alloca-07.ll
index 3b0acdd794af4..a48bfd0edd0c3 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..d38b4deac5533
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
@@ -0,0 +1,39 @@
+; Test allocas that do not cross suspension point will not go to frame
+; 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-rise-lifetime-02.ll b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
new file mode 100644
index 0000000000000..66a8d2d65727f
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
@@ -0,0 +1,61 @@
+; Test that we rise lifetime markers to the correct place if there are many suspend 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 @many_suspend() 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 i64 @llvm.coro.size.i64()
+ %call = call noalias ptr @malloc(i64 %size)
+ %hdl = call ptr @llvm.coro.begin(token %id, ptr %call)
+ call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca)
+ %save1 = call token @llvm.coro.save(ptr null)
+ %sp1 = call i8 @llvm.coro.suspend(token %save1, i1 false)
+ switch i8 %sp1, label %coro.ret [
+ i8 0, label %await.ready
+ i8 1, label %cleanup
+ ]
+
+await.ready:
+ %save2 = call token @llvm.coro.save(ptr null)
+ %sp2 = call i8 @llvm.coro.suspend(token %save2, i1 false)
+ switch i8 %sp2, label %coro.ret [
+ i8 0, label %await2.ready
+ i8 1, label %cleanup
+ ]
+
+await2.ready:
+ %value = load i8, ptr %large.alloca, align 1
+ call void @consume(i8 %value)
+ %save3 = call token @llvm.coro.save(ptr null)
+ %sp3 = call i8 @llvm.coro.suspend(token %save3, i1 false)
+ switch i8 %sp3, label %coro.ret [
+ i8 0, label %await3.ready
+ i8 1, label %cleanup
+ ]
+
+await3.ready:
+ %save4 = call token @llvm.coro.save(ptr null)
+ %sp4 = call i8 @llvm.coro.suspend(token %save4, i1 false)
+ switch i8 %sp4, label %coro.ret [
+ i8 0, label %cleanup
+ i8 1, label %cleanup
+ ]
+
+cleanup:
+ call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca)
+ %mem1 = call ptr @llvm.coro.free(token %id, ptr %hdl)
+ call void @free(ptr %mem1)
+ br label %coro.ret
+
+coro.ret:
+ %InResumePart = call i1 @llvm.coro.end(ptr null, i1 false, token none)
+ ret void
+}
+
+declare void @consume(i8)
+declare ptr @malloc(i64)
+declare void @free(ptr)
diff --git a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll
new file mode 100644
index 0000000000000..4cbd97a305dab
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll
@@ -0,0 +1,62 @@
+; Test we do not rise lifetime.end for allocas that may escape
+; RUN: opt < %s -passes='cgscc(lifetime-move,coro-split),early-cse' -S | FileCheck %s
+
+; CHECK-NOT: %escape.gep = alloca [500 x i8], align 16
+; CHECK: %escape.gep.reload.addr
+
+; CHECK-NOT: %escape.store = alloca [500 x i8], align 16
+; CHECK: %escape.store.reload.addr
+
+; CHECK-NOT: %escape.call = alloca [500 x i8], align 16
+; CHECK: %escape.call.reload.addr
+
+define void @fn() presplitcoroutine {
+entry:
+ %id = call token @llvm.coro.id(i32 16, ptr null, ptr null, ptr null)
+ %size = call i64 @llvm.coro.size.i64()
+ %mem = call ptr @malloc(i64 %size)
+ %hdl = call ptr @llvm.coro.begin(token %id, ptr %mem)
+
+ %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 @consume(ptr %escape.call)
+
+ %save = call token @llvm.coro.save(ptr null)
+ %suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
+ switch i8 %suspend, label %coro.ret [
+ i8 0, label %await.ready
+ i8 1, label %cleanup
+ ]
+
+await.ready:
+ %1 = load ptr, ptr %gep.ptr, align 8
+ %2 = load ptr, ptr %store.ptr, align 8
+ br label %cleanup
+
+cleanup:
+ 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)
+ %mem1 = call ptr @llvm.coro.free(token %id, ptr %hdl)
+ call void @free(ptr %mem1)
+ br label %coro.ret
+
+coro.ret:
+ %InResumePart = call i1 @llvm.coro.end(ptr null, i1 false, token none)
+ ret void
+}
+
+declare void @consume(ptr)
+declare ptr @malloc(i64)
+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 1d0cf94c1a979..b002d423ed474 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,6 @@
; 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
+; 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 }
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 38a2a33efe051..3e6a1ea599745 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,6 @@
; 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
+; 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 de377a6a38b94..4b07e3eb301e2 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,6 @@
; 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
+; 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 821045583092d..1fd234349ef88 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,6 @@
; 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
+; 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" }
>From 800fe15cd866e69c89dc6fec2c83ace3f7215840 Mon Sep 17 00:00:00 2001
From: NewSigma <NewSigma at 163.com>
Date: Thu, 12 Jun 2025 11:37:24 +0800
Subject: [PATCH 2/4] Make coro.await.suspend nocapture
---
llvm/docs/Coroutines.rst | 6 +++---
llvm/include/llvm/IR/Intrinsics.td | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/docs/Coroutines.rst b/llvm/docs/Coroutines.rst
index 7472c68a70df4..25960d9ad208c 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 d3899056bc240..54bf5de123855 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1811,15 +1811,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.
>From 17f75d35b17201798f0f06bbcb1918ef86000c5d Mon Sep 17 00:00:00 2001
From: NewSigma <NewSigma at 163.com>
Date: Sat, 14 Jun 2025 17:09:43 +0800
Subject: [PATCH 3/4] Update pipeline
---
llvm/lib/Passes/PassBuilderPipelines.cpp | 9 +++++++++
llvm/test/Other/new-pm-defaults.ll | 3 +++
llvm/test/Other/new-pm-pgo-preinline.ll | 1 +
llvm/test/Other/new-pm-thinlto-postlink-defaults.ll | 3 +++
llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll | 3 +++
.../Other/new-pm-thinlto-postlink-samplepgo-defaults.ll | 3 +++
llvm/test/Other/new-pm-thinlto-prelink-defaults.ll | 3 +++
llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll | 4 ++++
.../Other/new-pm-thinlto-prelink-samplepgo-defaults.ll | 3 +++
9 files changed, 32 insertions(+)
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 227390f557fda..e3ef2e735ca9f 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -100,6 +100,7 @@
#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
#include "llvm/Transforms/Scalar/JumpThreading.h"
#include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
#include "llvm/Transforms/Scalar/LoopDeletion.h"
#include "llvm/Transforms/Scalar/LoopDistribute.h"
#include "llvm/Transforms/Scalar/LoopFlatten.h"
@@ -444,6 +445,7 @@ PassBuilder::buildO1FunctionSimplificationPipeline(OptimizationLevel Level,
// Hoisting of scalars and load expressions.
FPM.addPass(
SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
+ FPM.addPass(LifetimeMovePass());
FPM.addPass(InstCombinePass());
FPM.addPass(LibCallsShrinkWrapPass());
@@ -521,6 +523,7 @@ PassBuilder::buildO1FunctionSimplificationPipeline(OptimizationLevel Level,
/*UseBlockFrequencyInfo=*/true));
FPM.addPass(
SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
+ FPM.addPass(LifetimeMovePass());
FPM.addPass(InstCombinePass());
// The loop passes in LPM2 (LoopFullUnrollPass) do not preserve MemorySSA.
// *All* loop passes must preserve it, in order to be able to use it.
@@ -559,6 +562,7 @@ PassBuilder::buildO1FunctionSimplificationPipeline(OptimizationLevel Level,
FPM.addPass(ADCEPass());
FPM.addPass(
SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
+ FPM.addPass(LifetimeMovePass());
FPM.addPass(InstCombinePass());
invokePeepholeEPCallbacks(FPM, Level);
@@ -613,6 +617,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
FPM.addPass(
SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
+ FPM.addPass(LifetimeMovePass());
FPM.addPass(InstCombinePass());
FPM.addPass(AggressiveInstCombinePass());
@@ -712,6 +717,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
/*UseBlockFrequencyInfo=*/true));
FPM.addPass(
SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
+ FPM.addPass(LifetimeMovePass());
FPM.addPass(InstCombinePass());
// The loop passes in LPM2 (LoopIdiomRecognizePass, IndVarSimplifyPass,
// LoopDeletionPass and LoopFullUnrollPass) do not preserve MemorySSA.
@@ -781,6 +787,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
.convertSwitchRangeToICmp(true)
.hoistCommonInsts(true)
.sinkCommonInsts(true)));
+ FPM.addPass(LifetimeMovePass());
FPM.addPass(InstCombinePass());
invokePeepholeEPCallbacks(FPM, Level);
@@ -817,6 +824,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);
@@ -1357,6 +1365,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/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index c554fdbf4c799..8f9e44447ee91 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -163,6 +163,7 @@
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-JUMP-TABLE-TO-SWITCH-NEXT: Running pass: JumpTableToSwitchPass
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass
; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass
@@ -188,6 +189,7 @@
; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
@@ -225,6 +227,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 ed13402e1c4b1..6b791492e38d0 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -92,6 +92,7 @@
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass
; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass
@@ -116,6 +117,7 @@
; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
@@ -149,6 +151,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 c82c34f7ff01e..b4cf6834c2b91 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -80,6 +80,7 @@
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMove
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass
; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass
@@ -102,6 +103,7 @@
; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
@@ -133,6 +135,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 d375747547d61..31f0aa3d36364 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -89,6 +89,7 @@
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass
; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass
@@ -111,6 +112,7 @@
; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
@@ -142,6 +144,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..8953f951bf4eb 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
@@ -124,6 +124,7 @@
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass
; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass
@@ -148,6 +149,7 @@
; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
@@ -181,6 +183,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..753adfe5ab776 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
@@ -122,6 +123,7 @@
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O-NEXT: Running analysis: LastRunTrackingAnalysis
; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on foo
@@ -151,6 +153,7 @@
; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
@@ -182,6 +185,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..ae24961e06edb 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
@@ -94,6 +94,7 @@
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass
; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass
@@ -116,6 +117,7 @@
; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O-NEXT: Running pass: LifetimeMovePass
; CHECK-O-NEXT: Running pass: InstCombinePass
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
@@ -146,6 +148,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
>From 9e27fb19f2054458f106965020601a9764392c7f Mon Sep 17 00:00:00 2001
From: Weibo He <NewSigma at 163.com>
Date: Mon, 16 Jun 2025 18:33:39 +0800
Subject: [PATCH 4/4] Add missing return
Co-authored-by: Nikita Popov <github at npopov.com>
---
llvm/lib/Transforms/Scalar/LifetimeMove.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
index 08349c36d24fc..2517d4553ee23 100644
--- a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
+++ b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
@@ -213,7 +213,7 @@ PreservedAnalyses LifetimeMovePass::run(Function &F,
FunctionAnalysisManager &AM) {
// Works for coroutine for now
if (!F.isPresplitCoroutine())
- PreservedAnalyses::all();
+ return PreservedAnalyses::all();
const DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(F);
LifetimeMover Mover(F, DT);
More information about the llvm-commits
mailing list