clang 24.0.0git
CIRGenCoroutine.cpp
Go to the documentation of this file.
1//===----- CGCoroutine.cpp - Emit CIR Code for C++ coroutines -------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This contains code dealing with C++ code generation of coroutines.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CIRGenFunction.h"
14#include "mlir/Support/LLVM.h"
15#include "clang/AST/StmtCXX.h"
21
22using namespace clang;
23using namespace clang::CIRGen;
24
26 // What is the current await expression kind and how many
27 // await/yield expressions were encountered so far.
28 // These are used to generate pretty labels for await expressions in LLVM IR.
29 cir::AwaitKind currentAwaitKind = cir::AwaitKind::Init;
30 // Stores the __builtin_coro_id emitted in the function so that we can supply
31 // it as the first argument to other builtins.
32 cir::CoroIdOp coroId = nullptr;
33
34 // Stores the result of __builtin_coro_begin call.
35 mlir::Value coroBegin = nullptr;
36
37 // How many co_return statements are in the coroutine. Used to decide whether
38 // we need to add co_return; equivalent at the end of the user authored body.
39 unsigned coreturnCount = 0;
40
41 // The promise type's 'unhandled_exception' handler, if it defines one.
43
44 // Stores the last emitted coro.free for the deallocate expressions, we use it
45 // to wrap dealloc code with if(auto mem = coro.free) dealloc(mem).
46 cir::CoroFreeOp lastCoroFree = nullptr;
47
48 // A temporary bool alloca that stores whether 'await_resume' threw an
49 // exception. If it did, 'true' is stored in this variable, and the coroutine
50 // body must be skipped. If the promise type does not define an exception
51 // handler, this is null.
53
54 // If coro.id came from the builtin, remember the expression to give better
55 // diagnostic. If CoroIdExpr is nullptr, the coro.id was created by
56 // EmitCoroutineBody.
57 CallExpr const *coroIdExpr = nullptr;
58};
59
60// Defining these here allows to keep CGCoroData private to this file.
63
64namespace {
65// FIXME: both GetParamRef and ParamReferenceReplacerRAII are good template
66// candidates to be shared among LLVM / CIR codegen.
67
68// Hunts for the parameter reference in the parameter copy/move declaration.
69struct GetParamRef : public StmtVisitor<GetParamRef> {
70public:
71 DeclRefExpr *expr = nullptr;
72 GetParamRef() {}
73 void VisitDeclRefExpr(DeclRefExpr *e) {
74 assert(expr == nullptr && "multilple declref in param move");
75 expr = e;
76 }
77 void VisitStmt(Stmt *s) {
78 for (Stmt *c : s->children()) {
79 if (c)
80 Visit(c);
81 }
82 }
83};
84
85// This class replaces references to parameters to their copies by changing
86// the addresses in CGF.LocalDeclMap and restoring back the original values in
87// its destructor.
88struct ParamReferenceReplacerRAII {
89 CIRGenFunction::DeclMapTy savedLocals;
90 CIRGenFunction::DeclMapTy &localDeclMap;
91
92 ParamReferenceReplacerRAII(CIRGenFunction::DeclMapTy &localDeclMap)
93 : localDeclMap(localDeclMap) {}
94
95 void addCopy(const DeclStmt *pm) {
96 // Figure out what param it refers to.
97
98 assert(pm->isSingleDecl());
99 const VarDecl *vd = static_cast<const VarDecl *>(pm->getSingleDecl());
100 const Expr *initExpr = vd->getInit();
101 GetParamRef visitor;
102 visitor.Visit(const_cast<Expr *>(initExpr));
103 assert(visitor.expr);
104 DeclRefExpr *dreOrig = visitor.expr;
105 auto *pd = dreOrig->getDecl();
106
107 auto it = localDeclMap.find(pd);
108 assert(it != localDeclMap.end() && "parameter is not found");
109 savedLocals.insert({pd, it->second});
110
111 auto copyIt = localDeclMap.find(vd);
112 assert(copyIt != localDeclMap.end() && "parameter copy is not found");
113 it->second = copyIt->getSecond();
114 }
115
116 ~ParamReferenceReplacerRAII() {
117 for (auto &&savedLocal : savedLocals) {
118 localDeclMap.insert({savedLocal.first, savedLocal.second});
119 }
120 }
121};
122} // namespace
123
124namespace {
125// Make sure to call coro.delete on scope exit.
126struct CallCoroDelete final : public EHScopeStack::Cleanup {
127 Stmt *deallocate;
128
129 // Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
130
131 // Note: That deallocation will be emitted twice: once for a normal exit and
132 // once for exceptional exit. This usage is safe because Deallocate does not
133 // contain any declarations. The SubStmtBuilder::makeNewAndDeleteExpr()
134 // builds a single call to a deallocation function which is safe to emit
135 // multiple times.
136 void emit(CIRGenFunction &cgf, Flags) override {
137 // Remember the current point, as we are going to emit deallocation code
138 // first to get to coro.free instruction that is an argument to a delete
139 // call.
140
141 if (cgf.emitStmt(deallocate, /*useCurrentScope=*/true).failed()) {
142 cgf.cgm.error(deallocate->getBeginLoc(),
143 "failed to emit coroutine deallocation expression");
144 return;
145 }
146
147 CIRGenBuilderTy &builder = cgf.getBuilder();
148 cir::CoroFreeOp coroFree = cgf.curCoro.data->lastCoroFree;
149
150 if (!coroFree) {
151 cgf.cgm.error(deallocate->getBeginLoc(),
152 "Deallocation expression does not refer to coro.free");
153 return;
154 }
155
156 builder.setInsertionPointAfter(coroFree);
157 mlir::Value isPtrNotNull = builder.createPtrIsNotNull(coroFree.getResult());
158
159 llvm::SmallVector<mlir::Operation *> opsToMove;
160 mlir::Block *block = builder.getInsertionBlock();
161 mlir::Block::iterator it(isPtrNotNull.getDefiningOp());
162
163 for (++it; it != block->end(); ++it)
164 opsToMove.push_back(&*it);
165
166 auto ifOp =
167 cir::IfOp::create(builder, cgf.getLoc(deallocate->getSourceRange()),
168 isPtrNotNull, /*withElseRegion*/ false,
169 [&](mlir::OpBuilder &builder, mlir::Location loc) {
170 cir::YieldOp::create(builder, loc);
171 });
172
173 mlir::Operation *yieldOp = ifOp.getThenRegion().back().getTerminator();
174 for (auto *op : opsToMove)
175 op->moveBefore(yieldOp);
176 }
177 explicit CallCoroDelete(Stmt *deallocStmt) : deallocate(deallocStmt) {}
178};
179} // namespace
180
182 if (curCoro.data && curCoro.data->coroBegin) {
183 return RValue::get(curCoro.data->coroBegin);
184 }
185 cgm.errorNYI("NYI");
186 return RValue();
187}
188
191 cir::CoroIdOp coroId,
192 CallExpr const *coroIdExpr = nullptr) {
193
194 if (curCoro.data) {
195 if (curCoro.data->coroIdExpr)
196 cgf.cgm.error(coroIdExpr->getBeginLoc(),
197 "only one __builtin_coro_id can be used in a function");
198 else if (coroIdExpr)
199 cgf.cgm.error(coroIdExpr->getBeginLoc(),
200 "__builtin_coro_id shall not be used in a C++ coroutine");
201 else
202 llvm_unreachable("EmitCoroutineBodyStatement called twice?");
203
204 return;
205 }
206
207 curCoro.data = std::make_unique<CGCoroData>();
208 curCoro.data->coroId = coroId;
209 curCoro.data->coroIdExpr = coroIdExpr;
210}
211
212static mlir::LogicalResult
214 Stmt *body,
215 const CIRGenFunction::LexicalScope *currLexScope) {
216 if (cgf.emitStmt(body, /*useCurrentScope=*/true).failed())
217 return mlir::failure();
218 // Note that classic codegen checks CanFallthrough by looking into the
219 // availability of the insert block which is kinda brittle and unintuitive,
220 // seems to be related with how landing pads are handled.
221 //
222 // CIRGen handles this by checking pre-existing co_returns in the current
223 // scope instead.
224
225 // From LLVM IR Gen: const bool CanFallthrough = Builder.GetInsertBlock();
226 const bool canFallthrough = !currLexScope->hasCoreturn();
227 if (canFallthrough)
228 if (Stmt *onFallthrough = s.getFallthroughHandler())
229 if (cgf.emitStmt(onFallthrough, /*useCurrentScope=*/true).failed())
230 return mlir::failure();
231
232 return mlir::success();
233}
234
236 mlir::Location loc = getLoc(e->getBeginLoc());
237
239 for (const Expr *arg : e->arguments())
240 args.push_back(emitScalarExpr(arg));
241
242 auto coroId = cir::CoroIdOp::create(cgm.getBuilder(), loc, args);
243 createCoroData(*this, curCoro, coroId, e);
244 return coroId;
245}
246
248 mlir::Location loc = getLoc(e->getBeginLoc());
249 if (!curCoro.data || !curCoro.data->coroId) {
250 cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has"
251 " been used earlier in this function");
252 return {};
253 }
254
255 return cir::CoroAllocOp::create(
256 cgm.getBuilder(), loc,
257 mlir::ValueRange{curCoro.data->coroId.getResult()});
258}
259
261
262 mlir::Location loc = getLoc(e->getBeginLoc());
263 if (!curCoro.data || !curCoro.data->coroId) {
264 cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has"
265 " been used earlier in this function");
266 return {};
267 }
269 args.push_back(curCoro.data->coroId.getResult());
270 for (const Expr *arg : e->arguments())
271 args.push_back(emitScalarExpr(arg));
272
273 auto coroBegin = cir::CoroBeginOp::create(cgm.getBuilder(), loc, args);
274 curCoro.data->coroBegin = coroBegin;
275 return coroBegin;
276}
277
279
280 mlir::Location loc = getLoc(e->getBeginLoc());
281 CIRGenBuilderTy &builder = cgm.getBuilder();
283 for (const Expr *arg : e->arguments())
284 args.push_back(emitScalarExpr(arg));
285 args.push_back(cir::TokenNoneOp::create(builder, loc));
286 return cir::CoroEndOp::create(builder, loc, {cgm.voidTy}, args);
287}
288
290 mlir::Location loc = getLoc(e->getBeginLoc());
291
292 if (!curCoro.data || !curCoro.data->coroId) {
293 cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has"
294 " been used earlier in this function");
295 return {};
296 }
297
298 auto coroFree =
299 cir::CoroFreeOp::create(cgm.getBuilder(), loc,
300 mlir::ValueRange{curCoro.data->coroId.getResult(),
301 curCoro.data->coroBegin});
302
303 curCoro.data->lastCoroFree = coroFree;
304 return coroFree;
305}
306
308 mlir::Location loc = getLoc(e->getBeginLoc());
309 return cir::CoroSizeOp::create(cgm.getBuilder(), loc);
310}
311
312cir::CoroPromiseOp
314 mlir::Location loc = getLoc(e->getBeginLoc());
315
317 for (const Expr *arg : e->arguments())
318 args.push_back(emitScalarExpr(arg));
319
320 auto coroPromise = cir::CoroPromiseOp::create(cgm.getBuilder(), loc, args);
321 return coroPromise;
322}
323
325 mlir::Location loc = getLoc(e->getBeginLoc());
326 return cir::CoroDoneOp::create(cgm.getBuilder(), loc,
327 emitScalarExpr(e->getArg(0)));
328}
329
331 mlir::Location loc = getLoc(e->getBeginLoc());
332 return cir::CoroResumeOp::create(cgm.getBuilder(), loc,
333 emitScalarExpr(e->getArg(0)));
334}
335
336cir::CoroDestroyOp
338 mlir::Location loc = getLoc(e->getBeginLoc());
339 return cir::CoroDestroyOp::create(cgm.getBuilder(), loc,
340 emitScalarExpr(e->getArg(0)));
341}
342
343static mlir::LogicalResult
345
346 CXXCatchStmt catchStmt(s.getBeginLoc(), /*exDecl=*/nullptr,
347 cgf.curCoro.data->exceptionHandler);
348 auto *tryStmt = CXXTryStmt::Create(cgf.getContext(), s.getBeginLoc(),
349 s.getBody(), &catchStmt);
350 struct handlerEmitter final : CIRGenFunction::cxxTryBodyEmitter {
351 const CoroutineBodyStmt &s;
352
353 handlerEmitter(const CoroutineBodyStmt &s) : s(s) /*, scope(scope)*/ {}
354 mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
355 return emitBodyAndFallthrough(cgf, s, s.getBody(), cgf.curLexScope);
356 }
357 ~handlerEmitter() override = default;
358 } emitter{s};
359
360 mlir::LogicalResult res = cgf.emitCXXTryStmt(*tryStmt, emitter);
361
362 return res;
363}
364
365mlir::LogicalResult
367 mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
368 cir::ConstantOp nullPtrCst = builder.getNullPtr(voidPtrTy, openCurlyLoc);
369
370 auto fn = mlir::cast<cir::FuncOp>(curFn);
371 fn.setCoroutine(true);
372 const TargetInfo &ti = cgm.getASTContext().getTargetInfo();
373 unsigned newAlign = ti.getNewAlign() / ti.getCharWidth();
374
375 cir::CoroIdOp coroId = cir::CoroIdOp::create(
376 cgm.getBuilder(), openCurlyLoc,
377 mlir::ValueRange{builder.getUInt32(newAlign, openCurlyLoc), nullPtrCst,
378 nullPtrCst, nullPtrCst});
379 createCoroData(*this, curCoro, coroId);
380
381 // Backend is allowed to elide memory allocations, to help it, emit
382 // auto mem = coro.alloc() ? 0 : ... allocation code ...;
383 cir::CoroAllocOp coroAlloc = cir::CoroAllocOp::create(
384 cgm.getBuilder(), openCurlyLoc,
385 mlir::ValueRange{curCoro.data->coroId.getResult()});
386
387 // Initialize address of coroutine frame to null
388 CanQualType astVoidPtrTy = cgm.getASTContext().VoidPtrTy;
389 mlir::Type allocaTy = convertTypeForMem(astVoidPtrTy);
390 Address coroFrame =
391 createTempAlloca(allocaTy, getContext().getTypeAlignInChars(astVoidPtrTy),
392 openCurlyLoc, "__coro_frame_addr",
393 /*ArraySize=*/nullptr);
394
395 mlir::Value storeAddr = coroFrame.getPointer();
396 builder.CIRBaseBuilderTy::createStore(openCurlyLoc, nullPtrCst, storeAddr);
397 mlir::LogicalResult res = mlir::success();
398 cir::IfOp::create(
399 builder, openCurlyLoc, coroAlloc.getResult(),
400 /*withElseRegion=*/false,
401 /*thenBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
402 mlir::Value allocatedPtr = emitScalarExpr(s.getAllocate());
403 builder.CIRBaseBuilderTy::createStore(loc, allocatedPtr, storeAddr);
404 // Handle allocation failure if 'ReturnStmtOnAllocFailure' was provided.
405 if (Stmt *retOnAllocFailure = s.getReturnStmtOnAllocFailure()) {
406 mlir::Value isPtrNull = builder.createPtrIsNull(allocatedPtr);
407 assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
408 cir::IfOp::create(builder, loc, isPtrNull, /*withElseRegion=*/false,
409 [&](mlir::OpBuilder &b, mlir::Location loc) {
410 res = emitStmt(retOnAllocFailure,
411 /*useCurrentScope=*/true);
412 cir::UnreachableOp::create(builder, loc);
413 });
414 }
415 cir::YieldOp::create(builder, loc);
416 });
417
418 if (res.failed())
419 return res;
420
421 curCoro.data->coroBegin = cir::CoroBeginOp::create(
422 cgm.getBuilder(), openCurlyLoc,
423 mlir::ValueRange{
424 curCoro.data->coroId.getResult(),
425 cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr)});
426
427 {
429 ParamReferenceReplacerRAII paramReplacer(localDeclMap);
430 RunCleanupsScope resumeScope(*this);
431 ehStack.pushCleanup<CallCoroDelete>(NormalAndEHCleanup, s.getDeallocate());
432 // Create mapping between parameters and copy-params for coroutine
433 // function.
434 llvm::ArrayRef<const Stmt *> paramMoves = s.getParamMoves();
435 assert((paramMoves.size() == 0 || (paramMoves.size() == fnArgs.size())) &&
436 "ParamMoves and FnArgs should be the same size for coroutine "
437 "function");
438 // For zipping the arg map into debug info.
440
441 // Create parameter copies. We do it before creating a promise, since an
442 // evolution of coroutine TS may allow promise constructor to observe
443 // parameter copies.
445 for (auto *pm : paramMoves) {
446 if (emitStmt(pm, /*useCurrentScope=*/true).failed())
447 return mlir::failure();
448 paramReplacer.addCopy(cast<DeclStmt>(pm));
449 }
450
451 if (emitStmt(s.getPromiseDeclStmt(), /*useCurrentScope=*/true).failed())
452 return mlir::failure();
453 // returnValue should be valid as long as the coroutine's return type
454 // is not void. The assertion could help us to reduce the check later.
455 assert(returnValue.isValid() == (bool)s.getReturnStmt());
456 // Now we have the promise, initialize the GRO.
457 // We need to emit `get_return_object` first. According to:
458 // [dcl.fct.def.coroutine]p7
459 // The call to get_return_­object is sequenced before the call to
460 // initial_suspend and is invoked at most once.
461 //
462 // So we couldn't emit return value when we emit return statment,
463 // otherwise the call to get_return_object wouldn't be in front
464 // of initial_suspend.
465 if (returnValue.isValid())
466 emitAnyExprToMem(s.getReturnValue(), returnValue,
467 s.getReturnValue()->getType().getQualifiers(),
468 /*isInit*/ true);
469
471
472 curCoro.data->currentAwaitKind = cir::AwaitKind::Init;
473 curCoro.data->exceptionHandler = s.getExceptionHandler();
474
475 if (emitStmt(s.getInitSuspendStmt(), /*useCurrentScope=*/true).failed())
476 return mlir::failure();
477
478 curCoro.data->currentAwaitKind = cir::AwaitKind::User;
479
480 mlir::OpBuilder::InsertPoint userBody;
481 auto coroBodyOp =
482 cir::CoroBodyOp::create(builder, openCurlyLoc, /*scopeBuilder=*/
483 [&](mlir::OpBuilder &b, mlir::Location loc) {
484 userBody = b.saveInsertionPoint();
485 });
486 {
487 mlir::OpBuilder::InsertionGuard guard(builder);
488 builder.restoreInsertionPoint(userBody);
489 if (curCoro.data->exceptionHandler) {
490 // This bit of code is supposed to do:
491 //
492 // if (await-resume-didnt-throw-exception) {
493 // try {
494 // coroutine-body
495 // } catch (...) {
496 // unhandled_exception();
497 // }
498 // }
499 //
500 // IF resume couldn't have thrown an exception(await_resume is
501 // noexcept), we skip the 'if'.
502 //
503 // Note that we've reversed the condition of the 'if' from classic
504 // codegen so that we don't need an 'else' block.
505 if (curCoro.data->resumeEHVar.isValid()) {
506 mlir::Value shouldSkip = builder.createFlagLoad(
507 openCurlyLoc, curCoro.data->resumeEHVar.getPointer());
508 mlir::LogicalResult res = mlir::success();
509 cir::IfOp::create(builder, openCurlyLoc, shouldSkip,
510 /*withElseRegion=*/false,
511 [&](mlir::OpBuilder &b, mlir::Location loc) {
512 res = coroutineBodyExceptionHelper(*this, s);
513 builder.createYield(openCurlyLoc);
514 });
515
516 if (res.failed())
517 return mlir::failure();
518
519 } else if (coroutineBodyExceptionHelper(*this, s).failed()) {
520 return mlir::failure();
521 }
522 } else if (emitBodyAndFallthrough(*this, s, s.getBody(), curLexScope)
523 .failed()) {
524 return mlir::failure();
525 }
526 }
527
528 mlir::Block &coroBodyBlock = coroBodyOp.getBody().back();
529 if (!coroBodyBlock.mightHaveTerminator()) {
530 mlir::OpBuilder::InsertionGuard guard(builder);
531 builder.setInsertionPointToEnd(&coroBodyBlock);
532 cir::YieldOp::create(builder, openCurlyLoc);
533 }
534
535 // Note that LLVM checks CanFallthrough by looking into the availability
536 // of the insert block which is kinda brittle and unintuitive, seems to be
537 // related with how landing pads are handled.
538 //
539 // CIRGen handles this by checking pre-existing co_returns in the current
540 // scope instead.
541 //
542 // From LLVM IR Gen: const bool CanFallthrough = Builder.GetInsertBlock();
543 const bool canFallthrough = curLexScope->hasCoreturn();
544 const bool hasCoreturns = curCoro.data->coreturnCount > 0;
545 if (canFallthrough || hasCoreturns) {
546 curCoro.data->currentAwaitKind = cir::AwaitKind::Final;
547 {
548 mlir::OpBuilder::InsertionGuard guard(builder);
549 if (emitStmt(s.getFinalSuspendStmt(), /*useCurrentScope=*/true)
550 .failed())
551 return mlir::failure();
552 }
553 }
554 }
555
556 cir::ConstantOp nullHandler =
557 builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc);
558 cir::ConstantOp noUnwind = builder.getBool(false, openCurlyLoc);
559 auto tkNone = cir::TokenNoneOp::create(builder, openCurlyLoc);
560 cir::CoroEndOp::create(builder, openCurlyLoc, nullHandler, noUnwind, tkNone);
561
562 if (auto *ret = cast_or_null<ReturnStmt>(s.getReturnStmt())) {
563 // Since we already emitted the return value above, so we shouldn't
564 // emit it again here.
565 Expr *previousRetValue = ret->getRetValue();
566 ret->setRetValue(nullptr);
567 if (emitStmt(ret, /*useCurrentScope=*/true).failed())
568 return mlir::failure();
569 // Set the return value back. The code generator, as the AST **Consumer**,
570 // shouldn't change the AST.
571 ret->setRetValue(previousRetValue);
572 }
573 return mlir::success();
574}
575
576static bool memberCallExpressionCanThrow(const Expr *e) {
577 if (const auto *ce = dyn_cast<CXXMemberCallExpr>(e))
578 if (const auto *proto =
579 ce->getMethodDecl()->getType()->getAs<FunctionProtoType>())
580 if (isNoexceptExceptionSpec(proto->getExceptionSpecType()) &&
581 proto->canThrow() == CT_Cannot)
582 return false;
583 return true;
584}
585
586// Given a suspend expression which roughly looks like:
587//
588// auto && x = CommonExpr();
589// if (!x.await_ready()) {
590// x.await_suspend(...); (*)
591// }
592// x.await_resume();
593//
594// where the result of the entire expression is the result of x.await_resume()
595//
596// (*) If x.await_suspend return type is bool, it allows to veto a suspend:
597// if (x.await_suspend(...))
598// llvm_coro_suspend();
599//
600// This is more higher level than LLVM codegen, for that one see llvm's
601// docs/Coroutines.md for more details.
602namespace {
603struct LValueOrRValue {
604 LValue lv;
605 RValue rv;
606};
607} // namespace
608
609static LValueOrRValue
611 CoroutineSuspendExpr const &s, cir::AwaitKind kind,
612 AggValueSlot aggSlot, bool ignoreResult,
613 mlir::Block *scopeParentBlock,
614 mlir::Value &tmpResumeRValAddr, bool forLValue) {
615 [[maybe_unused]] mlir::LogicalResult awaitBuild = mlir::success();
616 LValueOrRValue awaitRes;
617
620 CIRGenBuilderTy &builder = cgf.getBuilder();
621 [[maybe_unused]] cir::AwaitOp awaitOp = cir::AwaitOp::create(
622 builder, cgf.getLoc(s.getSourceRange()), kind,
623 /*readyBuilder=*/
624 [&](mlir::OpBuilder &b, mlir::Location loc) {
625 Expr *condExpr = s.getReadyExpr()->IgnoreParens();
626 builder.createCondition(cgf.evaluateExprAsBool(condExpr));
627 },
628 /*suspendBuilder=*/
629 [&](mlir::OpBuilder &b, mlir::Location loc) {
630 // Note that differently from LLVM codegen we do not emit coro.save
631 // and coro.suspend here, that should be done as part of lowering this
632 // to LLVM dialect (or some other MLIR dialect)
633
634 // A invalid suspendRet indicates "void returning await_suspend"
635 mlir::Value suspendRet = cgf.emitScalarExpr(s.getSuspendExpr());
636
637 // Veto suspension if requested by bool returning await_suspend.
638 if (suspendRet) {
639 cgf.cgm.errorNYI("Veto await_suspend");
640 }
641
642 // Signals the parent that execution flows to next region.
643 cir::YieldOp::create(builder, loc);
644 },
645 /*resumeBuilder=*/
646 [&](mlir::OpBuilder &b, mlir::Location loc) {
647 // Exception handling requires additional IR. If the 'await_resume'
648 // function is marked as 'noexcept', we avoid generating this additional
649 // IR.
650 if (coro.exceptionHandler && kind == cir::AwaitKind::Init &&
651 memberCallExpressionCanThrow(s.getResumeExpr())) {
652 // we are basically just emitting:
653 // resumeEh = false;
654 // try {
655 // resumeExpr();
656 // resumeEh = true;
657 // } catch(...) {
658 // exceptionHandler();
659 // }
660 // Note the values of resumeEh are reversed from classic codegen,
661 // simply so we can use an 'IfOp' without a 'else' later.
662 ASTContext &ctx = cgf.getContext();
663 SourceLocation resumeLoc = s.getResumeExpr()->getExprLoc();
664 mlir::Location mlirLoc = cgf.getLoc(resumeLoc);
665 coro.resumeEHVar = cgf.createTempAlloca(
666 builder.getBoolTy(), ctx.getTypeAlignInChars(ctx.BoolTy), mlirLoc,
667 "resume.eh");
668 builder.createFlagStore(mlirLoc, false,
669 coro.resumeEHVar.getPointer());
670
671 CXXCatchStmt catchStmt(resumeLoc,
672 /*exDecl=*/nullptr, coro.exceptionHandler);
673 auto *tryBody =
674 CompoundStmt::Create(ctx, s.getResumeExpr(), FPOptionsOverride(),
675 resumeLoc, resumeLoc);
676 CXXTryStmt *tryStmt =
677 CXXTryStmt::Create(ctx, resumeLoc, tryBody, &catchStmt);
678
679 struct resumeEmitter final : CIRGenFunction::cxxTryBodyEmitter {
680 const CXXTryStmt &tryStmt;
681 mlir::Location loc;
682 mlir::Value resumeEHVar;
683 resumeEmitter(const CXXTryStmt &tryStmt, mlir::Location loc,
684 Address resumeEHVar)
685 : tryStmt(tryStmt), loc(loc),
686 resumeEHVar(resumeEHVar.getPointer()) {}
687
688 mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
689 mlir::LogicalResult res =
690 cgf.emitStmt(tryStmt.getTryBlock(), /*useCurrentScope=*/true);
691 cgf.getBuilder().createFlagStore(loc, true, resumeEHVar);
692 return res;
693 }
694
695 ~resumeEmitter() override = default;
696 } emitter{*tryStmt, mlirLoc, coro.resumeEHVar};
697
698 awaitBuild = cgf.emitCXXTryStmt(*tryStmt, emitter);
699 // We are not supposed to obtain the value from init suspend
700 // await_resume().
701 awaitRes.rv = RValue::getIgnored();
702 } else if (forLValue) {
703 // FIXME(cir): the alloca for the resume expr should be placed in the
704 // enclosing cir.scope instead.
705 awaitRes.lv = cgf.emitLValue(s.getResumeExpr());
706 } else {
707 awaitRes.rv =
708 cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult);
709 if (!awaitRes.rv.isIgnored()) {
710 // Create the alloca in the block before the scope wrapping
711 // cir.await.
712 mlir::Value value;
713 RValue rv = awaitRes.rv;
714 if (rv.isScalar()) {
715 value = rv.getValue();
716 } else if (rv.isComplex()) {
717 value = rv.getComplexValue();
718 } else {
719 cgf.cgm.errorNYI("emitSuspendExpression: Aggregate value");
720 return;
721 }
722
723 tmpResumeRValAddr = cgf.emitAlloca(
724 "__coawait_resume_rval", value.getType(), loc, CharUnits::One(),
725 builder.getBestAllocaInsertPoint(scopeParentBlock));
726 // Store the rvalue so we can reload it before the promise call.
727 builder.CIRBaseBuilderTy::createStore(loc, value,
728 tmpResumeRValAddr);
729 }
730 }
731
732 // Returns control back to parent.
733 cir::YieldOp::create(builder, loc);
734 });
735
736 assert(awaitBuild.succeeded() && "Should know how to codegen");
737 return awaitRes;
738}
739
741 const CoroutineSuspendExpr &e,
742 cir::AwaitKind kind, AggValueSlot aggSlot,
743 bool ignoreResult) {
744 RValue rval;
745 mlir::Location scopeLoc = cgf.getLoc(e.getSourceRange());
746
747 // Since we model suspend / resume as an inner region, we must store
748 // resume scalar results in a tmp alloca, and load it after we build the
749 // suspend expression. An alternative way to do this would be to make
750 // every region return a value when promise.return_value() is used, but
751 // it's a bit awkward given that resume is the only region that actually
752 // returns a value.
753 mlir::Block *currEntryBlock = cgf.curLexScope->getEntryBlock();
754 [[maybe_unused]] mlir::Value tmpResumeRValAddr;
755
756 // No need to explicitly wrap this into a scope since the AST already uses a
757 // ExprWithCleanups, which will wrap this into a cir.scope anyways.
758 rval = emitSuspendExpression(cgf, *cgf.curCoro.data, e, kind, aggSlot,
759 ignoreResult, currEntryBlock, tmpResumeRValAddr,
760 /*forLValue*/ false)
761 .rv;
762
763 if (ignoreResult || rval.isIgnored())
764 return rval;
765
766 if (rval.isScalar()) {
767 rval = RValue::get(cir::LoadOp::create(cgf.getBuilder(), scopeLoc,
768 rval.getValue().getType(),
769 tmpResumeRValAddr));
770 } else if (rval.isAggregate()) {
771 // This is probably already handled via AggSlot, remove this assertion
772 // once we have a testcase and prove all pieces work.
773 cgf.cgm.errorNYI("emitSuspendExpr Aggregate");
774 } else { // complex
775 rval = RValue::getComplex(cir::LoadOp::create(
776 cgf.getBuilder(), scopeLoc, rval.getComplexValue().getType(),
777 tmpResumeRValAddr));
778 }
779 return rval;
780}
781
783 AggValueSlot aggSlot,
784 bool ignoreResult) {
785 return emitSuspendExpr(*this, e, curCoro.data->currentAwaitKind, aggSlot,
786 ignoreResult);
787}
788
790 AggValueSlot aggSlot,
791 bool ignoreResult) {
792 return emitSuspendExpr(*this, e, cir::AwaitKind::Yield, aggSlot,
793 ignoreResult);
794}
795
796mlir::LogicalResult CIRGenFunction::emitCoreturnStmt(CoreturnStmt const &s) {
797 ++curCoro.data->coreturnCount;
798 curLexScope->setCoreturn();
799
800 const Expr *rv = s.getOperand();
801 if (rv && rv->getType()->isVoidType() && !isa<InitListExpr>(rv)) {
802 // Make sure to evaluate the non initlist expression of a co_return
803 // with a void expression for side effects.
804 RunCleanupsScope cleanupScope(*this);
805 emitIgnoredExpr(rv);
806 }
807
808 if (emitStmt(s.getPromiseCall(), /*useCurrentScope=*/true).failed())
809 return mlir::failure();
810 // Create a new return block (if not existent) and add a branch to
811 // it. The actual return instruction is only inserted during current
812 // scope cleanup handling.
813 mlir::Location loc = getLoc(s.getSourceRange());
814 cir::CoReturnOp::create(builder, loc);
815
816 return mlir::success();
817}
static void emit(Program &P, llvm::SmallVectorImpl< std::byte > &Code, const T &Val, bool &Success)
Helper to write bytecode and bail out if 32-bit offsets become invalid.
static void createCoroData(CIRGenFunction &cgf, CIRGenFunction::CGCoroInfo &curCoro, cir::CoroIdOp coroId, CallExpr const *coroIdExpr=nullptr)
static LValueOrRValue emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro, CoroutineSuspendExpr const &s, cir::AwaitKind kind, AggValueSlot aggSlot, bool ignoreResult, mlir::Block *scopeParentBlock, mlir::Value &tmpResumeRValAddr, bool forLValue)
static RValue emitSuspendExpr(CIRGenFunction &cgf, const CoroutineSuspendExpr &e, cir::AwaitKind kind, AggValueSlot aggSlot, bool ignoreResult)
static bool memberCallExpressionCanThrow(const Expr *e)
static mlir::LogicalResult coroutineBodyExceptionHelper(CIRGenFunction &cgf, const CoroutineBodyStmt &s)
static mlir::LogicalResult emitBodyAndFallthrough(CIRGenFunction &cgf, const CoroutineBodyStmt &s, Stmt *body, const CIRGenFunction::LexicalScope *currLexScope)
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
cir::StoreOp createFlagStore(mlir::Location loc, bool val, mlir::Value dst)
mlir::Value createPtrIsNotNull(mlir::Value ptr)
cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value={})
Create a yield operation.
cir::LoadOp createFlagLoad(mlir::Location loc, mlir::Value addr)
Emit a load from an boolean flag variable.
CharUnits getTypeAlignInChars(QualType T) const
Return the ABI-specified alignment of a (complete) type T, in characters.
CanQualType BoolTy
mlir::Value getPointer() const
Definition Address.h:98
static Address invalid()
Definition Address.h:76
bool isValid() const
Definition Address.h:77
An aggregate value slot.
An RAII object to set (and then clear) a mapping for an OpaqueValueExpr.
Enters a new scope for capturing cleanups, all of which will be executed once the scope is exited.
mlir::LogicalResult emitCoreturnStmt(const CoreturnStmt &s)
cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc, const Twine &name="tmp", mlir::Value arraySize=nullptr, bool insertIntoFnEntryBlock=false)
This creates an alloca and inserts it into the entry block if ArraySize is nullptr,...
cir::CoroAllocOp emitCoroAllocBuiltinCall(const CallExpr *e)
llvm::DenseMap< const clang::Decl *, Address > DeclMapTy
LValue emitLValue(const clang::Expr *e)
Emit code to compute a designator that specifies the location of the expression.
mlir::Location getLoc(clang::SourceLocation srcLoc)
Helpers to convert Clang's SourceLocation to a MLIR Location.
cir::CoroBeginOp emitCoroBeginBuiltinCall(const CallExpr *e)
void emitAnyExprToMem(const Expr *e, Address location, Qualifiers quals, bool isInitializer)
Emits the code necessary to evaluate an arbitrary expression into the given memory location.
RValue emitCoyieldExpr(const CoyieldExpr &e, AggValueSlot aggSlot=AggValueSlot::ignored(), bool ignoreResult=false)
cir::CoroFreeOp emitCoroFreeBuiltin(const CallExpr *e)
mlir::Operation * curFn
The current function or global initializer that is generated code for.
EHScopeStack ehStack
Tracks function scope overall cleanup handling.
llvm::SmallVector< const ParmVarDecl * > fnArgs
Save Parameter Decl for coroutine.
mlir::Type convertTypeForMem(QualType t)
mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s, cxxTryBodyEmitter &bodyCallback)
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, mlir::Location loc, clang::CharUnits alignment, bool insertIntoFnEntryBlock, mlir::Value arraySize=nullptr)
cir::CoroPromiseOp emitCoroPromiseBuiltinCall(const CallExpr *e)
Address returnValue
The temporary alloca to hold the return value.
cir::CoroDestroyOp emitCoroDestroyBuiltinCall(const CallExpr *e)
cir::CoroEndOp emitCoroEndBuiltinCall(const CallExpr *e)
mlir::Value emitScalarExpr(const clang::Expr *e, bool ignoreResultAssign=false)
Emit the computation of the specified expression of scalar type.
CIRGenBuilderTy & getBuilder()
DeclMapTy localDeclMap
This keeps track of the CIR allocas or globals for local C declarations.
RValue emitCoawaitExpr(const CoawaitExpr &e, AggValueSlot aggSlot=AggValueSlot::ignored(), bool ignoreResult=false)
RValue emitAnyExpr(const clang::Expr *e, AggValueSlot aggSlot=AggValueSlot::ignored(), bool ignoreResult=false)
Emit code to compute the specified expression which can have any type.
cir::CoroSizeOp emitCoroSizeBuiltinCall(const CallExpr *e)
cir::CoroDoneOp emitCoroDoneBuiltinCall(const CallExpr *e)
cir::CoroResumeOp emitCoroResumeBuiltinCall(const CallExpr *e)
cir::CoroIdOp emitCoroIDBuiltinCall(const CallExpr *e)
clang::ASTContext & getContext() const
mlir::LogicalResult emitCoroutineBody(const CoroutineBodyStmt &s)
mlir::LogicalResult emitStmt(const clang::Stmt *s, bool useCurrentScope, llvm::ArrayRef< const Attr * > attrs={})
void emitIgnoredExpr(const clang::Expr *e)
Emit code to compute the specified expression, ignoring the result.
DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef)
Helpers to emit "not yet implemented" error diagnostics.
CIRGenBuilderTy & getBuilder()
void error(SourceLocation loc, llvm::StringRef error)
Emit a general error that something can't be done.
This trivial value class is used to represent the result of an expression that is evaluated.
Definition CIRGenValue.h:33
bool isAggregate() const
Definition CIRGenValue.h:51
static RValue get(mlir::Value v)
Definition CIRGenValue.h:83
static RValue getComplex(mlir::Value v)
Definition CIRGenValue.h:91
bool isComplex() const
Definition CIRGenValue.h:50
mlir::Value getValue() const
Return the value of this scalar value.
Definition CIRGenValue.h:57
bool isScalar() const
Definition CIRGenValue.h:49
bool isIgnored() const
Definition CIRGenValue.h:52
mlir::Value getComplexValue() const
Return the value of this complex value.
Definition CIRGenValue.h:63
static RValue getIgnored()
Definition CIRGenValue.h:78
CXXCatchStmt - This represents a C++ catch block.
Definition StmtCXX.h:29
static CXXTryStmt * Create(const ASTContext &C, SourceLocation tryLoc, CompoundStmt *tryBlock, ArrayRef< Stmt * > handlers)
Definition StmtCXX.cpp:26
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2987
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3191
SourceLocation getBeginLoc() const
Definition Expr.h:3321
arg_range arguments()
Definition Expr.h:3239
static CharUnits One()
One - Construct a CharUnits quantity of one.
Definition CharUnits.h:58
Represents a 'co_await' expression.
Definition ExprCXX.h:5422
static CompoundStmt * Create(const ASTContext &C, ArrayRef< Stmt * > Stmts, FPOptionsOverride FPFeatures, SourceLocation LB, SourceLocation RB)
Definition Stmt.cpp:399
Represents a 'co_return' statement in the C++ Coroutines TS.
Definition StmtCXX.h:474
Expr * getOperand() const
Retrieve the operand of the 'co_return' statement.
Definition StmtCXX.h:498
Expr * getPromiseCall() const
Retrieve the promise call that results from this 'co_return' statement.
Definition StmtCXX.h:503
Represents the body of a coroutine.
Definition StmtCXX.h:321
CompoundStmt * getBody() const
Retrieve the body of the coroutine as written.
Definition StmtCXX.h:381
Stmt * getFallthroughHandler() const
Definition StmtCXX.h:402
SourceLocation getBeginLoc() const LLVM_READONLY
Definition StmtCXX.h:428
Represents an expression that might suspend coroutine execution; either a co_await or co_yield expres...
Definition ExprCXX.h:5308
OpaqueValueExpr * getOpaqueValue() const
getOpaqueValue - Return the opaque value placeholder.
Definition ExprCXX.h:5362
Represents a 'co_yield' expression.
Definition ExprCXX.h:5503
A reference to a declared variable, function, enum, etc.
Definition Expr.h:1290
ValueDecl * getDecl()
Definition Expr.h:1358
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition Stmt.h:1656
const Decl * getSingleDecl() const
Definition Stmt.h:1658
This represents one expression.
Definition Expr.h:113
QualType getType() const
Definition Expr.h:145
Represents a prototype with parameter type info, e.g.
Definition TypeBase.h:5398
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
Stmt - This represents one statement.
Definition Stmt.h:85
child_range children()
Definition Stmt.cpp:304
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
Exposes information about the current target.
Definition TargetInfo.h:226
unsigned getNewAlign() const
Return the largest alignment for which a suitably-sized allocation with 'operator new(size_t)' is gua...
Definition TargetInfo.h:760
unsigned getCharWidth() const
Definition TargetInfo.h:526
bool isVoidType() const
Definition TypeBase.h:9037
const Expr * getInit() const
Definition Decl.h:1392
Defines the clang::TargetInfo interface.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
Top level wrappers for InstallAPI frontend operations.
CanQual< Type > CanQualType
Represents a canonical, potentially-qualified type.
bool isa(CodeGen::Address addr)
Definition Address.h:330
bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType)
U cast(CodeGen::Address addr)
Definition Address.h:327
static bool ehCleanupScope()
static bool coroOutsideFrameMD()
static bool generateDebugInfo()
std::unique_ptr< CGCoroData > data
Represents a scope, including function bodies, compound statements, and the substatements of if/while...
cir::PointerType voidPtrTy
void* in address space 0