@@ -22,6 +22,10 @@ using namespace clang;
2222using namespace clang ::CIRGen;
2323
2424struct clang ::CIRGen::CGCoroData {
25+ // What is the current await expression kind and how many
26+ // await/yield expressions were encountered so far.
27+ // These are used to generate pretty labels for await expressions in LLVM IR.
28+ cir::AwaitKind currentAwaitKind = cir::AwaitKind::Init;
2529 // Stores the __builtin_coro_id emitted in the function so that we can supply
2630 // it as the first argument to other builtins.
2731 cir::CallOp coroId = nullptr ;
@@ -249,7 +253,114 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
249253 emitAnyExprToMem (s.getReturnValue (), returnValue,
250254 s.getReturnValue ()->getType ().getQualifiers (),
251255 /* isInit*/ true );
256+
257+ assert (!cir::MissingFeatures::ehCleanupScope ());
258+ // FIXME(cir): EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
259+ curCoro.data ->currentAwaitKind = cir::AwaitKind::Init;
260+ if (emitStmt (s.getInitSuspendStmt (), /* useCurrentScope=*/ true ).failed ())
261+ return mlir::failure ();
252262 assert (!cir::MissingFeatures::emitBodyAndFallthrough ());
253263 }
254264 return mlir::success ();
255265}
266+ // Given a suspend expression which roughly looks like:
267+ //
268+ // auto && x = CommonExpr();
269+ // if (!x.await_ready()) {
270+ // x.await_suspend(...); (*)
271+ // }
272+ // x.await_resume();
273+ //
274+ // where the result of the entire expression is the result of x.await_resume()
275+ //
276+ // (*) If x.await_suspend return type is bool, it allows to veto a suspend:
277+ // if (x.await_suspend(...))
278+ // llvm_coro_suspend();
279+ //
280+ // This is more higher level than LLVM codegen, for that one see llvm's
281+ // docs/Coroutines.rst for more details.
282+ namespace {
283+ struct LValueOrRValue {
284+ LValue lv;
285+ RValue rv;
286+ };
287+ } // namespace
288+
289+ static LValueOrRValue
290+ emitSuspendExpression (CIRGenFunction &cgf, CGCoroData &coro,
291+ CoroutineSuspendExpr const &s, cir::AwaitKind kind,
292+ AggValueSlot aggSlot, bool ignoreResult,
293+ mlir::Block *scopeParentBlock,
294+ mlir::Value &tmpResumeRValAddr, bool forLValue) {
295+ mlir::LogicalResult awaitBuild = mlir::success ();
296+ LValueOrRValue awaitRes;
297+
298+ CIRGenFunction::OpaqueValueMapping binder =
299+ CIRGenFunction::OpaqueValueMapping (cgf, s.getOpaqueValue ());
300+ CIRGenBuilderTy &builder = cgf.getBuilder ();
301+ [[maybe_unused]] cir::AwaitOp awaitOp = cir::AwaitOp::create (
302+ builder, cgf.getLoc (s.getSourceRange ()), kind,
303+ /* readyBuilder=*/
304+ [&](mlir::OpBuilder &b, mlir::Location loc) {
305+ builder.createCondition (
306+ cgf.createDummyValue (loc, cgf.getContext ().BoolTy ));
307+ },
308+ /* suspendBuilder=*/
309+ [&](mlir::OpBuilder &b, mlir::Location loc) {
310+ cir::YieldOp::create (builder, loc);
311+ },
312+ /* resumeBuilder=*/
313+ [&](mlir::OpBuilder &b, mlir::Location loc) {
314+ cir::YieldOp::create (builder, loc);
315+ });
316+
317+ assert (awaitBuild.succeeded () && " Should know how to codegen" );
318+ return awaitRes;
319+ }
320+
321+ static RValue emitSuspendExpr (CIRGenFunction &cgf,
322+ const CoroutineSuspendExpr &e,
323+ cir::AwaitKind kind, AggValueSlot aggSlot,
324+ bool ignoreResult) {
325+ RValue rval;
326+ mlir::Location scopeLoc = cgf.getLoc (e.getSourceRange ());
327+
328+ // Since we model suspend / resume as an inner region, we must store
329+ // resume scalar results in a tmp alloca, and load it after we build the
330+ // suspend expression. An alternative way to do this would be to make
331+ // every region return a value when promise.return_value() is used, but
332+ // it's a bit awkward given that resume is the only region that actually
333+ // returns a value.
334+ mlir::Block *currEntryBlock = cgf.curLexScope ->getEntryBlock ();
335+ [[maybe_unused]] mlir::Value tmpResumeRValAddr;
336+
337+ // No need to explicitly wrap this into a scope since the AST already uses a
338+ // ExprWithCleanups, which will wrap this into a cir.scope anyways.
339+ rval = emitSuspendExpression (cgf, *cgf.curCoro .data , e, kind, aggSlot,
340+ ignoreResult, currEntryBlock, tmpResumeRValAddr,
341+ /* forLValue*/ false )
342+ .rv ;
343+
344+ if (ignoreResult || rval.isIgnored ())
345+ return rval;
346+
347+ if (rval.isScalar ()) {
348+ rval = RValue::get (cir::LoadOp::create (cgf.getBuilder (), scopeLoc,
349+ rval.getValue ().getType (),
350+ tmpResumeRValAddr));
351+ } else if (rval.isAggregate ()) {
352+ // This is probably already handled via AggSlot, remove this assertion
353+ // once we have a testcase and prove all pieces work.
354+ cgf.cgm .errorNYI (" emitSuspendExpr Aggregate" );
355+ } else { // complex
356+ cgf.cgm .errorNYI (" emitSuspendExpr Complex" );
357+ }
358+ return rval;
359+ }
360+
361+ RValue CIRGenFunction::emitCoawaitExpr (const CoawaitExpr &e,
362+ AggValueSlot aggSlot,
363+ bool ignoreResult) {
364+ return emitSuspendExpr (*this , e, curCoro.data ->currentAwaitKind , aggSlot,
365+ ignoreResult);
366+ }
0 commit comments