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