[clang] [CIR] Add coroutine cleanup handling and update co_return semantics (PR #189281)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 6 14:37:49 PDT 2026
================
@@ -110,6 +109,66 @@ struct ParamReferenceReplacerRAII {
};
} // namespace
+namespace {
+// Make sure to call coro.delete on scope exit.
+struct CallCoroDelete final : public EHScopeStack::Cleanup {
+ Stmt *deallocate;
+
+ // Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
+
+ // Note: That deallocation will be emitted twice: once for a normal exit and
+ // once for exceptional exit. This usage is safe because Deallocate does not
+ // contain any declarations. The SubStmtBuilder::makeNewAndDeleteExpr()
+ // builds a single call to a deallocation function which is safe to emit
+ // multiple times.
+ void emit(CIRGenFunction &cgf, Flags) override {
+ // 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.
+
+ if (cgf.emitStmt(deallocate, /*useCurrentScope=*/true).failed()) {
+ cgf.cgm.error(deallocate->getBeginLoc(),
+ "failed to emit coroutine deallocation expression");
+ return;
+ }
+
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ mlir::Location loc = cgf.getLoc(deallocate->getSourceRange());
+ cir::CallOp coroFree = cgf.curCoro.data->lastCoroFree;
+
+ if (!coroFree) {
+ cgf.cgm.error(deallocate->getBeginLoc(),
+ "Deallocation expressoin does not refer to coro.free");
+ return;
+ }
+
+ builder.setInsertionPointAfter(coroFree);
+ cir::ConstantOp nullPtrCst = builder.getNullPtr(cgf.voidPtrTy, loc);
+ auto cmp = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
+ coroFree.getResult(), nullPtrCst);
+
+ llvm::SmallVector<mlir::Operation *> opsToMove;
+ mlir::Block *block = cmp->getBlock();
+ mlir::Block::iterator it(cmp);
+
+ for (++it; it != block->end(); ++it)
+ opsToMove.push_back(&*it);
----------------
Andres-Salamanca wrote:
Yes, that’s exactly what I’m doing I’m moving the instructions generated by
`cgf.emitStmt(deallocate, /*useCurrentScope=*/true)` into the `if` body.
I’m making a couple of assumptions here:
First, that the deallocation AST always includes a call to `coro.free`. This comes from Sema (`makeNewAndDeleteExpr`)https://github.com/llvm/llvm-project/blob/8a11fe97a299b8b384195d52f317e198bb3a27d4/clang/lib/Sema/SemaCoroutine.cpp#L1650-L1660, so it’s not user-dependent.
Second, I’m assuming everything is emitted into a single block. The reason is that this AST node is fully constructed in Sema and doesn’t introduce any control flow it’s just a call to `coro.free` followed by a delete call so CodeGen ends up emitting it linearly.
Based on that, I just move everything after the `coro.free` into the `if`.
Do you think this assumption is reasonable here?
https://github.com/llvm/llvm-project/pull/189281
More information about the cfe-commits
mailing list