[clang] [llvm] [Coroutines] Mark parameter allocas with coro.outside.frame metadata (PR #127653)

Hans Wennborg via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 27 05:22:29 PST 2025


https://github.com/zmodem updated https://github.com/llvm/llvm-project/pull/127653

>From cde82a27139c39406a9afb5b471fa527e52e5bca Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans at chromium.org>
Date: Tue, 18 Feb 2025 15:27:37 +0100
Subject: [PATCH 1/7] [Coroutines] Mark parameter allocas with
 coro.outside.frame metadata

Parameters to a coroutine get copied (moved) to coroutine-local
instances which code inside the coroutine then uses.

The original parameters should not be part of the frame. Normally
CoroSplit figures that out by itself, but for [[clang::trivial_abi]]
parameters which, get destructed at the end of the ramp function,
it does not (see bug), causing use-after-free's if the frame is
destroyed before the end of the ramp (as happens if it doesn't suspend).

Since Clang knows these should never be part of the frame, use metadata
to make it so.

Fixes #127499
---
 clang/lib/CodeGen/CGCoroutine.cpp            | 10 +++++
 clang/test/CodeGenCoroutines/coro-params.cpp | 39 +++++++++++++++-----
 2 files changed, 40 insertions(+), 9 deletions(-)

diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index 9abf2e8c9190d..cdc61676524b4 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -855,6 +855,16 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
     // Create parameter copies. We do it before creating a promise, since an
     // evolution of coroutine TS may allow promise constructor to observe
     // parameter copies.
+    for (const ParmVarDecl *Parm : FnArgs) {
+      // If the original param is in an alloca, exclude it from the coroutine
+      // frame. The parameter copy will be part of the frame.
+      Address ParmAddr = GetAddrOfLocalVar(Parm);
+      if (auto *ParmAlloca =
+              dyn_cast<llvm::AllocaInst>(ParmAddr.getBasePointer())) {
+        ParmAlloca->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
+                                llvm::MDNode::get(CGM.getLLVMContext(), {}));
+      }
+    }
     for (auto *PM : S.getParamMoves()) {
       EmitStmt(PM);
       ParamReplacer.addCopy(cast<DeclStmt>(PM));
diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp b/clang/test/CodeGenCoroutines/coro-params.cpp
index b318f2f52ac09..9e640fb2c5c62 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -59,13 +59,22 @@ struct MoveAndCopy {
   ~MoveAndCopy();
 };
 
-void consume(int,int,int) noexcept;
+struct [[clang::trivial_abi]] TrivialABI {
+  int val;
+  TrivialABI(MoveAndCopy&&) noexcept;
+  ~TrivialABI();
+};
+
+void consume(int,int,int,int) noexcept;
 
 // TODO: Add support for CopyOnly params
-// CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy(i32 noundef %val, ptr noundef %[[MoParam:.+]], ptr noundef %[[McParam:.+]]) #0 personality ptr @__gxx_personality_v0
-void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
+// CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy10TrivialABI(i32 noundef %val, ptr noundef %[[MoParam:.+]], ptr noundef %[[McParam:.+]], i32 %[[TrivialParam:.+]]) #0 personality ptr @__gxx_personality_v0
+void f(int val, MoveOnly moParam, MoveAndCopy mcParam, TrivialABI trivialParam) {
+  // CHECK: %[[TrivialAlloca:.+]] = alloca %struct.TrivialABI,
+  // CHECK-SAME: !coro.outside.frame
   // CHECK: %[[MoCopy:.+]] = alloca %struct.MoveOnly,
   // CHECK: %[[McCopy:.+]] = alloca %struct.MoveAndCopy,
+  // CHECK: %[[TrivialCopy:.+]] = alloca %struct.TrivialABI,
   // CHECK: store i32 %val, ptr %[[ValAddr:.+]]
 
   // CHECK: call ptr @llvm.coro.begin(
@@ -73,25 +82,33 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr {{[^,]*}} %[[McCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[McParam]]) #
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
-  // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev(
+  // CHECK-NEXT: call void @llvm.memcpy
+  // CHECK-SAME: %[[TrivialCopy]]
+  // CHECK-SAME: %[[TrivialAlloca]]
+  // CHECK-NEXT: call void @llvm.lifetime.start.p0(
+  // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeC1Ev(
 
   // CHECK: call void @_ZN14suspend_always12await_resumeEv(
   // CHECK: %[[IntParam:.+]] = load i32, ptr %{{.*}}
   // CHECK: %[[MoGep:.+]] = getelementptr inbounds nuw %struct.MoveOnly, ptr %[[MoCopy]], i32 0, i32 0
   // CHECK: %[[MoVal:.+]] = load i32, ptr %[[MoGep]]
-  // CHECK: %[[McGep:.+]] =  getelementptr inbounds nuw %struct.MoveAndCopy, ptr %[[McCopy]], i32 0, i32 0
+  // CHECK: %[[McGep:.+]] = getelementptr inbounds nuw %struct.MoveAndCopy, ptr %[[McCopy]], i32 0, i32 0
   // CHECK: %[[McVal:.+]] = load i32, ptr %[[McGep]]
-  // CHECK: call void @_Z7consumeiii(i32 noundef %[[IntParam]], i32 noundef %[[MoVal]], i32 noundef %[[McVal]])
+  // CHECK: %[[TrivialGep:.+]] = getelementptr inbounds nuw %struct.TrivialABI, ptr %[[TrivialCopy]], i32 0, i32 0
+  // CHECK: %[[TrivialVal:.+]] = load i32, ptr %[[TrivialGep]]
+  // CHECK: call void @_Z7consumeiiii(i32 noundef %[[IntParam]], i32 noundef %[[MoVal]], i32 noundef %[[McVal]], i32 noundef %[[TrivialVal]])
 
-  consume(val, moParam.val, mcParam.val);
+  consume(val, moParam.val, mcParam.val, trivialParam.val);
   co_return;
 
   // Skip to final suspend:
-  // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv(
+  // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_type13final_suspendEv(
   // CHECK: call void @_ZN14suspend_always12await_resumeEv(
 
   // Destroy promise, then parameter copies:
-  // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(ptr {{[^,]*}} %__promise)
+  // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeD1Ev(ptr {{[^,]*}} %__promise)
+  // CHECK-NEXT: call void @llvm.lifetime.end.p0(
+  // CHECK-NEXT: call void @_ZN10TrivialABID1Ev(ptr {{[^,]*}} %[[TrivialCopy]])
   // CHECK-NEXT: call void @llvm.lifetime.end.p0(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(ptr {{[^,]*}} %[[McCopy]])
   // CHECK-NEXT: call void @llvm.lifetime.end.p0(
@@ -99,6 +116,10 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
   // CHECK-NEXT: call void @llvm.lifetime.end.p0(
   // CHECK-NEXT: call void @llvm.lifetime.end.p0(
   // CHECK-NEXT: call ptr @llvm.coro.free(
+
+  // The original trivial_abi parameter is destroyed when returning from the ramp.
+  // CHECK: call i1 @llvm.coro.end
+  // CHECK: call void @_ZN10TrivialABID1Ev(ptr {{[^,]*}} %[[TrivialAlloca]])
 }
 
 // CHECK-LABEL: void @_Z16dependent_paramsI1A1BEvT_T0_S3_(ptr noundef %x, ptr noundef %0, ptr noundef %y)

>From 4605efbb978d3212efd5aa6cc2dfd0c98a586da5 Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans at chromium.org>
Date: Tue, 18 Feb 2025 18:20:19 +0100
Subject: [PATCH 2/7] fix the TrivialABI move ctor

---
 clang/test/CodeGenCoroutines/coro-params.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp b/clang/test/CodeGenCoroutines/coro-params.cpp
index 9e640fb2c5c62..86b0f54b4d486 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -61,7 +61,7 @@ struct MoveAndCopy {
 
 struct [[clang::trivial_abi]] TrivialABI {
   int val;
-  TrivialABI(MoveAndCopy&&) noexcept;
+  TrivialABI(TrivialABI&&) noexcept;
   ~TrivialABI();
 };
 
@@ -82,9 +82,7 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam, TrivialABI trivialParam)
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr {{[^,]*}} %[[McCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[McParam]]) #
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
-  // CHECK-NEXT: call void @llvm.memcpy
-  // CHECK-SAME: %[[TrivialCopy]]
-  // CHECK-SAME: %[[TrivialAlloca]]
+  // CHECK-NEXT: call void @_ZN10TrivialABIC1EOS_(ptr {{[^,]*}} %[[TrivialCopy]], ptr {{[^,]*}} %[[TrivialAlloca]])
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeC1Ev(
 

>From 26e26100711b92a946180450d31f265647cfaba4 Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans at chromium.org>
Date: Thu, 20 Feb 2025 16:31:09 +0100
Subject: [PATCH 3/7] introduce and use an @llvm.coro.outside.frame intrinsic
 instead

---
 clang/lib/CodeGen/CGCoroutine.cpp             |  9 +--
 clang/test/CodeGenCoroutines/coro-gro.cpp     |  4 +-
 clang/test/CodeGenCoroutines/coro-params.cpp  |  2 +-
 llvm/docs/Coroutines.rst                      | 25 ++++++++
 llvm/include/llvm/IR/Intrinsics.td            |  1 +
 .../llvm/Transforms/Coroutines/CoroInstr.h    | 16 +++++
 .../llvm/Transforms/Coroutines/CoroShape.h    |  2 +
 llvm/lib/Transforms/Coroutines/CoroSplit.cpp  |  6 ++
 llvm/lib/Transforms/Coroutines/Coroutines.cpp |  3 +
 llvm/lib/Transforms/Coroutines/SpillUtils.cpp |  6 ++
 .../Coroutines/coro-alloca-outside-frame.ll   | 63 ++++++++++++++++---
 11 files changed, 120 insertions(+), 17 deletions(-)

diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index cdc61676524b4..e2d06f33dd2f7 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -709,8 +709,7 @@ struct GetReturnObjectManager {
     auto *GroAlloca = dyn_cast_or_null<llvm::AllocaInst>(
         GroEmission.getOriginalAllocatedAddress().getPointer());
     assert(GroAlloca && "expected alloca to be emitted");
-    GroAlloca->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
-                           llvm::MDNode::get(CGF.CGM.getLLVMContext(), {}));
+    Builder.CreateCall(CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_outside_frame), {GroAlloca});
 
     // Remember the top of EHStack before emitting the cleanup.
     auto old_top = CGF.EHStack.stable_begin();
@@ -859,10 +858,8 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
       // If the original param is in an alloca, exclude it from the coroutine
       // frame. The parameter copy will be part of the frame.
       Address ParmAddr = GetAddrOfLocalVar(Parm);
-      if (auto *ParmAlloca =
-              dyn_cast<llvm::AllocaInst>(ParmAddr.getBasePointer())) {
-        ParmAlloca->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
-                                llvm::MDNode::get(CGM.getLLVMContext(), {}));
+      if (auto *ParmAlloca = dyn_cast<llvm::AllocaInst>(ParmAddr.getBasePointer())) {
+        Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::coro_outside_frame), {ParmAlloca});
       }
     }
     for (auto *PM : S.getParamMoves()) {
diff --git a/clang/test/CodeGenCoroutines/coro-gro.cpp b/clang/test/CodeGenCoroutines/coro-gro.cpp
index b62134317cef2..bfcfc641dcd72 100644
--- a/clang/test/CodeGenCoroutines/coro-gro.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro.cpp
@@ -30,11 +30,12 @@ void doSomething() noexcept;
 int f() {
   // CHECK: %[[RetVal:.+]] = alloca i32
   // CHECK: %[[GroActive:.+]] = alloca i1
-  // CHECK: %[[CoroGro:.+]] = alloca %struct.GroType, {{.*}} !coro.outside.frame ![[OutFrameMetadata:.+]]
+  // CHECK: %[[CoroGro:.+]] = alloca %struct.GroType
 
   // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
   // CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef %[[Size]])
   // CHECK: store i1 false, ptr %[[GroActive]]
+  // CHECK: call void @llvm.coro.outside.frame(ptr %[[CoroGro]])
   // CHECK: call void @_ZNSt16coroutine_traitsIiJEE12promise_typeC1Ev(
   // CHECK: call void @_ZNSt16coroutine_traitsIiJEE12promise_type17get_return_objectEv({{.*}} %[[CoroGro]]
   // CHECK: store i1 true, ptr %[[GroActive]]
@@ -106,4 +107,3 @@ invoker g() {
   // CHECK: call void @_ZN7invoker15invoker_promise17get_return_objectEv({{.*}} %[[AggRes]]
   co_return;
 }
-// CHECK: ![[OutFrameMetadata]] = !{}
\ No newline at end of file
diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp b/clang/test/CodeGenCoroutines/coro-params.cpp
index 86b0f54b4d486..6277b25470552 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -71,13 +71,13 @@ void consume(int,int,int,int) noexcept;
 // CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy10TrivialABI(i32 noundef %val, ptr noundef %[[MoParam:.+]], ptr noundef %[[McParam:.+]], i32 %[[TrivialParam:.+]]) #0 personality ptr @__gxx_personality_v0
 void f(int val, MoveOnly moParam, MoveAndCopy mcParam, TrivialABI trivialParam) {
   // CHECK: %[[TrivialAlloca:.+]] = alloca %struct.TrivialABI,
-  // CHECK-SAME: !coro.outside.frame
   // CHECK: %[[MoCopy:.+]] = alloca %struct.MoveOnly,
   // CHECK: %[[McCopy:.+]] = alloca %struct.MoveAndCopy,
   // CHECK: %[[TrivialCopy:.+]] = alloca %struct.TrivialABI,
   // CHECK: store i32 %val, ptr %[[ValAddr:.+]]
 
   // CHECK: call ptr @llvm.coro.begin(
+  // CHECK: @llvm.coro.outside.frame(ptr %[[TrivialAlloca]])
   // CHECK: call void @_ZN8MoveOnlyC1EOS_(ptr {{[^,]*}} %[[MoCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[MoParam]])
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr {{[^,]*}} %[[McCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[McParam]]) #
diff --git a/llvm/docs/Coroutines.rst b/llvm/docs/Coroutines.rst
index 60e32dc467d27..08819733134c3 100644
--- a/llvm/docs/Coroutines.rst
+++ b/llvm/docs/Coroutines.rst
@@ -1641,6 +1641,30 @@ the function call.
                            ptr %ctxt, ptr %task, ptr %actor)
   unreachable
 
+
+.. _coro.outside.frame:
+
+'llvm.coro.outside.frame' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+    declare void @llvm.coro.outside.frame(ptr %p)
+
+Overview:
+"""""""""
+
+TODO
+
+Arguments:
+""""""""""
+
+TODO
+
+Semantics:
+""""""""""
+
+TODO
+
 .. _coro.suspend:
 .. _suspend points:
 
@@ -2185,6 +2209,7 @@ Metadata
 '``coro.outside.frame``' Metadata
 ---------------------------------
 
+TODO: Deprecate/remove
 ``coro.outside.frame`` metadata may be attached to an alloca instruction to
 to signify that it shouldn't be promoted to the coroutine frame, useful for
 filtering allocas out by the frontend when emitting internal control mechanisms.
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 14ecae41ff08f..24f36edf79eae 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1778,6 +1778,7 @@ def int_coro_alloca_alloc : Intrinsic<[llvm_token_ty],
                                       [llvm_anyint_ty, llvm_i32_ty], []>;
 def int_coro_alloca_get : Intrinsic<[llvm_ptr_ty], [llvm_token_ty], []>;
 def int_coro_alloca_free : Intrinsic<[], [llvm_token_ty], []>;
+def int_coro_outside_frame : Intrinsic<[], [llvm_ptr_ty], []>;
 
 // Coroutine Manipulation Intrinsics.
 
diff --git a/llvm/include/llvm/Transforms/Coroutines/CoroInstr.h b/llvm/include/llvm/Transforms/Coroutines/CoroInstr.h
index fbc76219ead86..9bfb8f5dcaf74 100644
--- a/llvm/include/llvm/Transforms/Coroutines/CoroInstr.h
+++ b/llvm/include/llvm/Transforms/Coroutines/CoroInstr.h
@@ -796,6 +796,22 @@ class CoroAllocaFreeInst : public IntrinsicInst {
   }
 };
 
+/// This represents the llvm.coro.outside.frame instruction.
+class CoroOutsideFrameInst : public IntrinsicInst {
+  enum { PtrArg };
+
+public:
+  Value *getPtr() const { return getArgOperand(PtrArg); } // XXX: Could we require it to be an alloca?
+
+  // Methods to support type inquiry through isa, cast, and dyn_cast:
+  static bool classof(const IntrinsicInst *I) {
+    return I->getIntrinsicID() == Intrinsic::coro_outside_frame;
+  }
+  static bool classof(const Value *V) {
+    return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
+  }
+};
+
 } // End namespace llvm.
 
 #endif // LLVM_TRANSFORMS_COROUTINES_COROINSTR_H
diff --git a/llvm/include/llvm/Transforms/Coroutines/CoroShape.h b/llvm/include/llvm/Transforms/Coroutines/CoroShape.h
index ea93ced1ce29e..657c2c8bfdc82 100644
--- a/llvm/include/llvm/Transforms/Coroutines/CoroShape.h
+++ b/llvm/include/llvm/Transforms/Coroutines/CoroShape.h
@@ -57,6 +57,7 @@ struct Shape {
   SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
   SmallVector<CoroAwaitSuspendInst *, 4> CoroAwaitSuspends;
   SmallVector<CallInst *, 2> SymmetricTransfers;
+  SmallVector<CoroOutsideFrameInst *, 8> OutsideFrames;
 
   // Values invalidated by replaceSwiftErrorOps()
   SmallVector<CallInst *, 2> SwiftErrorOps;
@@ -69,6 +70,7 @@ struct Shape {
     CoroSuspends.clear();
     CoroAwaitSuspends.clear();
     SymmetricTransfers.clear();
+    OutsideFrames.clear();
 
     SwiftErrorOps.clear();
 
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index e1f767edd6ee1..32b092d61a365 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -2015,7 +2015,13 @@ static void doSplitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
   simplifySuspendPoints(Shape);
 
   normalizeCoroutine(F, Shape, TTI);
+
   ABI.buildCoroutineFrame(OptimizeFrame);
+
+  // @llvm.coro.outside.frame no longer needed after the frame has been built.
+  for (Instruction *I : Shape.OutsideFrames)
+    I->eraseFromParent();
+
   replaceFrameSizeAndAlignment(Shape);
 
   bool isNoSuspendCoroutine = Shape.CoroSuspends.empty();
diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp
index 7b59c39283ded..392f74cd2f52f 100644
--- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp
+++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp
@@ -286,6 +286,9 @@ void coro::Shape::analyze(Function &F,
           }
         }
         break;
+      case Intrinsic::coro_outside_frame:
+        OutsideFrames.push_back(cast<CoroOutsideFrameInst>(II));
+        break;
       }
     }
   }
diff --git a/llvm/lib/Transforms/Coroutines/SpillUtils.cpp b/llvm/lib/Transforms/Coroutines/SpillUtils.cpp
index 5062ee97a665d..8075adf82602b 100644
--- a/llvm/lib/Transforms/Coroutines/SpillUtils.cpp
+++ b/llvm/lib/Transforms/Coroutines/SpillUtils.cpp
@@ -426,11 +426,17 @@ static void collectFrameAlloca(AllocaInst *AI, const coro::Shape &Shape,
   if (AI == Shape.SwitchLowering.PromiseAlloca)
     return;
 
+  // TODO: Deprecate/remove.
   // The __coro_gro alloca should outlive the promise, make sure we
   // keep it outside the frame.
   if (AI->hasMetadata(LLVMContext::MD_coro_outside_frame))
     return;
 
+  for (const CoroOutsideFrameInst *I : Shape.OutsideFrames) {
+    if (I->getPtr() == AI)
+      return;
+  }
+
   // The code that uses lifetime.start intrinsic does not work for functions
   // with loops without exit. Disable it on ABIs we know to generate such
   // code.
diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll b/llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll
index ac6a5752438ce..04b73beb71f37 100644
--- a/llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll
+++ b/llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll
@@ -2,6 +2,23 @@
 ; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S -o %t.ll
 ; RUN: FileCheck --input-file=%t.ll %s
 
+; %y and %alias_phi would all go to the frame, but not %x
+; CHECK:       %f.Frame = type { ptr, ptr, i64, ptr, i1 }
+; CHECK:       %g.Frame = type { ptr, ptr, i64, ptr, i1 }
+
+; CHECK-LABEL: @f(
+; CHECK:         %x = alloca i64, align 8, !coro.outside.frame !0
+; CHECK-NOT:     %x.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
+; CHECK:         %y.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
+; CHECK:         %alias_phi = phi ptr [ %y.reload.addr, %merge.from.flag_false ], [ %x, %entry ]
+
+; CHECK-LABEL: @g(
+; CHECK:         %x = alloca i64, align 8
+; CHECK-NOT:     %x.reload.addr = getelementptr inbounds %g.Frame, ptr %hdl, i32 0, i32 2
+; CHECK:         %y.reload.addr = getelementptr inbounds %g.Frame, ptr %hdl, i32 0, i32 2
+; CHECK:         %alias_phi = phi ptr [ %y.reload.addr, %merge.from.flag_false ], [ %x, %entry ]
+
+
 define ptr @f(i1 %n) presplitcoroutine {
 entry:
   %x = alloca i64, !coro.outside.frame !{}
@@ -37,13 +54,43 @@ suspend:
   ret ptr %hdl
 }
 
-; %y and %alias_phi would all go to the frame, but not %x
-; CHECK:       %f.Frame = type { ptr, ptr, i64, ptr, i1 }
-; CHECK-LABEL: @f(
-; CHECK:         %x = alloca i64, align 8, !coro.outside.frame !0
-; CHECK-NOT:     %x.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
-; CHECK:         %y.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
-; CHECK:         %alias_phi = phi ptr [ %y.reload.addr, %merge.from.flag_false ], [ %x, %entry ]
+
+define ptr @g(i1 %n) presplitcoroutine {
+entry:
+  %x = alloca i64
+  %y = alloca i64
+  call void @llvm.coro.outside.frame(ptr %x)
+  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
+  %size = call i32 @llvm.coro.size.i32()
+  %alloc = call ptr @malloc(i32 %size)
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
+  br i1 %n, label %flag_true, label %flag_false
+
+flag_true:
+  br label %merge
+
+flag_false:
+  br label %merge
+
+merge:
+  %alias_phi = phi ptr [ %x, %flag_true ], [ %y, %flag_false ]
+  %sp1 = call i8 @llvm.coro.suspend(token none, i1 false)
+  switch i8 %sp1, label %suspend [i8 0, label %resume
+                                  i8 1, label %cleanup]
+resume:
+  call void @print(ptr %alias_phi)
+  br label %cleanup
+
+cleanup:
+  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
+  call void @free(ptr %mem)
+  br label %suspend
+
+suspend:
+  call i1 @llvm.coro.end(ptr %hdl, i1 0, token none)
+  ret ptr %hdl
+}
+
 
 declare ptr @llvm.coro.free(token, ptr)
 declare i32 @llvm.coro.size.i32()
@@ -58,4 +105,4 @@ declare i1 @llvm.coro.end(ptr, i1, token)
 
 declare void @print(ptr)
 declare noalias ptr @malloc(i32)
-declare void @free(ptr)
\ No newline at end of file
+declare void @free(ptr)

>From b88b064e4c7a019c292c3e3e8198535243948330 Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans at chromium.org>
Date: Fri, 21 Feb 2025 11:21:47 +0100
Subject: [PATCH 4/7] add x86_64-pc-win32 test case

---
 clang/test/CodeGenCoroutines/coro-params.cpp | 35 ++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp b/clang/test/CodeGenCoroutines/coro-params.cpp
index 6277b25470552..792500ce5ac23 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -3,6 +3,7 @@
 // Vefifies that parameter copies are used in the body of the coroutine
 // Verifies that parameter copies are used to construct the promise type, if that type has a matching constructor
 // RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-win32          -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s --check-prefix=MSABI
 
 namespace std {
 template <typename... T> struct coroutine_traits;
@@ -209,3 +210,37 @@ method some_class::good_coroutine_calls_custom_constructor(float) {
   // CHECK: invoke void @_ZNSt16coroutine_traitsIJ6methodR10some_classfEE12promise_typeC1ES2_f(ptr {{[^,]*}} %__promise, ptr noundef nonnull align 1 dereferenceable(1) %{{.+}}, float
   co_return;
 }
+
+
+struct MSParm {
+  int val;
+  ~MSParm();
+};
+
+void consume(int) noexcept;
+
+// Similarly to the [[clang::trivial_abi]] parameters, with the MSVC ABI
+// parameters are also destroyed by the callee, and on x86-64 such parameters
+// may get passed in registers. In that case it's again important that the
+// parameter's local alloca does not become part of the coro frame since that
+// may be destroyed before the destructor call.
+void msabi(MSParm p) {
+  // MSABI: define{{.*}} void @"?msabi@@YAXUMSParm@@@Z"(i32 %[[Param:.+]])
+  // MSABI: %[[ParamAlloca:.+]] = alloca %struct.MSParm
+  // MSABI: %[[ParamCopy:.+]] = alloca %struct.MSParm
+
+  // The parameter's local alloca is marked not part of the frame.
+  // MSABI: call void @llvm.coro.outside.frame(ptr %[[ParamAlloca]])
+
+  consume(p.val);
+  // The parameter's copy is used by the coroutine.
+  // MSABI: %[[ValPtr:.+]] = getelementptr inbounds nuw %struct.MSParm, ptr %[[ParamCopy]], i32 0, i32 0
+  // MSABI: %[[Val:.+]] = load i32, ptr %[[ValPtr]]
+  // MSABI: call void @"?consume@@YAXH at Z"(i32{{.*}} %[[Val]])
+
+  co_return;
+
+  // The local alloca is used for the destructor call at the end of the ramp.
+  // MSABI: call i1 @llvm.coro.end
+  // MSABI: call void @"??1MSParm@@QEAA at XZ"(ptr{{.*}} %[[ParamAlloca]])
+}

>From 225c37c1457c8ba4fc3c3d7e3c18fd7722285155 Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans at chromium.org>
Date: Mon, 24 Feb 2025 14:18:04 +0100
Subject: [PATCH 5/7] handle coro.outside.frame in mem2reg

---
 .../Transforms/Utils/PromoteMemoryToRegister.cpp    |  3 ++-
 .../Transforms/Mem2Reg/ignore-coro-outside-frame.ll | 13 +++++++++++++
 2 files changed, 15 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/Mem2Reg/ignore-coro-outside-frame.ll

diff --git a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
index 05fd989271c32..d6a38a8670c07 100644
--- a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
+++ b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
@@ -81,7 +81,8 @@ bool llvm::isAllocaPromotable(const AllocaInst *AI) {
         return false;
     } else if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(U)) {
       if (!II->isLifetimeStartOrEnd() && !II->isDroppable() &&
-          II->getIntrinsicID() != Intrinsic::fake_use)
+          II->getIntrinsicID() != Intrinsic::fake_use &&
+          II->getIntrinsicID() != Intrinsic::coro_outside_frame)
         return false;
     } else if (const BitCastInst *BCI = dyn_cast<BitCastInst>(U)) {
       if (!onlyUsedByLifetimeMarkersOrDroppableInsts(BCI))
diff --git a/llvm/test/Transforms/Mem2Reg/ignore-coro-outside-frame.ll b/llvm/test/Transforms/Mem2Reg/ignore-coro-outside-frame.ll
new file mode 100644
index 0000000000000..ec9309503109d
--- /dev/null
+++ b/llvm/test/Transforms/Mem2Reg/ignore-coro-outside-frame.ll
@@ -0,0 +1,13 @@
+; RUN: opt -passes=mem2reg -S -o - < %s | FileCheck %s
+
+declare void @llvm.coro.outside.frame(ptr)
+
+define void @test() {
+; CHECK: test
+; CHECK-NOT: alloca
+; CHECK-NOT: call void @llvm.coro.outside.frame
+  %A = alloca i32
+  call void @llvm.coro.outside.frame(ptr %A)
+  store i32 1, ptr %A
+  ret void
+}

>From acb41463eec1443f35b2fe856954741e083a7dd9 Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans at chromium.org>
Date: Mon, 24 Feb 2025 14:40:49 +0100
Subject: [PATCH 6/7] add nocapture and memory(none) to the intrinsic

---
 llvm/include/llvm/IR/Intrinsics.td | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 24f36edf79eae..fd15a5984d000 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1778,7 +1778,8 @@ def int_coro_alloca_alloc : Intrinsic<[llvm_token_ty],
                                       [llvm_anyint_ty, llvm_i32_ty], []>;
 def int_coro_alloca_get : Intrinsic<[llvm_ptr_ty], [llvm_token_ty], []>;
 def int_coro_alloca_free : Intrinsic<[], [llvm_token_ty], []>;
-def int_coro_outside_frame : Intrinsic<[], [llvm_ptr_ty], []>;
+def int_coro_outside_frame : Intrinsic<[], [llvm_ptr_ty],
+                                       [IntrNoMem, NoCapture<ArgIndex<0>>]>;
 
 // Coroutine Manipulation Intrinsics.
 

>From 52a918e7cbf509facedcfe12643f1c359a717269 Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans at chromium.org>
Date: Thu, 27 Feb 2025 13:55:56 +0100
Subject: [PATCH 7/7] revert back to using the metadata

---
 clang/lib/CodeGen/CGCoroutine.cpp             |  9 ++-
 clang/test/CodeGenCoroutines/coro-gro.cpp     |  4 +-
 clang/test/CodeGenCoroutines/coro-params.cpp  |  9 +--
 llvm/docs/Coroutines.rst                      | 25 --------
 llvm/include/llvm/IR/Intrinsics.td            |  2 -
 .../llvm/Transforms/Coroutines/CoroInstr.h    | 16 -----
 .../llvm/Transforms/Coroutines/CoroShape.h    |  2 -
 llvm/lib/Transforms/Coroutines/CoroSplit.cpp  |  6 --
 llvm/lib/Transforms/Coroutines/Coroutines.cpp |  3 -
 llvm/lib/Transforms/Coroutines/SpillUtils.cpp |  6 --
 .../Utils/PromoteMemoryToRegister.cpp         |  3 +-
 .../Coroutines/coro-alloca-outside-frame.ll   | 63 +++----------------
 .../Mem2Reg/ignore-coro-outside-frame.ll      | 13 ----
 13 files changed, 22 insertions(+), 139 deletions(-)
 delete mode 100644 llvm/test/Transforms/Mem2Reg/ignore-coro-outside-frame.ll

diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index e2d06f33dd2f7..cdc61676524b4 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -709,7 +709,8 @@ struct GetReturnObjectManager {
     auto *GroAlloca = dyn_cast_or_null<llvm::AllocaInst>(
         GroEmission.getOriginalAllocatedAddress().getPointer());
     assert(GroAlloca && "expected alloca to be emitted");
-    Builder.CreateCall(CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_outside_frame), {GroAlloca});
+    GroAlloca->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
+                           llvm::MDNode::get(CGF.CGM.getLLVMContext(), {}));
 
     // Remember the top of EHStack before emitting the cleanup.
     auto old_top = CGF.EHStack.stable_begin();
@@ -858,8 +859,10 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
       // If the original param is in an alloca, exclude it from the coroutine
       // frame. The parameter copy will be part of the frame.
       Address ParmAddr = GetAddrOfLocalVar(Parm);
-      if (auto *ParmAlloca = dyn_cast<llvm::AllocaInst>(ParmAddr.getBasePointer())) {
-        Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::coro_outside_frame), {ParmAlloca});
+      if (auto *ParmAlloca =
+              dyn_cast<llvm::AllocaInst>(ParmAddr.getBasePointer())) {
+        ParmAlloca->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
+                                llvm::MDNode::get(CGM.getLLVMContext(), {}));
       }
     }
     for (auto *PM : S.getParamMoves()) {
diff --git a/clang/test/CodeGenCoroutines/coro-gro.cpp b/clang/test/CodeGenCoroutines/coro-gro.cpp
index bfcfc641dcd72..b62134317cef2 100644
--- a/clang/test/CodeGenCoroutines/coro-gro.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro.cpp
@@ -30,12 +30,11 @@ void doSomething() noexcept;
 int f() {
   // CHECK: %[[RetVal:.+]] = alloca i32
   // CHECK: %[[GroActive:.+]] = alloca i1
-  // CHECK: %[[CoroGro:.+]] = alloca %struct.GroType
+  // CHECK: %[[CoroGro:.+]] = alloca %struct.GroType, {{.*}} !coro.outside.frame ![[OutFrameMetadata:.+]]
 
   // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
   // CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef %[[Size]])
   // CHECK: store i1 false, ptr %[[GroActive]]
-  // CHECK: call void @llvm.coro.outside.frame(ptr %[[CoroGro]])
   // CHECK: call void @_ZNSt16coroutine_traitsIiJEE12promise_typeC1Ev(
   // CHECK: call void @_ZNSt16coroutine_traitsIiJEE12promise_type17get_return_objectEv({{.*}} %[[CoroGro]]
   // CHECK: store i1 true, ptr %[[GroActive]]
@@ -107,3 +106,4 @@ invoker g() {
   // CHECK: call void @_ZN7invoker15invoker_promise17get_return_objectEv({{.*}} %[[AggRes]]
   co_return;
 }
+// CHECK: ![[OutFrameMetadata]] = !{}
\ No newline at end of file
diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp b/clang/test/CodeGenCoroutines/coro-params.cpp
index 792500ce5ac23..719726cca29c5 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -72,13 +72,13 @@ void consume(int,int,int,int) noexcept;
 // CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy10TrivialABI(i32 noundef %val, ptr noundef %[[MoParam:.+]], ptr noundef %[[McParam:.+]], i32 %[[TrivialParam:.+]]) #0 personality ptr @__gxx_personality_v0
 void f(int val, MoveOnly moParam, MoveAndCopy mcParam, TrivialABI trivialParam) {
   // CHECK: %[[TrivialAlloca:.+]] = alloca %struct.TrivialABI,
+  // CHECK-SAME: !coro.outside.frame
   // CHECK: %[[MoCopy:.+]] = alloca %struct.MoveOnly,
   // CHECK: %[[McCopy:.+]] = alloca %struct.MoveAndCopy,
   // CHECK: %[[TrivialCopy:.+]] = alloca %struct.TrivialABI,
   // CHECK: store i32 %val, ptr %[[ValAddr:.+]]
 
   // CHECK: call ptr @llvm.coro.begin(
-  // CHECK: @llvm.coro.outside.frame(ptr %[[TrivialAlloca]])
   // CHECK: call void @_ZN8MoveOnlyC1EOS_(ptr {{[^,]*}} %[[MoCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[MoParam]])
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr {{[^,]*}} %[[McCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[McParam]]) #
@@ -226,11 +226,12 @@ void consume(int) noexcept;
 // may be destroyed before the destructor call.
 void msabi(MSParm p) {
   // MSABI: define{{.*}} void @"?msabi@@YAXUMSParm@@@Z"(i32 %[[Param:.+]])
-  // MSABI: %[[ParamAlloca:.+]] = alloca %struct.MSParm
-  // MSABI: %[[ParamCopy:.+]] = alloca %struct.MSParm
 
   // The parameter's local alloca is marked not part of the frame.
-  // MSABI: call void @llvm.coro.outside.frame(ptr %[[ParamAlloca]])
+  // MSABI: %[[ParamAlloca:.+]] = alloca %struct.MSParm
+  // MSABI-SAME: !coro.outside.frame
+
+  // MSABI: %[[ParamCopy:.+]] = alloca %struct.MSParm
 
   consume(p.val);
   // The parameter's copy is used by the coroutine.
diff --git a/llvm/docs/Coroutines.rst b/llvm/docs/Coroutines.rst
index 08819733134c3..60e32dc467d27 100644
--- a/llvm/docs/Coroutines.rst
+++ b/llvm/docs/Coroutines.rst
@@ -1641,30 +1641,6 @@ the function call.
                            ptr %ctxt, ptr %task, ptr %actor)
   unreachable
 
-
-.. _coro.outside.frame:
-
-'llvm.coro.outside.frame' Intrinsic
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-::
-
-    declare void @llvm.coro.outside.frame(ptr %p)
-
-Overview:
-"""""""""
-
-TODO
-
-Arguments:
-""""""""""
-
-TODO
-
-Semantics:
-""""""""""
-
-TODO
-
 .. _coro.suspend:
 .. _suspend points:
 
@@ -2209,7 +2185,6 @@ Metadata
 '``coro.outside.frame``' Metadata
 ---------------------------------
 
-TODO: Deprecate/remove
 ``coro.outside.frame`` metadata may be attached to an alloca instruction to
 to signify that it shouldn't be promoted to the coroutine frame, useful for
 filtering allocas out by the frontend when emitting internal control mechanisms.
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index fd15a5984d000..14ecae41ff08f 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1778,8 +1778,6 @@ def int_coro_alloca_alloc : Intrinsic<[llvm_token_ty],
                                       [llvm_anyint_ty, llvm_i32_ty], []>;
 def int_coro_alloca_get : Intrinsic<[llvm_ptr_ty], [llvm_token_ty], []>;
 def int_coro_alloca_free : Intrinsic<[], [llvm_token_ty], []>;
-def int_coro_outside_frame : Intrinsic<[], [llvm_ptr_ty],
-                                       [IntrNoMem, NoCapture<ArgIndex<0>>]>;
 
 // Coroutine Manipulation Intrinsics.
 
diff --git a/llvm/include/llvm/Transforms/Coroutines/CoroInstr.h b/llvm/include/llvm/Transforms/Coroutines/CoroInstr.h
index 9bfb8f5dcaf74..fbc76219ead86 100644
--- a/llvm/include/llvm/Transforms/Coroutines/CoroInstr.h
+++ b/llvm/include/llvm/Transforms/Coroutines/CoroInstr.h
@@ -796,22 +796,6 @@ class CoroAllocaFreeInst : public IntrinsicInst {
   }
 };
 
-/// This represents the llvm.coro.outside.frame instruction.
-class CoroOutsideFrameInst : public IntrinsicInst {
-  enum { PtrArg };
-
-public:
-  Value *getPtr() const { return getArgOperand(PtrArg); } // XXX: Could we require it to be an alloca?
-
-  // Methods to support type inquiry through isa, cast, and dyn_cast:
-  static bool classof(const IntrinsicInst *I) {
-    return I->getIntrinsicID() == Intrinsic::coro_outside_frame;
-  }
-  static bool classof(const Value *V) {
-    return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
-  }
-};
-
 } // End namespace llvm.
 
 #endif // LLVM_TRANSFORMS_COROUTINES_COROINSTR_H
diff --git a/llvm/include/llvm/Transforms/Coroutines/CoroShape.h b/llvm/include/llvm/Transforms/Coroutines/CoroShape.h
index 657c2c8bfdc82..ea93ced1ce29e 100644
--- a/llvm/include/llvm/Transforms/Coroutines/CoroShape.h
+++ b/llvm/include/llvm/Transforms/Coroutines/CoroShape.h
@@ -57,7 +57,6 @@ struct Shape {
   SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
   SmallVector<CoroAwaitSuspendInst *, 4> CoroAwaitSuspends;
   SmallVector<CallInst *, 2> SymmetricTransfers;
-  SmallVector<CoroOutsideFrameInst *, 8> OutsideFrames;
 
   // Values invalidated by replaceSwiftErrorOps()
   SmallVector<CallInst *, 2> SwiftErrorOps;
@@ -70,7 +69,6 @@ struct Shape {
     CoroSuspends.clear();
     CoroAwaitSuspends.clear();
     SymmetricTransfers.clear();
-    OutsideFrames.clear();
 
     SwiftErrorOps.clear();
 
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index 32b092d61a365..e1f767edd6ee1 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -2015,13 +2015,7 @@ static void doSplitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
   simplifySuspendPoints(Shape);
 
   normalizeCoroutine(F, Shape, TTI);
-
   ABI.buildCoroutineFrame(OptimizeFrame);
-
-  // @llvm.coro.outside.frame no longer needed after the frame has been built.
-  for (Instruction *I : Shape.OutsideFrames)
-    I->eraseFromParent();
-
   replaceFrameSizeAndAlignment(Shape);
 
   bool isNoSuspendCoroutine = Shape.CoroSuspends.empty();
diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp
index 392f74cd2f52f..7b59c39283ded 100644
--- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp
+++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp
@@ -286,9 +286,6 @@ void coro::Shape::analyze(Function &F,
           }
         }
         break;
-      case Intrinsic::coro_outside_frame:
-        OutsideFrames.push_back(cast<CoroOutsideFrameInst>(II));
-        break;
       }
     }
   }
diff --git a/llvm/lib/Transforms/Coroutines/SpillUtils.cpp b/llvm/lib/Transforms/Coroutines/SpillUtils.cpp
index 8075adf82602b..5062ee97a665d 100644
--- a/llvm/lib/Transforms/Coroutines/SpillUtils.cpp
+++ b/llvm/lib/Transforms/Coroutines/SpillUtils.cpp
@@ -426,17 +426,11 @@ static void collectFrameAlloca(AllocaInst *AI, const coro::Shape &Shape,
   if (AI == Shape.SwitchLowering.PromiseAlloca)
     return;
 
-  // TODO: Deprecate/remove.
   // The __coro_gro alloca should outlive the promise, make sure we
   // keep it outside the frame.
   if (AI->hasMetadata(LLVMContext::MD_coro_outside_frame))
     return;
 
-  for (const CoroOutsideFrameInst *I : Shape.OutsideFrames) {
-    if (I->getPtr() == AI)
-      return;
-  }
-
   // The code that uses lifetime.start intrinsic does not work for functions
   // with loops without exit. Disable it on ABIs we know to generate such
   // code.
diff --git a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
index d6a38a8670c07..05fd989271c32 100644
--- a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
+++ b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
@@ -81,8 +81,7 @@ bool llvm::isAllocaPromotable(const AllocaInst *AI) {
         return false;
     } else if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(U)) {
       if (!II->isLifetimeStartOrEnd() && !II->isDroppable() &&
-          II->getIntrinsicID() != Intrinsic::fake_use &&
-          II->getIntrinsicID() != Intrinsic::coro_outside_frame)
+          II->getIntrinsicID() != Intrinsic::fake_use)
         return false;
     } else if (const BitCastInst *BCI = dyn_cast<BitCastInst>(U)) {
       if (!onlyUsedByLifetimeMarkersOrDroppableInsts(BCI))
diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll b/llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll
index 04b73beb71f37..ac6a5752438ce 100644
--- a/llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll
+++ b/llvm/test/Transforms/Coroutines/coro-alloca-outside-frame.ll
@@ -2,23 +2,6 @@
 ; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S -o %t.ll
 ; RUN: FileCheck --input-file=%t.ll %s
 
-; %y and %alias_phi would all go to the frame, but not %x
-; CHECK:       %f.Frame = type { ptr, ptr, i64, ptr, i1 }
-; CHECK:       %g.Frame = type { ptr, ptr, i64, ptr, i1 }
-
-; CHECK-LABEL: @f(
-; CHECK:         %x = alloca i64, align 8, !coro.outside.frame !0
-; CHECK-NOT:     %x.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
-; CHECK:         %y.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
-; CHECK:         %alias_phi = phi ptr [ %y.reload.addr, %merge.from.flag_false ], [ %x, %entry ]
-
-; CHECK-LABEL: @g(
-; CHECK:         %x = alloca i64, align 8
-; CHECK-NOT:     %x.reload.addr = getelementptr inbounds %g.Frame, ptr %hdl, i32 0, i32 2
-; CHECK:         %y.reload.addr = getelementptr inbounds %g.Frame, ptr %hdl, i32 0, i32 2
-; CHECK:         %alias_phi = phi ptr [ %y.reload.addr, %merge.from.flag_false ], [ %x, %entry ]
-
-
 define ptr @f(i1 %n) presplitcoroutine {
 entry:
   %x = alloca i64, !coro.outside.frame !{}
@@ -54,43 +37,13 @@ suspend:
   ret ptr %hdl
 }
 
-
-define ptr @g(i1 %n) presplitcoroutine {
-entry:
-  %x = alloca i64
-  %y = alloca i64
-  call void @llvm.coro.outside.frame(ptr %x)
-  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
-  %size = call i32 @llvm.coro.size.i32()
-  %alloc = call ptr @malloc(i32 %size)
-  %hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
-  br i1 %n, label %flag_true, label %flag_false
-
-flag_true:
-  br label %merge
-
-flag_false:
-  br label %merge
-
-merge:
-  %alias_phi = phi ptr [ %x, %flag_true ], [ %y, %flag_false ]
-  %sp1 = call i8 @llvm.coro.suspend(token none, i1 false)
-  switch i8 %sp1, label %suspend [i8 0, label %resume
-                                  i8 1, label %cleanup]
-resume:
-  call void @print(ptr %alias_phi)
-  br label %cleanup
-
-cleanup:
-  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
-  call void @free(ptr %mem)
-  br label %suspend
-
-suspend:
-  call i1 @llvm.coro.end(ptr %hdl, i1 0, token none)
-  ret ptr %hdl
-}
-
+; %y and %alias_phi would all go to the frame, but not %x
+; CHECK:       %f.Frame = type { ptr, ptr, i64, ptr, i1 }
+; CHECK-LABEL: @f(
+; CHECK:         %x = alloca i64, align 8, !coro.outside.frame !0
+; CHECK-NOT:     %x.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
+; CHECK:         %y.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
+; CHECK:         %alias_phi = phi ptr [ %y.reload.addr, %merge.from.flag_false ], [ %x, %entry ]
 
 declare ptr @llvm.coro.free(token, ptr)
 declare i32 @llvm.coro.size.i32()
@@ -105,4 +58,4 @@ declare i1 @llvm.coro.end(ptr, i1, token)
 
 declare void @print(ptr)
 declare noalias ptr @malloc(i32)
-declare void @free(ptr)
+declare void @free(ptr)
\ No newline at end of file
diff --git a/llvm/test/Transforms/Mem2Reg/ignore-coro-outside-frame.ll b/llvm/test/Transforms/Mem2Reg/ignore-coro-outside-frame.ll
deleted file mode 100644
index ec9309503109d..0000000000000
--- a/llvm/test/Transforms/Mem2Reg/ignore-coro-outside-frame.ll
+++ /dev/null
@@ -1,13 +0,0 @@
-; RUN: opt -passes=mem2reg -S -o - < %s | FileCheck %s
-
-declare void @llvm.coro.outside.frame(ptr)
-
-define void @test() {
-; CHECK: test
-; CHECK-NOT: alloca
-; CHECK-NOT: call void @llvm.coro.outside.frame
-  %A = alloca i32
-  call void @llvm.coro.outside.frame(ptr %A)
-  store i32 1, ptr %A
-  ret void
-}



More information about the llvm-commits mailing list