https://github.com/NewSigma created 
https://github.com/llvm/llvm-project/pull/230933

A PoC fixing for exception throwing in `unhandled_exception`.

[dcl.fct.def.coroutine] specifies: a coroutine is suspended at a final suspend 
point if it is suspended

> **Type I**:  at a final await expression or
> **Type II**: due to an exception exiting from unhandled_exception().

**Type I** cleanup temporary variables after the evaluation of `co_await` 
expression, while **Type II** cleanup temporary variables upon stack unwinding. 
Current implementation ignores all scopes and naively jump to the Type-I final 
suspending point if an exception escapes. Thus, the cleanup logic corrupts and 
we may see double destruction (#61900, #109542) or deletion of a non-existent 
awaiter (#70478).

Note that suspending at the final suspend point does not imply evaluating the 
final await expression, and its only visible effect is that `handle.done()` 
evaluates to true. One can suspend at a normal suspending point and set the 
coroutine as done, which effectively simulates a *final suspend point*.

CodeGen(before):

``` LLVM
  call void @llvm.coro.end(ptr null, i1 true, token none)
  %InRamp = call i1 @llvm.coro.is_in_ramp()
  br i1 %InRamp, label %coro.cleanup, label %eh.resume

coro.cleanup:
  ; rest of the cleanup
  br label %eh.resume

eh.resume:
  resume { ptr, i32 } %lpad.val
```

CodeGen(after):

``` LLVM
  %InRamp = call i1 @llvm.coro.is_in_ramp()
  br i1 %InRamp, label %coro.cleanup, label %eh.suspend

eh.suspend:
  %sp = call i8 @llvm.coro.suspend(token none)
  switch i8 %sp, label %coro.unwind [
    i8 0, label %unreachable
    i8 1, label %coro.cleanup
  ]

coro.cleanup:
  ; rest of the cleanup
  call void @llvm.coro.end(ptr null, i1 false, token none)
  br label %eh.resume

coro.unwind:
  call void @llvm.coro.end(ptr null, i1 true, token none)
  br label %eh.resume

eh.resume:
  resume { ptr, i32 } %lpad.val
```

Close #61900 Close #70478 Close #109542

> Note: #70478 itself has UB even if this bug is resolved.

>From e8facf5489e5e467aa2c5182c7a7f533729025ce Mon Sep 17 00:00:00 2001
From: NewSigma <[email protected]>
Date: Sun, 11 Oct 2026 17:17:31 +0800
Subject: [PATCH 1/2] [LLVM][Coro] Make coro.end a pure marker for coroutine
 completion

---
 llvm/docs/Coroutines.md                       |  29 ++--
 llvm/lib/Transforms/Coroutines/CoroSplit.cpp  |  26 ----
 llvm/lib/Transforms/Coroutines/Coroutines.cpp |  12 --
 .../Coroutines/SuspendCrossingInfo.cpp        |   3 +-
 .../Transforms/Coroutines/coro-alloca-10.ll   |   2 +-
 .../Transforms/Coroutines/coro-split-eh-01.ll |   5 +-
 .../Coroutines/coro-split-final-suspend.ll    | 129 ------------------
 7 files changed, 20 insertions(+), 186 deletions(-)
 delete mode 100644 llvm/test/Transforms/Coroutines/coro-split-final-suspend.ll

diff --git a/llvm/docs/Coroutines.md b/llvm/docs/Coroutines.md
index fa18a71ad66dd..f75f65acaf7a7 100644
--- a/llvm/docs/Coroutines.md
+++ b/llvm/docs/Coroutines.md
@@ -1462,12 +1462,16 @@ For landingpad based exception model, it is expected 
that frontend uses the
 
 ```llvm
   ehcleanup:
-    call void @llvm.coro.end(ptr null, i1 true, token none)
     %InRamp = call i1 @llvm.coro.is_in_ramp()
-    br i1 %InRamp, label %cleanup.cont, label %eh.resume
+    br i1 %InRamp, label %cleanup.cont, label %coro.eh
 
   cleanup.cont:
     ; rest of the cleanup
+    br i1 %coro.eh
+
+  coro.eh:
+    call void @llvm.coro.end(ptr null, i1 true, token none)
+    br label %eh.resume
 
   eh.resume:
     %exn = load ptr, ptr %exn.slot, align 8
@@ -1482,20 +1486,16 @@ thus allowing to proceed to the rest of the cleanup 
code that is only needed dur
 initial invocation of the coroutine. Otherwise, it is replaced with `False`,
 thus leading to immediate unwind to the caller.
 
-For Windows Exception handling model, a frontend should attach a funclet bundle
-referring to an enclosing cleanuppad as follows:
+For Windows Exception handling model, it is expected that frontend uses the
+{ref}`coro.end` intrinsic as follows:
 
 ```llvm
   ehcleanup:
     %tok = cleanuppad within none []
-    call void @llvm.coro.end(ptr null, i1 true, token none) [ "funclet"(token 
%tok) ]
-    cleanupret from %tok unwind label %RestOfTheCleanup
+    call void @llvm.coro.end(ptr null, i1 true, token none)
+    cleanupret from %tok unwind to caller
 ```
 
-The `CoroSplit` pass, if the funclet bundle is present, will insert
-`cleanupret from %tok unwind to caller` before
-the {ref}`coro.end` intrinsic and will remove the rest of the block.
-
 In the unwind path (when the argument is `true`), `coro.end` will mark the 
coroutine
 as done, making it undefined behavior to resume the coroutine again and causing
 `llvm.coro.done` to return `true`.  This is not necessary in the normal path 
because
@@ -1503,11 +1503,10 @@ the coroutine will already be marked as done by the 
final suspend.
 
 The following table summarizes the handling of {ref}`coro.end` intrinsic.
 
-| unwind | EH model | In Start Function | In Resume/Destroy Functions |
-| --- | --- | --- | --- |
-| `false` | | nothing | `ret void` |
-| `true` | WinEH | mark coroutine as done | `cleanupret unwind to 
caller`<br>mark coroutine done |
-| `true` | Landingpad | mark coroutine as done | mark coroutine done |
+| unwind | In Start Function | In Resume/Destroy Functions |
+| --- | --- | --- |
+| `false` | nothing | `ret void` |
+| `true` | mark coroutine as done | mark coroutine done |
 
 (coro.end.results)=
 
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp 
b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index db8f835a5cedf..a4319b35a438d 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -377,24 +377,6 @@ static void markCoroutineAsDone(IRBuilder<> &Builder, 
const coro::Shape &Shape,
   // Resume function pointer is always first
   auto *NullPtr = ConstantPointerNull::get(Shape.getSwitchResumePointerType());
   Builder.CreateStore(NullPtr, FramePtr);
-
-  // If the coroutine don't have unwind coro end, we could omit the store to
-  // the final suspend point since we could infer the coroutine is suspended
-  // at the final suspend point by the nullness of ResumeFnAddr.
-  // However, we can't skip it if the coroutine have unwind coro end. Since
-  // the coroutine reaches unwind coro end is considered suspended at the
-  // final suspend point (the ResumeFnAddr is null) but in fact the coroutine
-  // didn't complete yet. We need the IndexVal for the final suspend point
-  // to make the states clear.
-  if (Shape.SwitchLowering.HasUnwindCoroEnd &&
-      Shape.SwitchLowering.HasFinalSuspend) {
-    assert(cast<CoroSuspendInst>(Shape.CoroSuspends.back())->isFinal() &&
-           "The final suspend should only live in the last position of "
-           "CoroSuspends.");
-    ConstantInt *IndexVal = Shape.getIndex(Shape.CoroSuspends.size() - 1);
-    Value *FinalIndex = createSwitchIndexPtr(Shape, Builder, FramePtr);
-    Builder.CreateStore(IndexVal, FinalIndex);
-  }
 }
 
 /// Replace an unwind call to llvm.coro.end.
@@ -425,14 +407,6 @@ static void replaceUnwindCoroEnd(AnyCoroEndInst *End, 
const coro::Shape &Shape,
     maybeFreeRetconStorage(Builder, Shape, FramePtr, CG);
     break;
   }
-
-  // If coro.end has an associated bundle, add cleanupret instruction.
-  if (auto Bundle = End->getOperandBundle(LLVMContext::OB_funclet)) {
-    auto *FromPad = cast<CleanupPadInst>(Bundle->Inputs[0]);
-    auto *CleanupRet = Builder.CreateCleanupRet(FromPad, nullptr);
-    End->getParent()->splitBasicBlock(End);
-    CleanupRet->getParent()->getTerminator()->eraseFromParent();
-  }
 }
 
 static void replaceCoroEnd(AnyCoroEndInst *End, const coro::Shape &Shape,
diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp 
b/llvm/lib/Transforms/Coroutines/Coroutines.cpp
index 2ecf66d18b5d3..9d986d06aea9f 100644
--- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp
+++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp
@@ -263,18 +263,6 @@ void coro::Shape::analyze(Function &F,
 
         if (CoroEnds.back()->isUnwind())
           HasUnwindCoroEnd = true;
-
-        if (CoroEnds.back()->isFallthrough() && isa<CoroEndInst>(II)) {
-          // Make sure that the fallthrough coro.end is the first element in 
the
-          // CoroEnds vector.
-          // Note: I don't think this is neccessary anymore.
-          if (CoroEnds.size() > 1) {
-            if (CoroEnds.front()->isFallthrough())
-              report_fatal_error(
-                  "Only one coro.end can be marked as fallthrough");
-            std::swap(CoroEnds.front(), CoroEnds.back());
-          }
-        }
         break;
       case Intrinsic::coro_is_in_ramp:
         CoroIsInRampInsts.push_back(cast<CoroIsInRampInst>(II));
diff --git a/llvm/lib/Transforms/Coroutines/SuspendCrossingInfo.cpp 
b/llvm/lib/Transforms/Coroutines/SuspendCrossingInfo.cpp
index f5ec3a17de033..e287aeb5617d2 100644
--- a/llvm/lib/Transforms/Coroutines/SuspendCrossingInfo.cpp
+++ b/llvm/lib/Transforms/Coroutines/SuspendCrossingInfo.cpp
@@ -167,7 +167,8 @@ SuspendCrossingInfo::SuspendCrossingInfo(Function &F, const 
coro::Shape &Shape)
     assert(CE->getParent()->getFirstInsertionPt() == CE->getIterator() &&
            CE->getParent()->size() <= 2 && "CoroEnd must be in its own BB");
 
-    getBlockData(CE->getParent()).setNeverKill();
+    if (CE->isFallthrough())
+      getBlockData(CE->getParent()).setNeverKill();
   }
 
   for (auto *InRamp : Shape.CoroIsInRampInsts)
diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-10.ll 
b/llvm/test/Transforms/Coroutines/coro-alloca-10.ll
index 756b9238552e0..d38509aec5642 100644
--- a/llvm/test/Transforms/Coroutines/coro-alloca-10.ll
+++ b/llvm/test/Transforms/Coroutines/coro-alloca-10.ll
@@ -50,7 +50,7 @@ catch.ret:
 
 ehcleanup:
   %cleanup = cleanuppad within none []
-  call void @llvm.coro.end(ptr null, i1 true, token none) [ "funclet"(token 
%cleanup) ]
+  call void @llvm.coro.end(ptr null, i1 true, token none)
   cleanupret from %cleanup unwind to caller
 
 coro.ret:
diff --git a/llvm/test/Transforms/Coroutines/coro-split-eh-01.ll 
b/llvm/test/Transforms/Coroutines/coro-split-eh-01.ll
index 5abea79320e11..b5407ac06a941 100644
--- a/llvm/test/Transforms/Coroutines/coro-split-eh-01.ll
+++ b/llvm/test/Transforms/Coroutines/coro-split-eh-01.ll
@@ -24,12 +24,12 @@ suspend:
 lpad:
   %tok = cleanuppad within none []
   call void @print(i32 2)
-  call void @llvm.coro.end(ptr null, i1 true, token none) [ "funclet"(token 
%tok) ]
+  call void @llvm.coro.end(ptr null, i1 true, token none)
   cleanupret from %tok unwind label %cleanup.cont
 
 cleanup.cont:
   %tok2 = cleanuppad within none []
-  call void @print(i32 3) ; should not be present in f.resume
+  call void @print(i32 3)
   cleanupret from %tok2 unwind to caller
 }
 
@@ -63,6 +63,7 @@ cleanup.cont:
 ; CHECK-NEXT:   call void @print(i32 2)
 ; Checks that the coroutine would be marked as done if it exits in unwinding 
path.
 ; CHECK-NEXT:   store ptr null, ptr %hdl, align 8
+; CHECK-NEXT:   call void @print(i32 3)
 ; CHECK-NEXT:   cleanupret from %tok unwind to caller
 
 declare ptr @llvm.coro.free(token, ptr)
diff --git a/llvm/test/Transforms/Coroutines/coro-split-final-suspend.ll 
b/llvm/test/Transforms/Coroutines/coro-split-final-suspend.ll
deleted file mode 100644
index becbc4418afad..0000000000000
--- a/llvm/test/Transforms/Coroutines/coro-split-final-suspend.ll
+++ /dev/null
@@ -1,129 +0,0 @@
-; Tests that we'll generate the store to the final suspend index if we see the 
unwind coro end.
-; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | 
FileCheck %s
-
-define ptr @unwind_coro_end() presplitcoroutine personality i32 3 {
-entry:
-  %id = call token @llvm.coro.id(i32 0, ptr null, ptr @unwind_coro_end, ptr 
null)
-  %hdl = call ptr @llvm.coro.begin(token %id, ptr null)
-  call void @print(i32 0)
-  br label %init
-
-init:
-  %initial_suspend = call i8 @llvm.coro.suspend(token none, i1 false)
-  switch i8 %initial_suspend, label %init_suspend [i8 0, label %init_resume
-                                                   i8 1, label %init_suspend]
-
-init_suspend:
-  ret ptr %hdl
-
-init_resume:
-  invoke void @print(i32 1)
-          to label %final_suspend unwind label %lpad
-
-final_suspend:
-  %0 = call i8 @llvm.coro.suspend(token none, i1 true)
-  switch i8 %0, label %suspend [i8 0, label %resume
-                                i8 1, label %suspend]
-resume:
-  invoke void @print(i32 1) to label %suspend unwind label %lpad
-
-suspend:
-  call void @llvm.coro.end(ptr %hdl, i1 false, token none)
-  call void @print(i32 0)
-  ret ptr %hdl
-
-lpad:
-  %lpval = landingpad { ptr, i32 }
-     cleanup
-
-  call void @print(i32 2)
-  call void @llvm.coro.end(ptr null, i1 true, token none)
-  %in.ramp = call i1 @llvm.coro.is_in_ramp()
-  br i1 %in.ramp, label %cleanup.cont, label %eh.resume
-
-cleanup.cont:
-  call void @print(i32 3)
-  br label %eh.resume
-
-eh.resume:
-  resume { ptr, i32 } %lpval
-}
-
-; Tests that we need to store the final index if we see unwind coro end.
-; CHECK: define{{.*}}@unwind_coro_end.resume
-; CHECK: invoke{{.*}}print
-; CHECK-NEXT: to label %[[RESUME:.*]] unwind label %[[LPAD:.*]]
-
-; CHECK: [[RESUME]]:
-; CHECK-NOT: {{.*:}}
-; CHECK: store ptr null, ptr %hdl
-; CHECK-NOT: {{.*:}}
-; CHECK: store i1 true, ptr %index.addr
-
-; CHECK: [[LPAD]]:
-; CHECK-NOT: {{.*:}}
-; CHECK: store ptr null, ptr %hdl
-; CHECK-NOT: {{.*:}}
-; CHECK: store i1 true, ptr %index.addr
-
-; Tests the use of final index in the destroy function.
-; CHECK: define{{.*}}@unwind_coro_end.destroy
-; CHECK: %[[INDEX:.+]] = load i1, ptr %index.addr
-; CHECK-NEXT: switch i1 %[[INDEX]],
-
-define ptr @nounwind_coro_end(i1 %val) presplitcoroutine personality i32 3 {
-entry:
-  %id = call token @llvm.coro.id(i32 0, ptr null, ptr @nounwind_coro_end, ptr 
null)
-  %hdl = call ptr @llvm.coro.begin(token %id, ptr null)
-  call void @print(i32 0)
-  br label %init
-
-init:
-  %initial_suspend = call i8 @llvm.coro.suspend(token none, i1 false)
-  switch i8 %initial_suspend, label %init_suspend [i8 0, label %init_resume
-                                                   i8 1, label %init_suspend]
-
-init_suspend:
-  ret ptr %hdl
-
-init_resume:
-  br label %susp
-
-susp:
-  %0 = call i8 @llvm.coro.suspend(token none, i1 true)
-  switch i8 %0, label %suspend [i8 0, label %resume
-                                i8 1, label %suspend]
-resume:
-  call void @print(i32 1)
-  br label %suspend
-
-suspend:
-  call void @llvm.coro.end(ptr %hdl, i1 false, token none)
-  call void @print(i32 0)
-  ret ptr %hdl
-}
-
-; Tests that we can omit to store the final suspend index if we don't
-; see unwind coro end.
-; CHECK: define{{.*}}@nounwind_coro_end.resume
-; CHECK-NOT: store i1 true, ptr %index.addr
-; CHECK: }
-
-; Tests that we judge the final suspend case by the nullness of resume 
function.
-; CHECK: define{{.*}}@nounwind_coro_end.destroy
-; CHECK:  %[[RESUME_FN:.+]] = load ptr, ptr %hdl, align 8
-; CHECK:  %{{.*}} = icmp eq ptr %[[RESUME_FN]], null
-
-declare ptr @llvm.coro.free(token, ptr)
-declare i32 @llvm.coro.size.i32()
-declare i8  @llvm.coro.suspend(token, i1)
-declare void @llvm.coro.resume(ptr)
-declare void @llvm.coro.destroy(ptr)
-
-declare token @llvm.coro.id(i32, ptr, ptr, ptr)
-declare ptr @llvm.coro.begin(token, ptr)
-declare void @llvm.coro.end(ptr, i1, token)
-
-declare noalias ptr @malloc(i32)
-declare void @print(i32)
-declare void @free(ptr)

>From e0cec6df0f711cfb4bf8f9ec326fd150815cc38d Mon Sep 17 00:00:00 2001
From: NewSigma <[email protected]>
Date: Sun, 11 Oct 2026 17:18:52 +0800
Subject: [PATCH 2/2] [clang][CodeGenCoroutine] Fix throwing in
 unhandled_exception

---
 clang/lib/CodeGen/CGCoroutine.cpp             | 101 ++++++++++--------
 clang/test/CodeGenCoroutines/coro-cleanup.cpp |   2 +-
 .../coro-destructor-of-final_suspend.cpp      |   6 +-
 .../CodeGenCoroutines/coro-eh-cleanup.cpp     |  40 ++++++-
 .../CodeGenCoroutines/coro-elide-thinlto.cpp  |   4 +-
 clang/test/CodeGenCoroutines/coro-gro.cpp     |  13 +--
 clang/test/CodeGenCoroutines/coro-halo.cpp    |   8 +-
 .../coro-suspend-cleanups.cpp                 |   7 +-
 8 files changed, 110 insertions(+), 71 deletions(-)

diff --git a/clang/lib/CodeGen/CGCoroutine.cpp 
b/clang/lib/CodeGen/CGCoroutine.cpp
index 2f162519139e0..360f35e8d707d 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -555,52 +555,13 @@ namespace {
   };
 }
 
-// For WinEH exception representation backend needs to know what funclet 
coro.end
-// belongs to. That information is passed in a funclet bundle.
-static SmallVector<llvm::OperandBundleDef, 1>
-getBundlesForCoroEnd(CodeGenFunction &CGF) {
-  SmallVector<llvm::OperandBundleDef, 1> BundleList;
-
-  if (llvm::Instruction *EHPad = CGF.CurrentFuncletPad)
-    BundleList.emplace_back("funclet", EHPad);
-
-  return BundleList;
-}
-
-namespace {
-// We will insert coro.end to cut any of the destructors for objects that
-// do not need to be destroyed once the coroutine is resumed.
-// See llvm/docs/Coroutines.md for more details about coro.end.
-struct CallCoroEnd final : public EHScopeStack::Cleanup {
-  void Emit(CodeGenFunction &CGF, Flags flags) override {
-    auto &CGM = CGF.CGM;
-    auto *NullPtr = llvm::ConstantPointerNull::get(CGF.Int8PtrTy);
-    llvm::Function *CoroEndFn = CGM.getIntrinsic(llvm::Intrinsic::coro_end);
-    // See if we have a funclet bundle to associate coro.end with. (WinEH)
-    auto Bundles = getBundlesForCoroEnd(CGF);
-    CGF.Builder.CreateCall(
-        CoroEndFn,
-        {NullPtr, CGF.Builder.getTrue(),
-         llvm::ConstantTokenNone::get(CoroEndFn->getContext())},
-        Bundles);
-    if (Bundles.empty()) {
-      // Otherwise, (landingpad model), create a conditional branch that leads
-      // either to a cleanup block or a block with EH resume instruction.
-      auto *ResumeBB = CGF.getEHResumeBlock(/*isCleanup=*/true);
-      auto *CleanupContBB = CGF.createBasicBlock("cleanup.cont");
-      auto *CoroIsInRampFn = 
CGM.getIntrinsic(llvm::Intrinsic::coro_is_in_ramp);
-      auto *CoroIsInRamp = CGF.Builder.CreateCall(CoroIsInRampFn);
-      CGF.Builder.CreateCondBr(CoroIsInRamp, CleanupContBB, ResumeBB);
-      CGF.EmitBlock(CleanupContBB);
-    }
-  }
-};
-}
-
 namespace {
 // Make sure to call coro.delete on scope exit.
 struct CallCoroDelete final : public EHScopeStack::Cleanup {
   Stmt *Deallocate;
+  // A branch to this block just before unwinding to caller
+  llvm::BasicBlock *CoroUnwind = nullptr;
+  llvm::CallInst *SuspendEH = nullptr;
 
   // Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
 
@@ -609,7 +570,9 @@ struct CallCoroDelete final : public EHScopeStack::Cleanup {
   // contain any declarations. The SubStmtBuilder::makeNewAndDeleteExpr()
   // builds a single call to a deallocation function which is safe to emit
   // multiple times.
-  void Emit(CodeGenFunction &CGF, Flags) override {
+  void Emit(CodeGenFunction &CGF, Flags Kind) override {
+    if (Kind.isForEHCleanup())
+      EmitSuspendEH(CGF);
     // Remember the current point, as we are going to emit deallocation code
     // first to get to coro.free instruction that is an argument to a delete
     // call.
@@ -646,8 +609,58 @@ struct CallCoroDelete final : public EHScopeStack::Cleanup 
{
 
     auto *CoroDeadFn = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_dead);
     CGF.Builder.CreateCall(CoroDeadFn, {CGF.CurCoro.Data->CoroBegin});
+
+    if (Kind.isForEHCleanup())
+      EmitCoroUnwind(CGF);
   }
   explicit CallCoroDelete(Stmt *DeallocStmt) : Deallocate(DeallocStmt) {}
+  // [dcl.fct.def.coroutine]:
+  // a coroutine is suspended at a final suspend point if it is suspended
+  // 1. at a final await expression or
+  // 2. due to an exception exiting from unhandled_exception().
+  //
+  // We simulate a final suspend with normal suspend before setting coroutine
+  // as done.
+  void EmitSuspendEH(CodeGenFunction &CGF) {
+    auto &Builder = CGF.Builder;
+
+    auto *InRampFn = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_is_in_ramp);
+    auto *InRamp = Builder.CreateCall(InRampFn);
+    auto *CleanupContBB = CGF.createBasicBlock("cleanup.cont");
+    auto *EHSuspendBB = CGF.createBasicBlock("eh.suspend");
+    CGF.Builder.CreateCondBr(InRamp, CleanupContBB, EHSuspendBB);
+
+    CGF.EmitBlock(EHSuspendBB);
+    auto *CoroSuspend = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_suspend);
+    SuspendEH = Builder.CreateCall(
+        CoroSuspend, {llvm::ConstantTokenNone::get(Builder.getContext()),
+                      Builder.getInt1(false)});
+    CoroUnwind = CGF.createBasicBlock("coro.unwind");
+    auto *Destroy = Builder.CreateICmpEQ(SuspendEH, Builder.getInt8(1));
+    // coro.end(true) clears resume function,
+    // so we do not have to care about SuspendEH = 0
+    Builder.CreateCondBr(Destroy, CleanupContBB, CoroUnwind);
+
+    CGF.EmitBlock(CleanupContBB);
+  }
+
+  void EmitCoroUnwind(CodeGenFunction &CGF) {
+    auto &Builder = CGF.Builder;
+    auto *CoroEndFn = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_end);
+    auto *NullPtr = llvm::ConstantPointerNull::get(CGF.Int8PtrTy);
+    auto *TokenNone = llvm::ConstantTokenNone::get(CoroEndFn->getContext());
+    CGF.Builder.CreateCall(CoroEndFn,
+                           {NullPtr, CGF.Builder.getFalse(), TokenNone});
+    auto *EHContBB = CGF.createBasicBlock("coro.eh.cont");
+    Builder.CreateBr(EHContBB);
+
+    CGF.EmitBlock(CoroUnwind);
+    CGF.Builder.CreateCall(
+        CoroEndFn, {NullPtr, CGF.Builder.getTrue(), TokenNone});
+    Builder.CreateBr(EHContBB);
+
+    CGF.EmitBlock(EHContBB);
+  }
 };
 }
 
@@ -1023,8 +1036,6 @@ void CodeGenFunction::EmitCoroutineBody(const 
CoroutineBodyStmt &S) {
     GroManager.EmitGroAlloca();
     GroManager.EmitGroInit();
 
-    EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
-
     CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(CleanupBB);
     CurCoro.Data->CurrentAwaitKind = AwaitKind::Init;
     CurCoro.Data->ExceptionHandler = S.getExceptionHandler();
diff --git a/clang/test/CodeGenCoroutines/coro-cleanup.cpp 
b/clang/test/CodeGenCoroutines/coro-cleanup.cpp
index 4e77ac25af1bf..c06469f4f5d9d 100644
--- a/clang/test/CodeGenCoroutines/coro-cleanup.cpp
+++ b/clang/test/CodeGenCoroutines/coro-cleanup.cpp
@@ -51,7 +51,7 @@ void f() {
   // CHECK-NEXT: landingpad
   // CHECK-NEXT:   cleanup
   // THROWEND:        br label %[[Dealloc:.+]]
-  // NOTHROWEND:      icmp ne ptr %[[#]], null
+  // NOTHROWEND:      call i1 @llvm.coro.is_in_ramp()
   // NOTHROWEND-NEXT: br i1 %[[#]], label %[[Dealloc:.+]], label
 
   Cleanup cleanup;
diff --git a/clang/test/CodeGenCoroutines/coro-destructor-of-final_suspend.cpp 
b/clang/test/CodeGenCoroutines/coro-destructor-of-final_suspend.cpp
index 9e35e4ea72a7e..e2479bc982223 100644
--- a/clang/test/CodeGenCoroutines/coro-destructor-of-final_suspend.cpp
+++ b/clang/test/CodeGenCoroutines/coro-destructor-of-final_suspend.cpp
@@ -60,8 +60,10 @@ gen maybe_throwing(bool x) {
 }
 
 // CHECK: define{{.*}}@_Z14maybe_throwingb.destroy
-// CHECK: %[[INDEX:.+]] = load i{{8|32}}, ptr %index.addr, align {{1|4}}
-// CHECK: br i1 {{%.*}}, label %{{.+}}, label %{{.+}}, !prof
+// CHECK: %[[INDEX:.+]] = load i8, ptr %index.addr, align 1
+// CHECK: %[[CMP:.+]] = icmp eq i8 %index, 2
+// CHECK: br i1 %[[CMP]], label %[[AFTERSUSPEND:.+]], label %[[CORO_FREE:.+]], 
!prof
+// CHECK: [[AFTERSUSPEND]]:
 // CHECK: call{{.*}}_ZN3gen12promise_type13final_awaiterD1Ev(
 // CHECK: call{{.*}}_ZdlPv
 
diff --git a/clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp 
b/clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp
index 6b61ccde5728b..3388988a4b2db 100644
--- a/clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp
+++ b/clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp
@@ -54,14 +54,31 @@ coro_t f() {
 // CHECK:   cleanupret from %{{.+}} unwind label %[[CATCHDISPATCH:.+]]
 
 // CHECK: [[CATCHDISPATCH]]:
-// CHECK:   catchswitch within none [label %[[CATCHPAD:.+]]] unwind label 
%[[COROENDBB:.+]]
+// CHECK:   catchswitch within none [label %[[CATCHPAD:.+]]] unwind label 
%[[UNWINDBB:.+]]
 // CHECK: [[CATCHPAD]]:
 // CHECK:   call void @"?unhandled_exception@promise_type@coro_t@@QEAAXXZ"
 
+// CHECK: [[UNWINDBB]]:
+// CHECK-NEXT: %[[CLPAD1:.+]] = cleanuppad within none
+// CHECK-NEXT: call void @llvm.lifetime.end
+// CHECK-NEXT: cleanupret from %[[CLPAD1]] unwind label %[[RAMPBB:.+]]
+
+// CHECK: [[RAMPBB]]:
+// CHECK-NEXT: %[[CLPAD2:.+]] = cleanuppad within none
+// CHECK-NEXT:   %[[InRamp:.+]] = call i1 @llvm.coro.is_in_ramp()
+// CHECK-NEXT:   br i1 %[[InRamp]], label %{{.+}}, label %[[EHSUSPEND:.+]]
+
+// CHECK: [[EHSUSPEND]]:
+// CHECK-NEXT:   call i8 @llvm.coro.suspend(token none, i1 false)
+// CHECK-NEXT:   icmp eq i8 %{{.*}}, 1
+// CHECK-NEXT:   br i1 %{{.*}}, label %{{.*}}, label %[[COROENDBB:.+]]
+
 // CHECK: [[COROENDBB]]:
-// CHECK-NEXT: %[[CLPAD:.+]] = cleanuppad within none
-// CHECK-NEXT: call void @llvm.coro.end(ptr null, i1 true, token none) [ 
"funclet"(token %[[CLPAD]]) ]
-// CHECK-NEXT: cleanupret from %[[CLPAD]] unwind label
+// CHECK-NEXT:   call void @llvm.coro.end(ptr null, i1 true, token none)
+// CHECK-NEXT:   br label %[[CORO_EH_CONT:.+]]
+
+// CHECK: [[CORO_EH_CONT]]:
+// CHECK-NEXT:   cleanupret from %[[CLPAD2]] unwind to caller
 
 // CHECK-LPAD: @_Z1fv(
 // CHECK-LPAD:   invoke void @_Z9may_throwv()
@@ -77,7 +94,20 @@ coro_t f() {
 
 // CHECK-LPAD: [[UNWINDBB]]:
 // CHECK-LPAD:   %[[InRamp:.+]] = call i1 @llvm.coro.is_in_ramp()
-// CHECK-LPAD:   br i1  %[[InRamp]], label %{{.+}}, label %[[EHRESUME:.+]]
+// CHECK-LPAD:   br i1 %[[InRamp]], label %{{.+}}, label %[[EHSUSPEND:.+]]
+
+// CHECK-LPAD: [[EHSUSPEND]]:
+// CHECK-LPAD-NEXT:   call i8 @llvm.coro.suspend(token none, i1 false)
+// CHECK-LPAD-NEXT:   icmp eq i8 %{{.*}}, 1
+// CHECK-LPAD-NEXT:   br i1 %{{.*}}, label %{{.*}}, label %[[CORO_UNWIND:.+]]
+
+// CHECK-LPAD: [[CORO_UNWIND]]:
+// CHECK-LPAD-NEXT:   call void @llvm.coro.end(ptr null, i1 true, token none)
+// CHECK-LPAD-NEXT:   br label %[[CORO_EH_CONT:.+]]
+
+// CHECK-LPAD: [[CORO_EH_CONT]]:
+// CHECK-LPAD-NEXT:   br label %[[EHRESUME:.+]]
+
 // CHECK-LPAD: [[EHRESUME]]:
 // CHECK-LPAD-NEXT:  %[[exn:.+]] = load ptr, ptr %exn.slot, align 8
 // CHECK-LPAD-NEXT:  %[[sel:.+]] = load i32, ptr %ehselector.slot, align 4
diff --git a/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp 
b/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp
index 54063cf0704aa..5d4936dd221d2 100644
--- a/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp
+++ b/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp
@@ -3,8 +3,8 @@
 // This test is adapted from coro-elide.cpp and splits functions into two 
files.
 //
 // RUN: split-file %s %t
-// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c 
%t/coro-elide-callee.cpp -o %t/coro-elide-callee.bc
-// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c 
%t/coro-elide-caller.cpp -o %t/coro-elide-caller.bc
+// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -fno-exceptions -flto=thin 
-I %S -c %t/coro-elide-callee.cpp -o %t/coro-elide-callee.bc
+// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -fno-exceptions -flto=thin 
-I %S -c %t/coro-elide-caller.cpp -o %t/coro-elide-caller.bc
 // RUN: llvm-lto --thinlto %t/coro-elide-callee.bc %t/coro-elide-caller.bc -o 
%t/summary
 // RUN: %clang_cc1 -O2 -x ir %t/coro-elide-caller.bc 
-fthinlto-index=%t/summary.thinlto.bc -emit-llvm -o - | FileCheck %s
 
diff --git a/clang/test/CodeGenCoroutines/coro-gro.cpp 
b/clang/test/CodeGenCoroutines/coro-gro.cpp
index c4c93a9457010..c510a19126d16 100644
--- a/clang/test/CodeGenCoroutines/coro-gro.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro.cpp
@@ -132,16 +132,17 @@ invoker g() {
 
   throw 0;
   // Test that GRO is destructed in EHCleanup
-  // CHECK: call void @llvm.coro.end(ptr null, i1 true, token none)
-  // CHECK-NEXT: call i1 @llvm.coro.is_in_ramp()
-  // CHECK-NEXT: br i1 {{.*}}, label %[[CLEANUP_CONT:.+]], label %eh.resume
-
-  // CHECK: [[CLEANUP_CONT]]
-  // CHECK-NEXT: %[[IS_ACTIVE:.+]] = load i1, ptr %[[RESULT_ACTIVE]]
+  // CHECK: %[[IS_ACTIVE:.+]] = load i1, ptr %[[RESULT_ACTIVE]]
   // CHECK-NEXT: br i1 %[[IS_ACTIVE]], label %[[CLEANUP_ACTION:.+]], label 
%{{.*}}
 
   // CHECK: [[CLEANUP_ACTION]]:
   // CHECK-NEXT: call void @_ZN7invokerD1Ev({{.*}} %[[AggRes]]
+  // CHECK-NEXT: br label %[[CLEANUP_DONE:.+]]
+
+  // CHECK: [[CLEANUP_DONE]]:
+  // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[Promise]])
+  // CHECK-NEXT: call i1 @llvm.coro.is_in_ramp()
+  // CHECK-NEXT: br i1 {{.*}}, label %{{.+}}, label %eh.suspend
   co_return;
 }
 
diff --git a/clang/test/CodeGenCoroutines/coro-halo.cpp 
b/clang/test/CodeGenCoroutines/coro-halo.cpp
index e75bedaf81fa2..ec74ff22f941c 100644
--- a/clang/test/CodeGenCoroutines/coro-halo.cpp
+++ b/clang/test/CodeGenCoroutines/coro-halo.cpp
@@ -65,13 +65,13 @@ template <typename T> struct generator {
 };
 
 template <typename T>
-generator<T> seq() {
+generator<T> seq() noexcept {
   for (T i = {};; ++i)
     co_yield i;
 }
 
 template <typename T>
-generator<T> take_until(generator<T> &g, T limit) {
+generator<T> take_until(generator<T> &g, T limit) noexcept {
   for (auto &&v : g)
     if (v < limit)
       co_yield v;
@@ -80,13 +80,13 @@ generator<T> take_until(generator<T> &g, T limit) {
 }
 
 template <typename T>
-generator<T> multiply(generator<T> &g, T factor) {
+generator<T> multiply(generator<T> &g, T factor) noexcept {
   for (auto &&v : g)
     co_yield v *factor;
 }
 
 template <typename T>
-generator<T> add(generator<T> &g, T adder) {
+generator<T> add(generator<T> &g, T adder) noexcept {
   for (auto &&v : g)
     co_yield v + adder;
 }
diff --git a/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp 
b/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
index 6c05d02d0642f..c766df28b8a84 100644
--- a/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
+++ b/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
@@ -52,11 +52,6 @@ coroutine ArrayInitCoro() {
     // CHECK-NEXT:  @_ZNSt14suspend_always11await_readyEv
     // CHECK-NEXT:  br i1 %{{.+}}, label %await.ready, label 
%{{CoroSave[0-9]+}}
   };
-  // CHECK:       await.cleanup:                                    ; preds = 
%AfterCoroSuspend{{.*}}
-  // CHECK-NEXT:    br label %cleanup{{.*}}.from.await.cleanup
-
-  // CHECK:       cleanup{{.*}}.from.await.cleanup:                      ; 
preds = %await.cleanup
-  // CHECK:         br label %cleanup{{.*}}
 
   // CHECK:       await.ready:
   // CHECK-NEXT:    %arrayinit.element.reload.addr = getelementptr inbounds 
i8, ptr %0, i64 48
@@ -65,7 +60,7 @@ coroutine ArrayInitCoro() {
   // CHECK-NEXT:    store i1 false, ptr %cleanup.isactive.reload.addr, align 1
   // CHECK-NEXT:    br label %cleanup{{.*}}.from.await.ready
 
-  // CHECK:       cleanup{{.*}}:                                         ; 
preds = %cleanup{{.*}}.from.await.ready, %cleanup{{.*}}.from.await.cleanup
+  // CHECK:       cleanup{{.*}}:                                         ; 
preds = %cleanup{{.*}}.from.await.ready
   // CHECK:         %cleanup.is_active = load i1, ptr 
%cleanup.isactive.reload.addr, align 1
   // CHECK-NEXT:    br i1 %cleanup.is_active, label %cleanup.action, label 
%cleanup.done
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to