llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-ir

Author: Weibo He (NewSigma)

<details>
<summary>Changes</summary>

As suggested by @<!-- -->ChuanqiXu9 in #<!-- -->143333 , I propose 
LifetimeMovePass, which was previously part of the CoroSplit pass, should now 
appear as a general pass. Lifetime markers determine whether we place alloca on 
the frame or on the stack. By moving these markers to optimized positions, we 
can reduce the coroutine frame size, leading to significant memory savings.
The LifetimeMovePass is positioned between SimplifyCFG and InstCombine. 
Currently, it only applies to pre-split coroutines, as I have not yet developed 
a concrete plan for its interaction with non-coroutine code. This patch is WIP, 
feel free to share your feedback or suggestions.

Close #<!-- -->49716 

---

Patch is 47.61 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/144319.diff


28 Files Affected:

- (modified) clang/test/CodeGenCoroutines/pr56919.cpp (+3-3) 
- (modified) llvm/docs/Coroutines.rst (+3-3) 
- (modified) llvm/include/llvm/IR/Intrinsics.td (+3-3) 
- (added) llvm/include/llvm/Transforms/Utils/LifetimeMove.h (+23) 
- (modified) llvm/lib/Passes/PassBuilder.cpp (+1) 
- (modified) llvm/lib/Passes/PassBuilderPipelines.cpp (+5) 
- (modified) llvm/lib/Passes/PassRegistry.def (+1) 
- (modified) llvm/lib/Transforms/Coroutines/CoroFrame.cpp (-87) 
- (modified) llvm/lib/Transforms/Utils/CMakeLists.txt (+1) 
- (added) llvm/lib/Transforms/Utils/LifetimeMove.cpp (+335) 
- (modified) llvm/test/Other/new-pm-defaults.ll (+1) 
- (modified) llvm/test/Other/new-pm-pgo-preinline.ll (+1) 
- (modified) llvm/test/Other/new-pm-thinlto-postlink-defaults.ll (+1) 
- (modified) llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll (+1) 
- (modified) llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll (+1) 
- (modified) llvm/test/Other/new-pm-thinlto-prelink-defaults.ll (+1) 
- (modified) llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll (+2) 
- (modified) llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll (+1) 
- (modified) llvm/test/Transforms/Coroutines/coro-alloca-07.ll (+3-1) 
- (added) llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll (+39) 
- (modified) llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-01.ll 
(+3-4) 
- (modified) llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-02.ll 
(+2-3) 
- (modified) llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-03.ll 
(+2-3) 
- (modified) llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-04.ll 
(+3-4) 
- (added) llvm/test/Transforms/LifetimeMove/erase.ll (+62) 
- (added) llvm/test/Transforms/LifetimeMove/escape.ll (+58) 
- (added) llvm/test/Transforms/LifetimeMove/loop.ll (+84) 
- (added) llvm/test/Transforms/LifetimeMove/multi_critical.ll (+79) 


``````````diff
diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task<void> Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl    $40, %esi
+// CHECK-NEXT: movl    $32, %esi
 // CHECK-NEXT: jmp     _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl    $80, %esi
+// CHECK-NEXT: movl    $64, %esi
 // CHECK-NEXT: jmp     _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl    $120, %esi
+// CHECK-NEXT: movl    $96, %esi
 // CHECK-NEXT: jmp     _ZdlPvm
diff --git a/llvm/docs/Coroutines.rst b/llvm/docs/Coroutines.rst
index dde73c9c3cc23..e6788141f79e0 100644
--- a/llvm/docs/Coroutines.rst
+++ b/llvm/docs/Coroutines.rst
@@ -1863,7 +1863,7 @@ executes a call to ``llvm.coro.suspend.retcon`` after 
resuming in any way.
 ::
 
   declare void @llvm.coro.await.suspend.void(
-                ptr <awaiter>,
+                ptr captures(none) <awaiter>,
                 ptr <handle>,
                 ptr <await_suspend_function>)
 
@@ -1945,7 +1945,7 @@ Example:
 ::
 
   declare i1 @llvm.coro.await.suspend.bool(
-                ptr <awaiter>,
+                ptr captures(none) <awaiter>,
                 ptr <handle>,
                 ptr <await_suspend_function>)
 
@@ -2035,7 +2035,7 @@ Example:
 ::
 
   declare void @llvm.coro.await.suspend.handle(
-                ptr <awaiter>,
+                ptr captures(none) <awaiter>,
                 ptr <handle>,
                 ptr <await_suspend_function>)
 
diff --git a/llvm/include/llvm/IR/Intrinsics.td 
b/llvm/include/llvm/IR/Intrinsics.td
index e0ee12391b31d..46ac0d95740d0 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1808,15 +1808,15 @@ def int_coro_promise : Intrinsic<[llvm_ptr_ty],
 
 def int_coro_await_suspend_void : Intrinsic<[],
                                        [llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty],
-                                       [Throws]>;
+                                       [Throws, NoCapture<ArgIndex<0>>]>;
 
 def int_coro_await_suspend_bool : Intrinsic<[llvm_i1_ty],
                                             [llvm_ptr_ty, llvm_ptr_ty, 
llvm_ptr_ty],
-                                            [Throws]>;
+                                            [Throws, NoCapture<ArgIndex<0>>]>;
 
 def int_coro_await_suspend_handle : Intrinsic<[],
                                               [llvm_ptr_ty, llvm_ptr_ty, 
llvm_ptr_ty],
-                                              [Throws]>;
+                                              [Throws, 
NoCapture<ArgIndex<0>>]>;
 
 // Coroutine Lowering Intrinsics. Used internally by coroutine passes.
 
diff --git a/llvm/include/llvm/Transforms/Utils/LifetimeMove.h 
b/llvm/include/llvm/Transforms/Utils/LifetimeMove.h
new file mode 100644
index 0000000000000..f9a690433cb77
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/LifetimeMove.h
@@ -0,0 +1,23 @@
+//===- LifetimeMove.h - Narrowing lifetimes ---------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+#define LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+struct LifetimeMovePass : PassInfoMixin<LifetimeMovePass> {
+  /// Run the pass over the function.
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index f810368a84940..389e10fe258c6 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -353,6 +353,7 @@
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
 #include "llvm/Transforms/Utils/InstructionNamer.h"
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
+#include "llvm/Transforms/Utils/LifetimeMove.h"
 #include "llvm/Transforms/Utils/LoopSimplify.h"
 #include "llvm/Transforms/Utils/LoopVersioning.h"
 #include "llvm/Transforms/Utils/LowerGlobalDtors.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp 
b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 98821bb1408a7..4740e9be08948 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -137,6 +137,7 @@
 #include "llvm/Transforms/Utils/ExtraPassManager.h"
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
+#include "llvm/Transforms/Utils/LifetimeMove.h"
 #include "llvm/Transforms/Utils/Mem2Reg.h"
 #include "llvm/Transforms/Utils/MoveAutoInit.h"
 #include "llvm/Transforms/Utils/NameAnonGlobals.h"
@@ -557,6 +558,7 @@ 
PassBuilder::buildO1FunctionSimplificationPipeline(OptimizationLevel Level,
   FPM.addPass(ADCEPass());
   FPM.addPass(
       SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
+  FPM.addPass(LifetimeMovePass());
   FPM.addPass(InstCombinePass());
   invokePeepholeEPCallbacks(FPM, Level);
 
@@ -777,6 +779,7 @@ 
PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
                                   .convertSwitchRangeToICmp(true)
                                   .hoistCommonInsts(true)
                                   .sinkCommonInsts(true)));
+  FPM.addPass(LifetimeMovePass());
   FPM.addPass(InstCombinePass());
   invokePeepholeEPCallbacks(FPM, Level);
 
@@ -813,6 +816,7 @@ void PassBuilder::addPreInlinerPasses(ModulePassManager 
&MPM,
   FPM.addPass(EarlyCSEPass()); // Catch trivial redundancies.
   FPM.addPass(SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(
       true)));                    // Merge & remove basic blocks.
+  FPM.addPass(LifetimeMovePass());
   FPM.addPass(InstCombinePass()); // Combine silly sequences.
   invokePeepholeEPCallbacks(FPM, Level);
 
@@ -1356,6 +1360,7 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level,
                                         /*UseBlockFrequencyInfo=*/true));
     ExtraPasses.addPass(
         SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));
+    ExtraPasses.addPass(LifetimeMovePass());
     ExtraPasses.addPass(InstCombinePass());
     FPM.addPass(std::move(ExtraPasses));
   }
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 1b111dc20d35c..ec58773be397d 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -457,6 +457,7 @@ FUNCTION_PASS("kcfi", KCFIPass())
 FUNCTION_PASS("kernel-info", KernelInfoPrinter(TM))
 FUNCTION_PASS("lcssa", LCSSAPass())
 FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrinkWrapPass())
+FUNCTION_PASS("lifetime-move", LifetimeMovePass())
 FUNCTION_PASS("load-store-vectorizer", LoadStoreVectorizerPass())
 FUNCTION_PASS("loop-data-prefetch", LoopDataPrefetchPass())
 FUNCTION_PASS("loop-distribute", LoopDistributePass())
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index b775c43460190..95a6d708bb78e 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -1751,89 +1751,6 @@ static void eliminateSwiftError(Function &F, coro::Shape 
&Shape) {
   }
 }
 
-/// For each local variable that all of its user are only used inside one of
-/// suspended region, we sink their lifetime.start markers to the place where
-/// after the suspend block. Doing so minimizes the lifetime of each variable,
-/// hence minimizing the amount of data we end up putting on the frame.
-static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape,
-                                     SuspendCrossingInfo &Checker,
-                                     const DominatorTree &DT) {
-  if (F.hasOptNone())
-    return;
-
-  // Collect all possible basic blocks which may dominate all uses of allocas.
-  SmallPtrSet<BasicBlock *, 4> DomSet;
-  DomSet.insert(&F.getEntryBlock());
-  for (auto *CSI : Shape.CoroSuspends) {
-    BasicBlock *SuspendBlock = CSI->getParent();
-    assert(coro::isSuspendBlock(SuspendBlock) &&
-           SuspendBlock->getSingleSuccessor() &&
-           "should have split coro.suspend into its own block");
-    DomSet.insert(SuspendBlock->getSingleSuccessor());
-  }
-
-  for (Instruction &I : instructions(F)) {
-    AllocaInst* AI = dyn_cast<AllocaInst>(&I);
-    if (!AI)
-      continue;
-
-    for (BasicBlock *DomBB : DomSet) {
-      bool Valid = true;
-      SmallVector<Instruction *, 1> Lifetimes;
-
-      auto isLifetimeStart = [](Instruction* I) {
-        if (auto* II = dyn_cast<IntrinsicInst>(I))
-          return II->getIntrinsicID() == Intrinsic::lifetime_start;
-        return false;
-      };
-
-      auto collectLifetimeStart = [&](Instruction *U, AllocaInst *AI) {
-        if (isLifetimeStart(U)) {
-          Lifetimes.push_back(U);
-          return true;
-        }
-        if (!U->hasOneUse() || U->stripPointerCasts() != AI)
-          return false;
-        if (isLifetimeStart(U->user_back())) {
-          Lifetimes.push_back(U->user_back());
-          return true;
-        }
-        return false;
-      };
-
-      for (User *U : AI->users()) {
-        Instruction *UI = cast<Instruction>(U);
-        // For all users except lifetime.start markers, if they are all
-        // dominated by one of the basic blocks and do not cross
-        // suspend points as well, then there is no need to spill the
-        // instruction.
-        if (!DT.dominates(DomBB, UI->getParent()) ||
-            Checker.isDefinitionAcrossSuspend(DomBB, UI)) {
-          // Skip lifetime.start, GEP and bitcast used by lifetime.start
-          // markers.
-          if (collectLifetimeStart(UI, AI))
-            continue;
-          Valid = false;
-          break;
-        }
-      }
-      // Sink lifetime.start markers to dominate block when they are
-      // only used outside the region.
-      if (Valid && Lifetimes.size() != 0) {
-        auto *NewLifetime = Lifetimes[0]->clone();
-        NewLifetime->replaceUsesOfWith(NewLifetime->getOperand(0), AI);
-        NewLifetime->insertBefore(DomBB->getTerminator()->getIterator());
-
-        // All the outsided lifetime.start markers are no longer necessary.
-        for (Instruction *S : Lifetimes)
-          S->eraseFromParent();
-
-        break;
-      }
-    }
-  }
-}
-
 static std::optional<std::pair<Value &, DIExpression &>>
 salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> 
&ArgToAllocaMap,
                      bool UseEntryValue, Function *F, Value *Storage,
@@ -2012,10 +1929,6 @@ void coro::BaseABI::buildCoroutineFrame(bool 
OptimizeFrame) {
   doRematerializations(F, Checker, IsMaterializable);
 
   const DominatorTree DT(F);
-  if (Shape.ABI != coro::ABI::Async && Shape.ABI != coro::ABI::Retcon &&
-      Shape.ABI != coro::ABI::RetconOnce)
-    sinkLifetimeStartMarkers(F, Shape, Checker, DT);
-
   // All values (that are not allocas) that needs to be spilled to the frame.
   coro::SpillInfo Spills;
   // All values defined as allocas that need to live in the frame.
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt 
b/llvm/lib/Transforms/Utils/CMakeLists.txt
index e411d68570096..3c211b6f7a4dc 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -41,6 +41,7 @@ add_llvm_component_library(LLVMTransformUtils
   IRNormalizer.cpp
   LCSSA.cpp
   LibCallsShrinkWrap.cpp
+  LifetimeMove.cpp
   Local.cpp
   LoopConstrainer.cpp
   LoopPeel.cpp
diff --git a/llvm/lib/Transforms/Utils/LifetimeMove.cpp 
b/llvm/lib/Transforms/Utils/LifetimeMove.cpp
new file mode 100644
index 0000000000000..4051bcdd0147b
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/LifetimeMove.cpp
@@ -0,0 +1,335 @@
+//===- LifetimeMove.cpp - Narrowing lifetimes 
-----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The LifetimeMovePass identifies the precise lifetime range of allocas and
+// repositions lifetime markers to stricter positions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/LifetimeMove.h"
+#include "llvm/Analysis/CFG.h"
+#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Analysis/PtrUseVisitor.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/Transforms/Coroutines/CoroInstr.h"
+
+#define DEBUG_TYPE "lifetime-move"
+
+namespace llvm {
+namespace {
+class LifetimeMover : public PtrUseVisitor<LifetimeMover> {
+  using This = LifetimeMover;
+  using Base = PtrUseVisitor<LifetimeMover>;
+
+  const DominatorTree &DT;
+  const LoopInfo &LI;
+
+  SmallVector<AllocaInst *, 4> Allocas;
+  // Critical points are instructions where the crossing of a variable's
+  // lifetime makes a difference. We attempt to rise lifetime.end
+  // before critical points and sink lifetime.start after them.
+  SmallVector<Instruction *, 4> CriticalPoints;
+
+  SmallVector<Instruction *, 2> LifetimeStarts;
+  SmallVector<Instruction *, 2> LifetimeEnds;
+  SmallVector<Instruction *, 8> OtherUsers;
+  SmallPtrSet<BasicBlock *, 2> LifetimeStartBBs;
+  SmallPtrSet<BasicBlock *, 2> UserBBs;
+
+public:
+  LifetimeMover(Function &F, const DominatorTree &DT, const LoopInfo &LI);
+
+  bool run();
+
+  void visitInstruction(Instruction &I);
+  void visitPHINode(PHINode &I);
+  void visitSelectInst(SelectInst &I);
+  void visitStoreInst(StoreInst &SI);
+  void visitIntrinsicInst(IntrinsicInst &II);
+  void visitMemIntrinsic(MemIntrinsic &I);
+  void visitCallBase(CallBase &CB);
+
+private:
+  bool sinkLifetimeStartMarkers(AllocaInst *AI);
+  bool riseLifetimeEndMarkers();
+  void reset();
+};
+} // namespace
+
+LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT,
+                             const LoopInfo &LI)
+    : Base(F.getDataLayout()), DT(DT), LI(LI) {
+  for (Instruction &I : instructions(F)) {
+    if (auto *AI = dyn_cast<AllocaInst>(&I))
+      Allocas.push_back(AI);
+    else if (isa<LifetimeIntrinsic>(I))
+      continue;
+    else if (isa<AnyCoroSuspendInst>(I))
+      CriticalPoints.push_back(&I);
+    else if (isa<CallInst>(I))
+      CriticalPoints.push_back(&I);
+    else if (isa<InvokeInst>(I))
+      CriticalPoints.push_back(&I);
+  }
+}
+
+bool LifetimeMover::run() {
+  bool Changed = false;
+  for (auto *AI : Allocas) {
+    reset();
+    Base::visitPtr(*AI);
+
+    if (!LifetimeStarts.empty())
+      Changed |= sinkLifetimeStartMarkers(AI);
+
+    // Do not move lifetime.end if alloca escapes
+    if (!LifetimeEnds.empty() && !PI.isEscaped())
+      Changed |= riseLifetimeEndMarkers();
+  }
+  return Changed;
+}
+
+void LifetimeMover::visitInstruction(Instruction &I) {
+  OtherUsers.push_back(&I);
+  UserBBs.insert(I.getParent());
+}
+
+void LifetimeMover::visitPHINode(PHINode &I) { enqueueUsers(I); }
+
+void LifetimeMover::visitSelectInst(SelectInst &I) { enqueueUsers(I); }
+
+void LifetimeMover::visitStoreInst(StoreInst &SI) {
+  if (SI.getPointerOperand() == U->get())
+    return InstVisitor<This>::visitStoreInst(SI);
+
+  // We are storing the pointer into a memory location, potentially escaping.
+  // As an optimization, we try to detect simple cases where it doesn't
+  // actually escape, for example:
+  //   %ptr = alloca ..
+  //   %addr = alloca ..
+  //   store %ptr, %addr
+  //   %x = load %addr
+  //   ..
+  // If %addr is only used by loading from it, we could simply treat %x as
+  // another alias of %ptr, and not considering %ptr being escaped.
+  auto IsSimpleStoreThenLoad = [&]() {
+    auto *AI = dyn_cast<AllocaInst>(SI.getPointerOperand());
+    // If the memory location we are storing to is not an alloca, it
+    // could be an alias of some other memory locations, which is difficult
+    // to analyze.
+    if (!AI)
+      return false;
+    // StoreAliases contains aliases of the memory location stored into.
+    SmallVector<Instruction *, 4> StoreAliases = {AI};
+    while (!StoreAliases.empty()) {
+      Instruction *I = StoreAliases.pop_back_val();
+      for (User *U : I->users()) {
+        // If we are loading from the memory location, we are creating an
+        // alias of the original pointer.
+        if (auto *LI = dyn_cast<LoadInst>(U)) {
+          enqueueUsers(*LI);
+          continue;
+        }
+        // If we are overriding the memory location, the pointer certainly
+        // won't escape.
+        if (auto *S = dyn_cast<StoreInst>(U))
+          if (S->getPointerOperand() == I)
+            continue;
+        if (isa<LifetimeIntrinsic>(U))
+          continue;
+        // BitCastInst creats aliases of the memory location being stored
+        // into.
+        if (auto *BI = dyn_cast<BitCastInst>(U)) {
+          StoreAliases.push_back(BI);
+          continue;
+        }
+        return false;
+      }
+    }
+
+    return true;
+  };
+
+  if (!IsSimpleStoreThenLoad())
+    PI.setEscaped(&SI);
+  InstVisitor<This>::visitStoreInst(SI);
+}
+
+void LifetimeMover::visitIntrinsicInst(IntrinsicInst &II) {
+  // lifetime markers are not actual uses
+  switch (II.getIntrinsicID()) {
+  case Intrinsic::lifetime_start:
+    LifetimeStarts.push_back(&II);
+    LifetimeStartBBs.insert(II.getParent());
+    break;
+  case Intrinsic::lifetime_end:
+    LifetimeEnds.push_back(&II);
+    break;
+  default:
+    Base::visitIntrinsicInst(II);
+  }
+}
+
+void LifetimeMover::visitMemIntrinsic(MemIntrinsic &I) { visitInstruction(I); }
+
+void LifetimeMover::visitCallBase(CallBase &CB) {
+  for (unsigned Op = 0, OpCount = CB.arg_size(); Op < OpCount; ++Op)
+    if (U->get() == CB.getArgOperand(Op) && !CB.doesNotCapture(Op))
+      PI.setEscaped(&CB);
+  InstVisitor<This>::visitCallBase(CB);
+}
+/// For each local variable that all of its user are dominated by one of the
+/// critical point, we sink their lifetime.start markers to the place where
+/// after the critical point. Doing so minimizes the lifetime of each variable.
+bool LifetimeMover::sinkLifetimeStartMarkers(AllocaInst *AI) {
+  auto Update = [this](Instruction *Old, Instruction *New) {
+    // Reject the new proposal if it lengthens lifetime
+    if (DT.dominates(New, Old))
+      return Old;
+
+    if (LI.getLoopFor(New->getParent()))
+      return Old;
+
+    bool DomAll = llvm::all_of(UserBBs, [this, New](BasicBlock *UserBB) {
+      // Instruction level analysis if lifetime and users share a common BB
+      BasicBlock *NewBB = New->getParent();
+      if (UserBB == NewBB) {
+        return llvm::all_of(OtherUsers, [New, UserBB](Instruction *I) {
+          return UserBB != I->getParent() || New->comesBefore(I);
+        });
+      }
+      // Otherwise, BB level analysis is enough
+      return DT.dominates(New, UserBB);
+    });
+    return DomAll ? New : Old;
+  };
+
+  // AllocaInst is a trivial critical point
+  Instruction *DomPoint = AI;
+  for (auto *P : CriticalPoints)
+    DomPoint = Update(DomPoint, P);
+
+  // Sink lifetime.start markers to dominate block when they are
+  // only used outside the region.
+  if (DomPoint != AI) {
+    // If existing position is better, do nothing
+    for (auto *P : LifetimeStarts) {
+      if (P == Update(DomPoint, P))
+        return false;
+    }
+
+    auto *NewStart = LifetimeStarts[0]->clone();
+    NewStart->replaceUsesOfWith(NewStart->getOperand(0), AI);
+    if (DomPoint->isTerminator())
+      NewStart->insertBefore(
+          cast<InvokeInst>(DomPoint)->getNormalDest()->getFirstNonPHIIt());
+    else
+      NewStart->insertAfter(DomPoint->getIterator());
+
+    // All the outsided lifetime.start markers are no longer necessary.
+    for (auto *I : LifetimeStarts) {
+      if (LI.getLoopFor(I->getParent()))
+        contin...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/144319
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to