@@ -33,17 +33,16 @@ struct clang::CIRGen::CGCoroData {
3333 // Stores the result of __builtin_coro_begin call.
3434 mlir::Value coroBegin = nullptr ;
3535
36- // Stores the insertion point for final suspend, this happens after the
37- // promise call (return_xxx promise member) but before a cir.br to the return
38- // block.
39- mlir::Operation *finalSuspendInsPoint;
40-
4136 // How many co_return statements are in the coroutine. Used to decide whether
4237 // we need to add co_return; equivalent at the end of the user authored body.
4338 unsigned coreturnCount = 0 ;
4439
4540 // The promise type's 'unhandled_exception' handler, if it defines one.
4641 Stmt *exceptionHandler = nullptr ;
42+
43+ // Stores the last emitted coro.free for the deallocate expressions, we use it
44+ // to wrap dealloc code with if(auto mem = coro.free) dealloc(mem).
45+ cir::CallOp lastCoroFree = nullptr ;
4746};
4847
4948// Defining these here allows to keep CGCoroData private to this file.
@@ -110,6 +109,63 @@ struct ParamReferenceReplacerRAII {
110109};
111110} // namespace
112111
112+ namespace {
113+ // Make sure to call coro.delete on scope exit.
114+ struct CallCoroDelete final : public EHScopeStack::Cleanup {
115+ Stmt *deallocate;
116+
117+ // Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
118+
119+ // Note: That deallocation will be emitted twice: once for a normal exit and
120+ // once for exceptional exit. This usage is safe because Deallocate does not
121+ // contain any declarations. The SubStmtBuilder::makeNewAndDeleteExpr()
122+ // builds a single call to a deallocation function which is safe to emit
123+ // multiple times.
124+ void emit (CIRGenFunction &cgf, Flags) override {
125+ // Remember the current point, as we are going to emit deallocation code
126+ // first to get to coro.free instruction that is an argument to a delete
127+ // call.
128+
129+ if (cgf.emitStmt (deallocate, /* useCurrentScope=*/ true ).failed ()) {
130+ cgf.cgm .error (deallocate->getBeginLoc (),
131+ " failed to emit coroutine deallocation expression" );
132+ return ;
133+ }
134+
135+ CIRGenBuilderTy &builder = cgf.getBuilder ();
136+ cir::CallOp coroFree = cgf.curCoro .data ->lastCoroFree ;
137+
138+ if (!coroFree) {
139+ cgf.cgm .error (deallocate->getBeginLoc (),
140+ " Deallocation expression does not refer to coro.free" );
141+ return ;
142+ }
143+
144+ builder.setInsertionPointAfter (coroFree);
145+ mlir::Value isPtrNotNull = builder.createPtrIsNotNull (coroFree.getResult ());
146+
147+ llvm::SmallVector<mlir::Operation *> opsToMove;
148+ mlir::Block *block = builder.getInsertionBlock ();
149+ mlir::Block::iterator it (isPtrNotNull.getDefiningOp ());
150+
151+ for (++it; it != block->end (); ++it)
152+ opsToMove.push_back (&*it);
153+
154+ auto ifOp =
155+ cir::IfOp::create (builder, cgf.getLoc (deallocate->getSourceRange ()),
156+ isPtrNotNull, /* withElseRegion*/ false ,
157+ [&](mlir::OpBuilder &builder, mlir::Location loc) {
158+ cir::YieldOp::create (builder, loc);
159+ });
160+
161+ mlir::Operation *yieldOp = ifOp.getThenRegion ().back ().getTerminator ();
162+ for (auto *op : opsToMove)
163+ op->moveBefore (yieldOp);
164+ }
165+ explicit CallCoroDelete (Stmt *deallocStmt) : deallocate(deallocStmt) {}
166+ };
167+ } // namespace
168+
113169RValue CIRGenFunction::emitCoroutineFrame () {
114170 if (curCoro.data && curCoro.data ->coroBegin ) {
115171 return RValue::get (curCoro.data ->coroBegin );
@@ -235,6 +291,28 @@ cir::CallOp CIRGenFunction::emitCoroEndBuiltinCall(mlir::Location loc,
235291 loc, fnOp, mlir::ValueRange{nullPtr, builder.getBool (false , loc)});
236292}
237293
294+ cir::CallOp CIRGenFunction::emitCoroFreeBuiltin (const CallExpr *e) {
295+ mlir::Operation *builtin = cgm.getGlobalValue (cgm.builtinCoroFree );
296+ mlir::Location loc = getLoc (e->getBeginLoc ());
297+ cir::FuncOp fnOp;
298+ if (!builtin) {
299+ fnOp = cgm.createCIRBuiltinFunction (
300+ loc, cgm.builtinCoroFree ,
301+ cir::FuncType::get ({uInt32Ty, voidPtrTy}, voidPtrTy),
302+ /* fd=*/ nullptr );
303+ assert (fnOp && " should always succeed" );
304+ } else {
305+ fnOp = cast<cir::FuncOp>(builtin);
306+ }
307+ cir::CallOp coroFree =
308+ builder.createCallOp (loc, fnOp,
309+ mlir::ValueRange{curCoro.data ->coroId .getResult (),
310+ curCoro.data ->coroBegin });
311+
312+ curCoro.data ->lastCoroFree = coroFree;
313+ return coroFree;
314+ }
315+
238316mlir::LogicalResult
239317CIRGenFunction::emitCoroutineBody (const CoroutineBodyStmt &s) {
240318 mlir::Location openCurlyLoc = getLoc (s.getBeginLoc ());
@@ -280,6 +358,8 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
280358 {
281359 assert (!cir::MissingFeatures::generateDebugInfo ());
282360 ParamReferenceReplacerRAII paramReplacer (localDeclMap);
361+ RunCleanupsScope resumeScope (*this );
362+ ehStack.pushCleanup <CallCoroDelete>(NormalAndEHCleanup, s.getDeallocate ());
283363 // Create mapping between parameters and copy-params for coroutine
284364 // function.
285365 llvm::ArrayRef<const Stmt *> paramMoves = s.getParamMoves ();
@@ -326,11 +406,31 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
326406
327407 curCoro.data ->currentAwaitKind = cir::AwaitKind::User;
328408
329- // FIXME(cir): wrap emitBodyAndFallthrough with try/catch bits.
330- if (s.getExceptionHandler ())
331- assert (!cir::MissingFeatures::coroutineExceptions ());
332- if (emitBodyAndFallthrough (*this , s, s.getBody (), curLexScope).failed ())
333- return mlir::failure ();
409+ mlir::OpBuilder::InsertPoint userBody;
410+ auto coroBodyOp =
411+ cir::CoroBodyOp::create (builder, openCurlyLoc, /* scopeBuilder=*/
412+ [&](mlir::OpBuilder &b, mlir::Location loc) {
413+ userBody = b.saveInsertionPoint ();
414+ });
415+ {
416+ mlir::OpBuilder::InsertionGuard guard (builder);
417+ builder.restoreInsertionPoint (userBody);
418+ // FIXME(cir): wrap emitBodyAndFallthrough with try/catch bits.
419+ if (s.getExceptionHandler ()) {
420+ assert (!cir::MissingFeatures::coroutineExceptions ());
421+ cgm.errorNYI (" exceptions in coroutines are not yet supported in CIR" );
422+ }
423+ if (emitBodyAndFallthrough (*this , s, s.getBody (), curLexScope).failed ()) {
424+ return mlir::failure ();
425+ }
426+ }
427+
428+ mlir::Block &coroBodyBlock = coroBodyOp.getBody ().back ();
429+ if (!coroBodyBlock.mightHaveTerminator ()) {
430+ mlir::OpBuilder::InsertionGuard guard (builder);
431+ builder.setInsertionPointToEnd (&coroBodyBlock);
432+ cir::YieldOp::create (builder, openCurlyLoc);
433+ }
334434
335435 // Note that LLVM checks CanFallthrough by looking into the availability
336436 // of the insert block which is kinda brittle and unintuitive, seems to be
@@ -346,13 +446,26 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
346446 curCoro.data ->currentAwaitKind = cir::AwaitKind::Final;
347447 {
348448 mlir::OpBuilder::InsertionGuard guard (builder);
349- builder.setInsertionPoint (curCoro.data ->finalSuspendInsPoint );
350449 if (emitStmt (s.getFinalSuspendStmt (), /* useCurrentScope=*/ true )
351450 .failed ())
352451 return mlir::failure ();
353452 }
354453 }
355454 }
455+
456+ emitCoroEndBuiltinCall (
457+ openCurlyLoc, builder.getNullPtr (builder.getVoidPtrTy (), openCurlyLoc));
458+ if (auto *ret = cast_or_null<ReturnStmt>(s.getReturnStmt ())) {
459+ // Since we already emitted the return value above, so we shouldn't
460+ // emit it again here.
461+ Expr *previousRetValue = ret->getRetValue ();
462+ ret->setRetValue (nullptr );
463+ if (emitStmt (ret, /* useCurrentScope=*/ true ).failed ())
464+ return mlir::failure ();
465+ // Set the return value back. The code generator, as the AST **Consumer**,
466+ // shouldn't change the AST.
467+ ret->setRetValue (previousRetValue);
468+ }
356469 return mlir::success ();
357470}
358471
@@ -538,13 +651,7 @@ mlir::LogicalResult CIRGenFunction::emitCoreturnStmt(CoreturnStmt const &s) {
538651 // it. The actual return instruction is only inserted during current
539652 // scope cleanup handling.
540653 mlir::Location loc = getLoc (s.getSourceRange ());
541- mlir::Block *retBlock = curLexScope->getOrCreateRetBlock (*this , loc);
542- curCoro.data ->finalSuspendInsPoint =
543- cir::BrOp::create (builder, loc, retBlock);
544-
545- // Insert the new block to continue codegen after branch to ret block,
546- // this will likely be an empty block.
547- builder.createBlock (builder.getBlock ()->getParent ());
654+ cir::CoReturnOp::create (builder, loc);
548655
549656 return mlir::success ();
550657}
0 commit comments