https://github.com/NewSigma updated 
https://github.com/llvm/llvm-project/pull/144319

>From 584d47295f7719f96ee77d32a61111b4329f82be Mon Sep 17 00:00:00 2001
From: NewSigma <newsi...@163.com>
Date: Mon, 16 Jun 2025 15:36:53 +0800
Subject: [PATCH 1/8] 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@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 <newsi...@163.com>
Date: Thu, 12 Jun 2025 11:37:24 +0800
Subject: [PATCH 2/8] 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 <newsi...@163.com>
Date: Sat, 14 Jun 2025 17:09:43 +0800
Subject: [PATCH 3/8] 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 <newsi...@163.com>
Date: Mon, 16 Jun 2025 18:33:39 +0800
Subject: [PATCH 4/8] Add missing return

Co-authored-by: Nikita Popov <git...@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);

>From d5cadc17a68852d4236c08bd0879075c18ddf687 Mon Sep 17 00:00:00 2001
From: NewSigma <newsi...@163.com>
Date: Tue, 17 Jun 2025 11:26:29 +0800
Subject: [PATCH 5/8] Update pipeline

---
 llvm/lib/Passes/PassBuilderPipelines.cpp                      | 4 ----
 llvm/test/Other/new-pm-defaults.ll                            | 2 --
 llvm/test/Other/new-pm-thinlto-postlink-defaults.ll           | 2 --
 llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll       | 2 --
 llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll | 2 --
 llvm/test/Other/new-pm-thinlto-prelink-defaults.ll            | 2 --
 llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll        | 2 --
 llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll  | 2 --
 8 files changed, 18 deletions(-)

diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp 
b/llvm/lib/Passes/PassBuilderPipelines.cpp
index e3ef2e735ca9f..94a09abcb36f0 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -445,7 +445,6 @@ 
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());
@@ -523,7 +522,6 @@ 
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.
@@ -617,7 +615,6 @@ 
PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
 
   FPM.addPass(
       SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
-  FPM.addPass(LifetimeMovePass());
   FPM.addPass(InstCombinePass());
   FPM.addPass(AggressiveInstCombinePass());
 
@@ -717,7 +714,6 @@ 
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.
diff --git a/llvm/test/Other/new-pm-defaults.ll 
b/llvm/test/Other/new-pm-defaults.ll
index 8f9e44447ee91..c0d53b5d6af46 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -163,7 +163,6 @@
 ; 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
@@ -189,7 +188,6 @@
 ; 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
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll 
b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
index 6b791492e38d0..965d463c6f3c5 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -92,7 +92,6 @@
 ; 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
@@ -117,7 +116,6 @@
 ; 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
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 b4cf6834c2b91..0e494a79f5443 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -80,7 +80,6 @@
 ; 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
@@ -103,7 +102,6 @@
 ; 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
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 31f0aa3d36364..aa90c19c7ed82 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -89,7 +89,6 @@
 ; 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
@@ -112,7 +111,6 @@
 ; 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
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll 
b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
index 8953f951bf4eb..3961315c89ba7 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
@@ -124,7 +124,6 @@
 ; 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
@@ -149,7 +148,6 @@
 ; 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
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 753adfe5ab776..3b503f338e3b2 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
@@ -123,7 +123,6 @@
 ; 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
@@ -153,7 +152,6 @@
 ; 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
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 ae24961e06edb..545bdc9154ab1 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
@@ -94,7 +94,6 @@
 ; 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
@@ -117,7 +116,6 @@
 ; 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

>From 211abfebe0da0e959fb078078caf08e982bfdddd Mon Sep 17 00:00:00 2001
From: NewSigma <newsi...@163.com>
Date: Tue, 17 Jun 2025 11:27:22 +0800
Subject: [PATCH 6/8] Resolve review comments

---
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  |  1 -
 llvm/lib/Transforms/Scalar/LifetimeMove.cpp   |  5 +-
 .../Coroutines/coro-split-rise-lifetime-01.ll |  2 +-
 .../Coroutines/coro-split-rise-lifetime-02.ll | 61 --------------
 .../Coroutines/coro-split-rise-lifetime-03.ll | 62 ---------------
 .../Coroutines/coro-split-sink-lifetime-01.ll |  3 +-
 .../Coroutines/coro-split-sink-lifetime-02.ll |  3 +-
 .../Coroutines/coro-split-sink-lifetime-03.ll |  3 +-
 .../Coroutines/coro-split-sink-lifetime-04.ll |  3 +-
 llvm/test/Transforms/LifetimeMove/escape.ll   | 58 ++++++++++++++
 .../Transforms/LifetimeMove/indirect_start.ll | 23 ++++++
 .../Transforms/LifetimeMove/multi_critical.ll | 79 +++++++++++++++++++
 12 files changed, 169 insertions(+), 134 deletions(-)
 delete mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 delete mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.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/multi_critical.ll

diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index a7d7e6ef6d710..8119f9fa3e02d 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,7 +18,6 @@
 #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"
diff --git a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp 
b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
index 2517d4553ee23..5beccd1d34dc4 100644
--- a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
+++ b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
@@ -218,6 +218,9 @@ PreservedAnalyses LifetimeMovePass::run(Function &F,
   const DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(F);
   LifetimeMover Mover(F, DT);
   Mover.run();
-  return PreservedAnalyses::all();
+
+  PreservedAnalyses PA;
+  PA.preserveSet<CFGAnalyses>();
+  return PA;
 }
 } // namespace llvm
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 d38b4deac5533..967221baf57cd 100644
--- a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
+++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
@@ -1,4 +1,4 @@
-; Test allocas that do not cross suspension point will not go to frame
+; 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
diff --git a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll 
b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
deleted file mode 100644
index 66a8d2d65727f..0000000000000
--- a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
+++ /dev/null
@@ -1,61 +0,0 @@
-; 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
deleted file mode 100644
index 4cbd97a305dab..0000000000000
--- a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll
+++ /dev/null
@@ -1,62 +0,0 @@
-; 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 b002d423ed474..c98e7d1a1f2aa 100644
--- a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-01.ll
+++ b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-01.ll
@@ -1,5 +1,4 @@
-; Tests that coro-split will optimize the lifetime.start maker of each local 
variable,
-; sink them to the places after the suspend block.
+; 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 }
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 3e6a1ea599745..491c6444077b7 100644
--- a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-02.ll
+++ b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-02.ll
@@ -1,5 +1,4 @@
-; Tests that coro-split will optimize the lifetime.start maker of each local 
variable,
-; sink them to the places after the suspend block.
+; 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 }
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 4b07e3eb301e2..2749b468061c4 100644
--- a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-03.ll
+++ b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-03.ll
@@ -1,5 +1,4 @@
-; 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.
+; 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 }
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 1fd234349ef88..63aacd056e3df 100644
--- a/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-04.ll
+++ b/llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-04.ll
@@ -1,5 +1,4 @@
-; Tests that coro-split will optimize the lifetime.start maker of each local 
variable,
-; sink them to the places after the suspend block.
+; 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 }
diff --git a/llvm/test/Transforms/LifetimeMove/escape.ll 
b/llvm/test/Transforms/LifetimeMove/escape.ll
new file mode 100644
index 0000000000000..7608c330341e7
--- /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:    call void @llvm.lifetime.start.p0(i64 500, ptr [[ESCAPE_GEP]])
+; 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:    [[TMP0:%.*]] = load ptr, ptr [[GEP_PTR]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[STORE_PTR]], align 8
+; 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)
+  %1 = load ptr, ptr %gep.ptr, align 8
+  %2 = load ptr, ptr %store.ptr, align 8
+  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/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 7e0742183d2f58178e8effa424c112fcc8cfd833 Mon Sep 17 00:00:00 2001
From: NewSigma <newsi...@163.com>
Date: Wed, 18 Jun 2025 22:07:54 +0800
Subject: [PATCH 7/8] Handle loops

---
 llvm/lib/Transforms/Scalar/LifetimeMove.cpp | 57 +++++++++++++++------
 llvm/test/Transforms/LifetimeMove/erase.ll  | 42 +++++++++++++++
 llvm/test/Transforms/LifetimeMove/loop.ll   | 55 ++++++++++++++++++++
 3 files changed, 137 insertions(+), 17 deletions(-)
 create mode 100644 llvm/test/Transforms/LifetimeMove/erase.ll
 create mode 100644 llvm/test/Transforms/LifetimeMove/loop.ll

diff --git a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp 
b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
index 5beccd1d34dc4..255be457e4b1a 100644
--- a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
+++ b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
@@ -13,6 +13,8 @@
 #include "llvm/Transforms/Scalar/LifetimeMove.h"
 #include "llvm/Analysis/CFG.h"
 #include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/PostDominators.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/InstIterator.h"
 #include "llvm/Transforms/Coroutines/CoroInstr.h"
@@ -20,7 +22,7 @@
 #define DEBUG_TYPE "lifetime-move"
 
 namespace llvm {
-static bool mayEscape(Value *V, User *U) {
+static bool mayEscape(Value *V, User *U) { // TODO: Use PtrUseVisitor
   if (V == U->stripInBoundsOffsets() || isa<PHINode>(U))
     return true;
 
@@ -39,6 +41,8 @@ static bool mayEscape(Value *V, User *U) {
 namespace {
 class LifetimeMover {
   const DominatorTree &DT;
+  const PostDominatorTree &PDT;
+  const LoopInfo &LI;
 
   SmallVector<AllocaInst *, 4> Allocas;
   // Critical points are instructions where the crossing of a variable's
@@ -52,7 +56,8 @@ class LifetimeMover {
   SmallPtrSet<BasicBlock *, 2> UserBBs;
 
 public:
-  LifetimeMover(Function &F, const DominatorTree &DT);
+  LifetimeMover(Function &F, const DominatorTree &DT,
+                const PostDominatorTree &PDT, const LoopInfo &LI);
 
   void run();
 
@@ -64,7 +69,9 @@ class LifetimeMover {
 };
 } // namespace
 
-LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT) : DT(DT) {
+LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT,
+                             const PostDominatorTree &PDT, const LoopInfo &LI)
+    : DT(DT), PDT(PDT), LI(LI) {
   for (Instruction &I : instructions(F)) {
     if (auto *AI = dyn_cast<AllocaInst>(&I))
       Allocas.push_back(AI);
@@ -115,15 +122,15 @@ void LifetimeMover::sinkLifetimeStartMarkers(AllocaInst 
*AI) {
 
     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);
+      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;
   };
 
@@ -136,9 +143,11 @@ void LifetimeMover::sinkLifetimeStartMarkers(AllocaInst 
*AI) {
   // 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)))
+    for (auto *P : LifetimeStarts) {
+      bool Reachable = isPotentiallyReachable(DomPoint, P, nullptr, &DT, &LI);
+      if (Reachable && (P == Update(DomPoint, P)))
         return;
+    }
 
     auto *NewStart = LifetimeStarts[0]->clone();
     NewStart->replaceUsesOfWith(NewStart->getOperand(1), AI);
@@ -146,7 +155,7 @@ void LifetimeMover::sinkLifetimeStartMarkers(AllocaInst 
*AI) {
 
     // All the outsided lifetime.start markers are no longer necessary.
     for (auto *I : LifetimeStarts)
-      if (DT.dominates(I, DomPoint))
+      if (PDT.dominates(DomPoint, I))
         I->eraseFromParent();
   }
 }
@@ -158,12 +167,22 @@ void LifetimeMover::riseLifetimeEndMarkers() {
       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);
+      BasicBlock *NewBB = New->getParent();
+      if (UserBB == NewBB) {
+        return llvm::all_of(OtherUsers, [New, UserBB](Instruction *I) {
+          return UserBB != I->getParent() || I->comesBefore(New);
         });
       }
-      return DT.dominates(UserBB, New->getParent());
+
+      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;
   };
@@ -173,9 +192,11 @@ void LifetimeMover::riseLifetimeEndMarkers() {
     DomPoint = Update(DomPoint, P);
 
   if (DomPoint != nullptr) {
-    for (auto *P : LifetimeEnds)
-      if (isPotentiallyReachable(P, DomPoint) && (P == Update(DomPoint, P)))
+    for (auto *P : LifetimeEnds) {
+      bool Reachable = isPotentiallyReachable(P, DomPoint, nullptr, &DT, &LI);
+      if (Reachable && (P == Update(DomPoint, P)))
         return;
+    }
 
     auto *NewEnd = LifetimeEnds[0]->clone();
     NewEnd->insertBefore(DomPoint->getIterator());
@@ -216,7 +237,9 @@ PreservedAnalyses LifetimeMovePass::run(Function &F,
     return PreservedAnalyses::all();
 
   const DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(F);
-  LifetimeMover Mover(F, DT);
+  const PostDominatorTree &PDT = AM.getResult<PostDominatorTreeAnalysis>(F);
+  const LoopInfo &LI = AM.getResult<LoopAnalysis>(F);
+  LifetimeMover Mover(F, DT, PDT, LI);
   Mover.run();
 
   PreservedAnalyses PA;
diff --git a/llvm/test/Transforms/LifetimeMove/erase.ll 
b/llvm/test/Transforms/LifetimeMove/erase.ll
new file mode 100644
index 0000000000000..4df06e8afcdab
--- /dev/null
+++ b/llvm/test/Transforms/LifetimeMove/erase.ll
@@ -0,0 +1,42 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 
UTC_ARGS: --version 5
+; Test we do not erase lifetime markers that shorten lifetime of alloca
+; 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:    [[TESTVAL:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 4, ptr [[TESTVAL]])
+; CHECK-NEXT:    [[COND:%.*]] = call i1 @produce()
+; CHECK-NEXT:    br i1 [[COND]], label %[[TRUE:.*]], label %[[FALSE:.*]]
+; CHECK:       [[TRUE]]:
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 4, ptr [[TESTVAL]])
+; CHECK-NEXT:    [[UNUSED:%.*]] = call i8 @llvm.coro.suspend(token none, i1 
false)
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 4, ptr [[TESTVAL]])
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[FALSE]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 4, ptr [[TESTVAL]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %testval = alloca i32
+  call void @llvm.lifetime.start.p0(i64 4, ptr %testval)
+  %cond = call i1 @produce()
+  br i1 %cond, label %true, label %false
+
+true:
+  %unused = call i8 @llvm.coro.suspend(token none, i1 false)
+  br label %exit
+
+false:
+  br label %exit
+
+exit:
+  call void @llvm.lifetime.end.p0(i64 4, ptr  %testval)
+  ret void
+}
+
+declare i1 @produce()
diff --git a/llvm/test/Transforms/LifetimeMove/loop.ll 
b/llvm/test/Transforms/LifetimeMove/loop.ll
new file mode 100644
index 0000000000000..acffcd1154540
--- /dev/null
+++ b/llvm/test/Transforms/LifetimeMove/loop.ll
@@ -0,0 +1,55 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 
UTC_ARGS: --version 5
+; Test we correctly expose loop exiting blocks for lifetime.end
+; 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:    [[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
+}

>From 0d5882da0918596dbbf089ccfed884088c811f45 Mon Sep 17 00:00:00 2001
From: NewSigma <newsi...@163.com>
Date: Thu, 19 Jun 2025 17:04:19 +0800
Subject: [PATCH 8/8] Use PtrUseVisitor to trace indirect use

---
 llvm/lib/Transforms/Scalar/LifetimeMove.cpp | 171 +++++++++++++-------
 llvm/test/Transforms/LifetimeMove/escape.ll |  10 +-
 2 files changed, 121 insertions(+), 60 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp 
b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
index 255be457e4b1a..65ed1dcf9d1c6 100644
--- a/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
+++ b/llvm/lib/Transforms/Scalar/LifetimeMove.cpp
@@ -15,6 +15,7 @@
 #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"
@@ -22,24 +23,11 @@
 #define DEBUG_TYPE "lifetime-move"
 
 namespace llvm {
-static bool mayEscape(Value *V, User *U) { // TODO: Use PtrUseVisitor
-  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 {
+class LifetimeMover : public PtrUseVisitor<LifetimeMover> {
+  using This = LifetimeMover;
+  using Base = PtrUseVisitor<LifetimeMover>;
+
   const DominatorTree &DT;
   const PostDominatorTree &PDT;
   const LoopInfo &LI;
@@ -61,17 +49,23 @@ class LifetimeMover {
 
   void run();
 
+  void visitInstruction(Instruction &I);
+  void visitPHINode(PHINode &I);
+  void visitSelectInst(SelectInst &I);
+  void visitStoreInst(StoreInst &SI);
+  void visitIntrinsicInst(IntrinsicInst &II);
+  void visitCallBase(CallBase &CB);
+
 private:
   void sinkLifetimeStartMarkers(AllocaInst *AI);
   void riseLifetimeEndMarkers();
-  bool collectLifetime(Instruction *I);
   void reset();
 };
 } // namespace
 
 LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT,
                              const PostDominatorTree &PDT, const LoopInfo &LI)
-    : DT(DT), PDT(PDT), LI(LI) {
+    : Base(F.getDataLayout()), DT(DT), PDT(PDT), LI(LI) {
   for (Instruction &I : instructions(F)) {
     if (auto *AI = dyn_cast<AllocaInst>(&I))
       Allocas.push_back(AI);
@@ -83,34 +77,113 @@ LifetimeMover::LifetimeMover(Function &F, const 
DominatorTree &DT,
 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());
-    }
+    Base::visitPtr(*AI);
 
     if (!LifetimeStarts.empty())
       sinkLifetimeStartMarkers(AI);
 
     // Do not move lifetime.end if alloca escapes
-    if (!LifetimeEnds.empty() && !Escape)
+    if (!LifetimeEnds.empty() && !PI.isEscaped())
       riseLifetimeEndMarkers();
   }
 }
+
+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);
+    break;
+  case Intrinsic::lifetime_end:
+    LifetimeEnds.push_back(&II);
+    break;
+  default:
+    Base::visitIntrinsicInst(II);
+  }
+}
+
+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.
@@ -207,23 +280,11 @@ void LifetimeMover::riseLifetimeEndMarkers() {
   }
 }
 
-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() {
+  PI.reset();
+  Worklist.clear();
+  VisitedUses.clear();
+
   LifetimeStarts.clear();
   LifetimeEnds.clear();
   OtherUsers.clear();
diff --git a/llvm/test/Transforms/LifetimeMove/escape.ll 
b/llvm/test/Transforms/LifetimeMove/escape.ll
index 7608c330341e7..8cfe13646f647 100644
--- a/llvm/test/Transforms/LifetimeMove/escape.ll
+++ b/llvm/test/Transforms/LifetimeMove/escape.ll
@@ -7,7 +7,6 @@ define void @fn() presplitcoroutine {
 ; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[ESCAPE_GEP:%.*]] = alloca [500 x i8], align 16
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 500, ptr [[ESCAPE_GEP]])
 ; 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
@@ -18,8 +17,9 @@ define void @fn() presplitcoroutine {
 ; 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:    [[TMP0:%.*]] = load ptr, ptr [[GEP_PTR]], align 8
-; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[STORE_PTR]], align 8
+; 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]])
@@ -44,8 +44,8 @@ entry:
   call void @capture(ptr %escape.call)
 
   %unused = call i8 @llvm.coro.suspend(token none, i1 false)
-  %1 = load ptr, ptr %gep.ptr, align 8
-  %2 = load ptr, ptr %store.ptr, align 8
+  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)

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to