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"
20
21using namespace clang;
22using namespace clang::CIRGen;
23
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;
29 // Stores the __builtin_coro_id emitted in the function so that we can supply
30 // it as the first argument to other builtins.
31 cir::CoroIdOp coroId = nullptr;
32
33 // Stores the result of __builtin_coro_begin call.
34 mlir::Value coroBegin = nullptr;
35
36 // How many co_return statements are in the coroutine. Used to decide whether
37 // we need to add co_return; equivalent at the end of the user authored body.
38 unsigned coreturnCount = 0;
39
40 // The promise type's 'unhandled_exception' handler, if it defines one.
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::CoroFreeOp lastCoroFree = nullptr;
46
47 // A temporary bool alloca that stores whether 'await_resume' threw an
48 // exception. If it did, 'true' is stored in this variable, and the coroutine
49 // body must be skipped. If the promise type does not define an exception
50 // handler, this is null.
52
53 // If coro.id came from the builtin, remember the expression to give better
54 // diagnostic. If CoroIdExpr is nullptr, the coro.id was created by
55 // EmitCoroutineBody.
56 CallExpr const *coroIdExpr = nullptr;
57};
58
59// Defining these here allows to keep CGCoroData private to this file.
62
63namespace {
64// FIXME: both GetParamRef and ParamReferenceReplacerRAII are good template
65// candidates to be shared among LLVM / CIR codegen.
66
67// Hunts for the parameter reference in the parameter copy/move declaration.
68struct GetParamRef : public StmtVisitor<GetParamRef> {
69public:
70 DeclRefExpr *expr = nullptr;
71 GetParamRef() {}
72 void VisitDeclRefExpr(DeclRefExpr *e) {
73 assert(expr == nullptr && "multilple declref in param move");
74 expr = e;
75 }
76 void VisitStmt(Stmt *s) {
77 for (Stmt *c : s->children()) {
78 if (c)
79 Visit(c);
80 }
81 }
82};
83
84// This class replaces references to parameters to their copies by changing
85// the addresses in CGF.LocalDeclMap and restoring back the original values in
86// its destructor.
87struct ParamReferenceReplacerRAII {
88 CIRGenFunction::DeclMapTy savedLocals;
89 CIRGenFunction::DeclMapTy &localDeclMap;
90
91 ParamReferenceReplacerRAII(CIRGenFunction::DeclMapTy &localDeclMap)
92 : localDeclMap(localDeclMap) {}
93
94 void addCopy(const DeclStmt *pm) {
95 // Figure out what param it refers to.
96
97 assert(pm->isSingleDecl());
98 const VarDecl *vd = static_cast<const VarDecl *>(pm->getSingleDecl());
99 const Expr *initExpr = vd->getInit();
100 GetParamRef visitor;
101 visitor.Visit(const_cast<Expr *>(initExpr));
102 assert(visitor.expr);
103 DeclRefExpr *dreOrig = visitor.expr;
104 auto *pd = dreOrig->getDecl();
105
106 auto it = localDeclMap.find(pd);
107 assert(it != localDeclMap.end() && "parameter is not found");
108 savedLocals.insert({pd, it->second});
109
110 auto copyIt = localDeclMap.find(vd);
111 assert(copyIt != localDeclMap.end() && "parameter copy is not found");
112 it->second = copyIt->getSecond();
113 }
114
115 ~ParamReferenceReplacerRAII() {
116 for (auto &&savedLocal : savedLocals) {
117 localDeclMap.insert({savedLocal.first, savedLocal.second});
118 }
119 }
120};
121} // namespace
122
124 if (curCoro.data && curCoro.data->coroBegin) {
125 return RValue::get(curCoro.data->coroBegin);
126 }
127 cgm.errorNYI("NYI");
128 return RValue();
129}
130
133 cir::CoroIdOp coroId,
134 CallExpr const *coroIdExpr = nullptr) {
135
136 if (curCoro.data) {
137 if (curCoro.data->coroIdExpr)
138 cgf.cgm.error(coroIdExpr->getBeginLoc(),
139 "only one __builtin_coro_id can be used in a function");
140 else if (coroIdExpr)
141 cgf.cgm.error(coroIdExpr->getBeginLoc(),
142 "__builtin_coro_id shall not be used in a C++ coroutine");
143 else
144 llvm_unreachable("EmitCoroutineBodyStatement called twice?");
145
146 return;
147 }
148
149 curCoro.data = std::make_unique<CGCoroData>();
150 curCoro.data->coroId = coroId;
151 curCoro.data->coroIdExpr = coroIdExpr;
152}
153
154static mlir::LogicalResult
156 Stmt *body,
157 const CIRGenFunction::LexicalScope *currLexScope) {
158 {
159 // Destroy the body's local variables, and the temporaries they extend, when
160 // the body ends, before the fall-through handler, the implicit
161 // `co_return;`, calls return_void(). The body stays in the current lexical
162 // scope so that a co_return at its top level is recorded in currLexScope.
164 if (cgf.emitStmt(body, /*useCurrentScope=*/true).failed())
165 return mlir::failure();
166 }
167
168 // Note that classic codegen checks CanFallthrough by looking into the
169 // availability of the insert block which is kinda brittle and unintuitive,
170 // seems to be related with how landing pads are handled.
171 //
172 // CIRGen handles this by checking pre-existing co_returns in the current
173 // scope instead.
174
175 // From LLVM IR Gen: const bool CanFallthrough = Builder.GetInsertBlock();
176 const bool canFallthrough = !currLexScope->hasCoreturn();
177 if (canFallthrough)
178 if (Stmt *onFallthrough = s.getFallthroughHandler())
179 if (cgf.emitStmt(onFallthrough, /*useCurrentScope=*/true).failed())
180 return mlir::failure();
181
182 return mlir::success();
183}
184
186 mlir::Location loc = getLoc(e->getBeginLoc());
187
189 for (const Expr *arg : e->arguments())
190 args.push_back(emitScalarExpr(arg));
191
192 auto coroId = cir::CoroIdOp::create(cgm.getBuilder(), loc, args);
193 createCoroData(*this, curCoro, coroId, e);
194 return coroId;
195}
196
198 mlir::Location loc = getLoc(e->getBeginLoc());
199 if (!curCoro.data || !curCoro.data->coroId) {
200 cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has"
201 " been used earlier in this function");
202 return {};
203 }
204
205 return cir::CoroAllocOp::create(
206 cgm.getBuilder(), loc,
207 mlir::ValueRange{curCoro.data->coroId.getResult()});
208}
209
211
212 mlir::Location loc = getLoc(e->getBeginLoc());
213 if (!curCoro.data || !curCoro.data->coroId) {
214 cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has"
215 " been used earlier in this function");
216 return {};
217 }
219 args.push_back(curCoro.data->coroId.getResult());
220 for (const Expr *arg : e->arguments())
221 args.push_back(emitScalarExpr(arg));
222
223 auto coroBegin = cir::CoroBeginOp::create(cgm.getBuilder(), loc, args);
224 curCoro.data->coroBegin = coroBegin;
225 return coroBegin;
226}
227
229
230 mlir::Location loc = getLoc(e->getBeginLoc());
231 CIRGenBuilderTy &builder = cgm.getBuilder();
233 for (const Expr *arg : e->arguments())
234 args.push_back(emitScalarExpr(arg));
235 args.push_back(cir::TokenNoneOp::create(builder, loc));
236 return cir::CoroEndOp::create(builder, loc, {cgm.voidTy}, args);
237}
238
240 mlir::Location loc = getLoc(e->getBeginLoc());
241
242 if (!curCoro.data || !curCoro.data->coroId) {
243 cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has"
244 " been used earlier in this function");
245 return {};
246 }
247
248 auto coroFree =
249 cir::CoroFreeOp::create(cgm.getBuilder(), loc,
250 mlir::ValueRange{curCoro.data->coroId.getResult(),
251 curCoro.data->coroBegin});
252
253 curCoro.data->lastCoroFree = coroFree;
254 return coroFree;
255}
256
258 mlir::Location loc = getLoc(e->getBeginLoc());
259 return cir::CoroSizeOp::create(cgm.getBuilder(), loc,
260 convertType(e->getType()));
261}
262
264 mlir::Location loc = getLoc(e->getBeginLoc());
265 return cir::CoroAlignOp::create(cgm.getBuilder(), loc,
266 convertType(e->getType()));
267}
268
269cir::CoroPromiseOp
271 mlir::Location loc = getLoc(e->getBeginLoc());
272
274 for (const Expr *arg : e->arguments())
275 args.push_back(emitScalarExpr(arg));
276
277 auto coroPromise = cir::CoroPromiseOp::create(cgm.getBuilder(), loc, args);
278 return coroPromise;
279}
280
282 mlir::Location loc = getLoc(e->getBeginLoc());
283 return cir::CoroDoneOp::create(cgm.getBuilder(), loc,
284 emitScalarExpr(e->getArg(0)));
285}
286
288 mlir::Location loc = getLoc(e->getBeginLoc());
289 return cir::CoroResumeOp::create(cgm.getBuilder(), loc,
290 emitScalarExpr(e->getArg(0)));
291}
292
293cir::CoroDestroyOp
295 mlir::Location loc = getLoc(e->getBeginLoc());
296 return cir::CoroDestroyOp::create(cgm.getBuilder(), loc,
297 emitScalarExpr(e->getArg(0)));
298}
299
301 mlir::Location loc = getLoc(e->getBeginLoc());
302 return cir::CoroNoopOp::create(cgm.getBuilder(), loc);
303}
304
305cir::CoroSuspendOp
307 mlir::Location loc = getLoc(e->getBeginLoc());
309 args.push_back(cir::TokenNoneOp::create(builder, loc));
310 args.push_back(emitScalarExpr(e->getArg(0)));
311
312 return cir::CoroSuspendOp::create(cgm.getBuilder(), loc, args);
313}
314
315static mlir::LogicalResult
317
318 CXXCatchStmt catchStmt(s.getBeginLoc(), /*exDecl=*/nullptr,
319 cgf.curCoro.data->exceptionHandler);
320 auto *tryStmt = CXXTryStmt::Create(cgf.getContext(), s.getBeginLoc(),
321 s.getBody(), &catchStmt);
322 struct handlerEmitter final : CIRGenFunction::cxxTryBodyEmitter {
323 const CoroutineBodyStmt &s;
324
325 handlerEmitter(const CoroutineBodyStmt &s) : s(s) /*, scope(scope)*/ {}
326 mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
327 return emitBodyAndFallthrough(cgf, s, s.getBody(), cgf.curLexScope);
328 }
329 ~handlerEmitter() override = default;
330 } emitter{s};
331
332 mlir::LogicalResult res = cgf.emitCXXTryStmt(*tryStmt, emitter);
333
334 return res;
335}
336
337mlir::LogicalResult
339 mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
340 cir::ConstantOp nullPtrCst = builder.getNullPtr(voidPtrTy, openCurlyLoc);
341
342 auto fn = mlir::cast<cir::FuncOp>(curFn);
343 fn.setCoroutineKind(cir::CoroutineKind::Coroutine);
344 const TargetInfo &ti = cgm.getASTContext().getTargetInfo();
345 unsigned newAlign = ti.getNewAlign() / ti.getCharWidth();
346
347 cir::CoroIdOp coroId = cir::CoroIdOp::create(
348 cgm.getBuilder(), openCurlyLoc,
349 mlir::ValueRange{builder.getUInt32(newAlign, openCurlyLoc), nullPtrCst,
350 nullPtrCst, nullPtrCst});
351 createCoroData(*this, curCoro, coroId);
352
353 // Backend is allowed to elide memory allocations, to help it, emit
354 // auto mem = coro.alloc() ? 0 : ... allocation code ...;
355 cir::CoroAllocOp coroAlloc = cir::CoroAllocOp::create(
356 cgm.getBuilder(), openCurlyLoc,
357 mlir::ValueRange{curCoro.data->coroId.getResult()});
358
359 // Initialize address of coroutine frame to null
360 CanQualType astVoidPtrTy = cgm.getASTContext().VoidPtrTy;
361 mlir::Type allocaTy = convertTypeForMem(astVoidPtrTy);
362 Address coroFrame =
363 createTempAlloca(allocaTy, getContext().getTypeAlignInChars(astVoidPtrTy),
364 openCurlyLoc, "__coro_frame_addr",
365 /*ArraySize=*/nullptr);
366
367 mlir::Value storeAddr = coroFrame.getPointer();
368 builder.CIRBaseBuilderTy::createStore(openCurlyLoc, nullPtrCst, storeAddr);
369 mlir::LogicalResult res = mlir::success();
370 cir::IfOp::create(
371 builder, openCurlyLoc, coroAlloc.getResult(),
372 /*withElseRegion=*/false,
373 /*thenBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
374 mlir::Value allocatedPtr = emitScalarExpr(s.getAllocate());
375 builder.CIRBaseBuilderTy::createStore(loc, allocatedPtr, storeAddr);
376 // Handle allocation failure if 'ReturnStmtOnAllocFailure' was provided.
377 if (Stmt *retOnAllocFailure = s.getReturnStmtOnAllocFailure()) {
378 mlir::Value isPtrNull = builder.createPtrIsNull(allocatedPtr);
379 assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
380 cir::IfOp::create(builder, loc, isPtrNull, /*withElseRegion=*/false,
381 [&](mlir::OpBuilder &b, mlir::Location loc) {
382 res = emitStmt(retOnAllocFailure,
383 /*useCurrentScope=*/true);
384 cir::UnreachableOp::create(builder, loc);
385 });
386 }
387 cir::YieldOp::create(builder, loc);
388 });
389
390 if (res.failed())
391 return res;
392
393 curCoro.data->coroBegin = cir::CoroBeginOp::create(
394 cgm.getBuilder(), openCurlyLoc,
395 mlir::ValueRange{
396 curCoro.data->coroId.getResult(),
397 cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr)});
398
399 {
401 ParamReferenceReplacerRAII paramReplacer(localDeclMap);
402
403 // Create mapping between parameters and copy-params for coroutine
404 // function.
405 llvm::ArrayRef<const Stmt *> paramMoves = s.getParamMoves();
406 assert((paramMoves.size() == 0 || (paramMoves.size() == fnArgs.size())) &&
407 "ParamMoves and FnArgs should be the same size for coroutine "
408 "function");
409 // For zipping the arg map into debug info.
411
412 // Create parameter copies. We do it before creating a promise, since an
413 // evolution of coroutine TS may allow promise constructor to observe
414 // parameter copies.
416 for (auto *pm : paramMoves) {
417 if (emitStmt(pm, /*useCurrentScope=*/true).failed())
418 return mlir::failure();
419 paramReplacer.addCopy(cast<DeclStmt>(pm));
420 }
421
422 // Builds `initial_suspend`.
423 auto initialSuspendBuilder = [&]() -> mlir::LogicalResult {
424 if (emitStmt(s.getPromiseDeclStmt(), /*useCurrentScope=*/true).failed())
425 return mlir::failure();
426 // Update coro.id to refer to the promise. We could not do it earlier
427 // because the promise local variable was not emitted yet.
428 Address promiseAddr = getAddrOfLocalVar(s.getPromiseDecl());
429 {
430 mlir::OpBuilder::InsertionGuard guard(builder);
431 builder.setInsertionPoint(coroId);
432 mlir::Value promisePtr = builder.createBitcast(
433 promiseAddr.emitRawPointer(), builder.getVoidPtrTy());
434 coroId.getPromiseMutable().assign(promisePtr);
435 }
436 // returnValue should be valid as long as the coroutine's return type
437 // is not void. The assertion could help us to reduce the check later.
438 assert(returnValue.isValid() == (bool)s.getReturnStmt());
439 // Now we have the promise, initialize the GRO.
440 // We need to emit `get_return_object` first. According to:
441 // [dcl.fct.def.coroutine]p7
442 // The call to get_return_­object is sequenced before the call to
443 // initial_suspend and is invoked at most once.
444 //
445 // So we couldn't emit return value when we emit return statment,
446 // otherwise the call to get_return_object wouldn't be in front
447 // of initial_suspend.
448 if (returnValue.isValid())
449 emitAnyExprToMem(s.getReturnValue(), returnValue,
450 s.getReturnValue()->getType().getQualifiers(),
451 /*isInit*/ true);
452
453 curCoro.data->currentAwaitKind = cir::AwaitKind::Init;
454 curCoro.data->exceptionHandler = s.getExceptionHandler();
455
456 if (emitStmt(s.getInitSuspendStmt(), /*useCurrentScope=*/true).failed())
457 return mlir::failure();
458
459 cir::YieldOp::create(builder, openCurlyLoc);
460 return mlir::success();
461 };
462
463 // Builds `body`: the user-written coroutine code, including its
464 // implicit `try { ... } catch (...) { unhandled_exception(); }`.
465 auto bodyBuilder = [&]() -> mlir::LogicalResult {
466 curCoro.data->currentAwaitKind = cir::AwaitKind::User;
467
468 if (curCoro.data->exceptionHandler) {
469 // This bit of code is supposed to do:
470 //
471 // if (await-resume-didnt-throw-exception) {
472 // try {
473 // coroutine-body
474 // } catch (...) {
475 // unhandled_exception();
476 // }
477 // }
478 //
479 // IF resume couldn't have thrown an exception(await_resume is
480 // noexcept), we skip the 'if'.
481 //
482 // Note that we've reversed the condition of the 'if' from classic
483 // codegen so that we don't need an 'else' block.
484 if (curCoro.data->resumeEHVar.isValid()) {
485 mlir::Value shouldSkip = builder.createFlagLoad(
486 openCurlyLoc, curCoro.data->resumeEHVar.getPointer());
487 mlir::LogicalResult res = mlir::success();
488 cir::IfOp::create(builder, openCurlyLoc, shouldSkip,
489 /*withElseRegion=*/false,
490 [&](mlir::OpBuilder &b, mlir::Location loc) {
491 res = coroutineBodyExceptionHelper(*this, s);
492 builder.createYield(openCurlyLoc);
493 });
494
495 if (res.failed())
496 return mlir::failure();
497
498 } else if (coroutineBodyExceptionHelper(*this, s).failed()) {
499 return mlir::failure();
500 }
501 } else if (emitBodyAndFallthrough(*this, s, s.getBody(), curLexScope)
502 .failed()) {
503 return mlir::failure();
504 }
505
506 return mlir::success();
507 };
508
509 // Builds `final_suspend`: only emitted at all if the body can actually
510 // reach it (an explicit co_return, or falling off the end).
511 auto finalSuspendBuilder = [&]() -> mlir::LogicalResult {
512 // Note that LLVM checks CanFallthrough by looking into the availability
513 // of the insert block which is kinda brittle and unintuitive, seems to be
514 // related with how landing pads are handled.
515 //
516 // CIRGen handles this by checking pre-existing co_returns in the current
517 // scope instead.
518 //
519 // From LLVM IR Gen: const bool CanFallthrough = Builder.GetInsertBlock();
520 const bool canFallthrough = curLexScope->hasCoreturn();
521 const bool hasCoreturns = curCoro.data->coreturnCount > 0;
522 if (canFallthrough || hasCoreturns) {
523 curCoro.data->currentAwaitKind = cir::AwaitKind::Final;
524 if (emitStmt(s.getFinalSuspendStmt(), /*useCurrentScope=*/true)
525 .failed())
526 return mlir::failure();
527 }
528 cir::YieldOp::create(builder, openCurlyLoc);
529 return mlir::success();
530 };
531
532 // Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
533 auto destroyBuilder = [&]() -> mlir::LogicalResult {
534 Stmt *deallocate = s.getDeallocate();
535 if (emitStmt(deallocate, /*useCurrentScope=*/true).failed()) {
536 cgm.error(deallocate->getBeginLoc(),
537 "failed to emit coroutine deallocation expression");
538 return mlir::failure();
539 }
540
541 cir::CoroFreeOp coroFree = curCoro.data->lastCoroFree;
542
543 if (!coroFree) {
544 cgm.error(deallocate->getBeginLoc(),
545 "Deallocation expression does not refer to coro.free");
546 return mlir::failure();
547 }
548 {
549 mlir::OpBuilder::InsertionGuard guard(builder);
550 builder.setInsertionPointAfter(coroFree);
551 mlir::Value isPtrNotNull =
552 builder.createPtrIsNotNull(coroFree.getResult());
553
554 llvm::SmallVector<mlir::Operation *> opsToMove;
555 mlir::Block *block = builder.getInsertionBlock();
556 mlir::Block::iterator it(isPtrNotNull.getDefiningOp());
557
558 for (++it; it != block->end(); ++it)
559 opsToMove.push_back(&*it);
560
561 auto ifOp = cir::IfOp::create(
562 builder, getLoc(deallocate->getSourceRange()), isPtrNotNull,
563 /*withElseRegion*/ false,
564 [&](mlir::OpBuilder &builder, mlir::Location loc) {
565 cir::YieldOp::create(builder, loc);
566 });
567
568 mlir::Operation *yieldOp = ifOp.getThenRegion().back().getTerminator();
569 for (auto *op : opsToMove)
570 op->moveBefore(yieldOp);
571 }
572
573 cir::YieldOp::create(builder, openCurlyLoc);
574 return mlir::success();
575 };
576
577 // Builds `exit`: coro.end(/*unwind*/ false) followed by the actual return
578 // to the caller.
579
581 auto exitBuilder = [&]() {
582 cir::ConstantOp nullHandler =
583 builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc);
584 cir::ConstantOp noUnwind = builder.getBool(false, openCurlyLoc);
585 auto tkNone = cir::TokenNoneOp::create(builder, openCurlyLoc);
586 cir::CoroEndOp::create(builder, openCurlyLoc, nullHandler, noUnwind,
587 tkNone);
588
589 if (auto *ret = cast_or_null<ReturnStmt>(s.getReturnStmt())) {
590 // Since we already emitted the return value above, so we shouldn't
591 // emit it again here.
592 Expr *previousRetValue = ret->getRetValue();
593 ret->setRetValue(nullptr);
594 if (emitStmt(ret, /*useCurrentScope=*/true).failed())
595 return mlir::failure();
596 mlir::Block *block = builder.getInsertionBlock();
597 // emitReturnStmt() always creates a new insertion block after emitting
598 // the return. That block is unreachable in this case, so erase it.
599 block->erase();
600 // Set the return value back. The code generator, as the AST
601 // **Consumer**, shouldn't change the AST.
602 ret->setRetValue(previousRetValue);
603 } else {
604 cir::ReturnOp::create(builder, openCurlyLoc);
605 }
606
607 return mlir::success();
608 };
609
610 cir::CoroutineOp coro = cir::CoroutineOp::create(
611 builder, openCurlyLoc,
612 /*initialSuspendBuilder=*/
613 [&](mlir::OpBuilder &b, mlir::Location loc) {
614 if (initialSuspendBuilder().failed())
615 res = mlir::failure();
616 },
617 /*bodyBuilder=*/
618 [&](mlir::OpBuilder &b, mlir::Location loc) {
619 if (bodyBuilder().failed())
620 res = mlir::failure();
621 },
622 /*finalSuspendBuilder=*/
623 [&](mlir::OpBuilder &b, mlir::Location loc) {
624 if (finalSuspendBuilder().failed())
625 res = mlir::failure();
626 },
627 /*destroyBuilder=*/
628 [&](mlir::OpBuilder &b, mlir::Location loc) {
629 if (destroyBuilder().failed())
630 res = mlir::failure();
631 },
632 /*exitBuilder=*/
633 [&](mlir::OpBuilder &b, mlir::Location loc) {
634 if (exitBuilder().failed())
635 res = mlir::failure();
636 });
637
638 if (coro && !coro.getBody().empty()) {
639 mlir::Block &lastBlock = coro.getBody().back();
640 mlir::Block *prevBlock = lastBlock.getPrevNode();
641 if (lastBlock.empty() && lastBlock.hasNoPredecessors() && prevBlock &&
642 !prevBlock->empty() && isa<cir::CoReturnOp>(prevBlock->back())) {
643 lastBlock.erase();
644 } else if (!lastBlock.mightHaveTerminator()) {
645 mlir::OpBuilder::InsertionGuard guard(builder);
646 builder.setInsertionPointToEnd(&lastBlock);
647 cir::YieldOp::create(builder, openCurlyLoc);
648 }
649 }
650 }
651
652 return res;
653}
654
655static bool functionCanThrow(const FunctionDecl *d) {
656 const auto *proto = d->getType()->getAs<FunctionProtoType>();
657 if (!proto)
658 return true;
659 return !isNoexceptExceptionSpec(proto->getExceptionSpecType()) ||
660 proto->canThrow() != CT_Cannot;
661}
662
663static bool stmtCanThrow(const Stmt *s) {
664 if (const auto *ce = dyn_cast<CallExpr>(s)) {
665 const FunctionDecl *callee = ce->getDirectCallee();
666 if (!callee || functionCanThrow(callee))
667 return true;
668 }
669
670 // The destructor call of a bound temporary is not among the children.
671 if (const auto *te = dyn_cast<CXXBindTemporaryExpr>(s))
672 if (functionCanThrow(te->getTemporary()->getDestructor()))
673 return true;
674
675 return llvm::any_of(s->children(), [](const Stmt *child) {
676 return child && stmtCanThrow(child);
677 });
678}
679
680// Given a suspend expression which roughly looks like:
681//
682// auto && x = CommonExpr();
683// if (!x.await_ready()) {
684// x.await_suspend(...); (*)
685// }
686// x.await_resume();
687//
688// where the result of the entire expression is the result of x.await_resume()
689//
690// (*) If x.await_suspend return type is bool, it allows to veto a suspend:
691// if (x.await_suspend(...))
692// llvm_coro_suspend();
693//
694// This is more higher level than LLVM codegen, for that one see llvm's
695// docs/Coroutines.md for more details.
696namespace {
697struct LValueOrRValue {
698 LValue lv;
699 RValue rv;
700};
701} // namespace
702
703static bool isResumeExprInTryCatch(const CGCoroData &coro, cir::AwaitKind kind,
704 const CoroutineSuspendExpr &s) {
705 return coro.exceptionHandler && kind == cir::AwaitKind::Init &&
707}
708
709static LValueOrRValue
711 CoroutineSuspendExpr const &s, cir::AwaitKind kind,
712 AggValueSlot aggSlot, bool ignoreResult,
713 mlir::Block *scopeParentBlock,
714 mlir::Value &tmpResumeRValAddr, bool forLValue) {
715 [[maybe_unused]] mlir::LogicalResult awaitBuild = mlir::success();
716 LValueOrRValue awaitRes;
717
720 CIRGenBuilderTy &builder = cgf.getBuilder();
721
722 // Exception handling requires additional IR. We avoid generating it when
723 // neither the await_resume() call nor the destruction of its result can
724 // throw.
725 const bool resumeInTry = isResumeExprInTryCatch(coro, kind, s);
726
727 // Resume-expression temporaries live until the enclosing full-expression
728 // ends. Defer their cleanups and guard them with construction flags, since
729 // destroying the coroutine while suspended skips their construction.
730 // The initial-suspend try/catch cleans up its temporaries in the try block.
731 std::optional<CIRGenFunction::ConditionalEvaluation> resumeEval;
732 const bool resumeDefersCleanups =
734 if (resumeDefersCleanups && cgf.currentFullExprCleanupScope)
735 resumeEval.emplace(cgf, cgf.getLoc(s.getSourceRange()));
736 else if (resumeDefersCleanups)
738 "resume expression cleanups without a full-expression "
739 "cleanup scope");
740 [[maybe_unused]] EHScopeStack::stable_iterator ehDepth =
741 cgf.ehStack.stable_begin();
742
743 // If the await_resume() result needs a destructor, take over its
744 // destruction, unless the destination owns it. The try/catch path destroys
745 // the result itself.
746 const CXXBindTemporaryExpr *resultBind = nullptr;
747 if (!resumeInTry && !aggSlot.isExternallyDestructed())
748 resultBind = dyn_cast<CXXBindTemporaryExpr>(s.getResumeExpr());
749 if (resultBind) {
750 // Emit the result into a slot created outside of the cir.await, so that it
751 // is still available after it.
752 if (aggSlot.isIgnored())
753 aggSlot = cgf.createAggTemp(resultBind->getType(),
754 cgf.getLoc(resultBind->getSourceRange()),
755 "agg.tmp.ensured");
756 // Don't push the destructor from within the resume region, where its
757 // cir.cleanup.scope would capture the region's terminator.
758 aggSlot.setExternallyDestructed();
759 }
760
761 [[maybe_unused]] cir::AwaitOp awaitOp = cir::AwaitOp::create(
762 builder, cgf.getLoc(s.getSourceRange()), kind,
763 /*readyBuilder=*/
764 [&](mlir::OpBuilder &b, mlir::Location loc) {
765 Expr *condExpr = s.getReadyExpr()->IgnoreParens();
766 builder.createCondition(cgf.evaluateExprAsBool(condExpr));
767 },
768 /*suspendBuilder=*/
769 [&](mlir::OpBuilder &b, mlir::Location loc) {
770 // Note that differently from LLVM codegen we do not emit coro.save
771 // and coro.suspend here, that should be done as part of lowering this
772 // to LLVM dialect (or some other MLIR dialect)
773
774 switch (s.getSuspendReturnType()) {
775 case CoroutineSuspendExpr::SuspendReturnType::SuspendBool: {
776 mlir::Value suspendRet = cgf.evaluateExprAsBool(s.getSuspendExpr());
777 // Veto suspension if requested by bool returning await_suspend.
778 cir::CoroSuspendPoint::create(builder, loc, suspendRet);
779 break;
780 }
781 case CoroutineSuspendExpr::SuspendReturnType::SuspendVoid:
782 cgf.emitScalarExpr(s.getSuspendExpr());
783 // Signals the parent that execution flows to next region.
784 cir::CoroSuspendPoint::create(builder, loc);
785 break;
786 case CoroutineSuspendExpr::SuspendReturnType::SuspendHandle:
787 cgf.cgm.errorNYI(s.getSourceRange(),
788 "await_suspend returning handle");
789 cir::CoroSuspendPoint::create(builder, loc);
790 break;
791 }
792 },
793 /*resumeBuilder=*/
794 [&](mlir::OpBuilder &b, mlir::Location loc) {
795 if (resumeEval)
796 resumeEval->beginEvaluation();
797 if (resumeInTry) {
798 // we are basically just emitting:
799 // resumeEh = false;
800 // try {
801 // resumeExpr();
802 // resumeEh = true;
803 // } catch(...) {
804 // exceptionHandler();
805 // }
806 // Note the values of resumeEh are reversed from classic codegen,
807 // simply so we can use an 'IfOp' without a 'else' later.
808 ASTContext &ctx = cgf.getContext();
809 SourceLocation resumeLoc = s.getResumeExpr()->getExprLoc();
810 mlir::Location mlirLoc = cgf.getLoc(resumeLoc);
811 coro.resumeEHVar = cgf.createTempAlloca(
812 builder.getBoolTy(), ctx.getTypeAlignInChars(ctx.BoolTy), mlirLoc,
813 "resume.eh");
814 builder.createFlagStore(mlirLoc, false,
815 coro.resumeEHVar.getPointer());
816
817 CXXCatchStmt catchStmt(resumeLoc,
818 /*exDecl=*/nullptr, coro.exceptionHandler);
819 auto *tryBody =
820 CompoundStmt::Create(ctx, s.getResumeExpr(), FPOptionsOverride(),
821 resumeLoc, resumeLoc);
822 CXXTryStmt *tryStmt =
823 CXXTryStmt::Create(ctx, resumeLoc, tryBody, &catchStmt);
824
825 struct resumeEmitter final : CIRGenFunction::cxxTryBodyEmitter {
826 const CXXTryStmt &tryStmt;
827 const Expr *resumeExpr;
828 mlir::Location loc;
829 mlir::Value resumeEHVar;
830 resumeEmitter(const CXXTryStmt &tryStmt, const Expr *resumeExpr,
831 mlir::Location loc, Address resumeEHVar)
832 : tryStmt(tryStmt), resumeExpr(resumeExpr), loc(loc),
833 resumeEHVar(resumeEHVar.getPointer()) {}
834
835 mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
836 // Destroy resume temporaries in the try block before marking
837 // resumption successful, so destructor exceptions are caught too.
838 CIRGenFunction::FullExprCleanupScope fullExpr(cgf, resumeExpr);
839 mlir::LogicalResult res =
840 cgf.emitStmt(tryStmt.getTryBlock(), /*useCurrentScope=*/true);
841 fullExpr.exit();
842 cgf.getBuilder().createFlagStore(loc, true, resumeEHVar);
843 return res;
844 }
845
846 ~resumeEmitter() override = default;
847 } emitter{*tryStmt, s.getResumeExpr(), mlirLoc, coro.resumeEHVar};
848
849 awaitBuild = cgf.emitCXXTryStmt(*tryStmt, emitter);
850 // We are not supposed to obtain the value from init suspend
851 // await_resume().
852 awaitRes.rv = RValue::getIgnored();
853 } else if (forLValue) {
854 awaitRes.lv = cgf.emitLValue(s.getResumeExpr());
855 assert(awaitRes.lv.isSimple() &&
856 "expected simple lvalue for coroutine resume");
857 mlir::Value ptr = awaitRes.lv.getPointer();
858 llvm::StringRef tmpName = kind == cir::AwaitKind::Yield
859 ? "__coyield_resume_lval"
860 : "__coawait_resume_lval";
861 tmpResumeRValAddr = cgf.emitAlloca(
862 tmpName, ptr.getType(), loc, cgf.getPointerAlign(),
863 builder.getBestAllocaInsertPoint(scopeParentBlock));
864 builder.CIRBaseBuilderTy::createStore(loc, ptr, tmpResumeRValAddr);
865 } else {
866 awaitRes.rv =
867 cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult);
868 if (!awaitRes.rv.isIgnored()) {
869 RValue rv = awaitRes.rv;
870 if (rv.isScalar() || rv.isComplex()) {
871 mlir::Value value =
872 rv.isScalar() ? rv.getValue() : rv.getComplexValue();
873 tmpResumeRValAddr = cgf.emitAlloca(
874 "__coawait_resume_rval", value.getType(), loc,
876 builder.getBestAllocaInsertPoint(scopeParentBlock));
877 // Store the rvalue so we can reload it before the promise call.
878 builder.CIRBaseBuilderTy::createStore(loc, value,
879 tmpResumeRValAddr);
880 } else {
881 assert(rv.isAggregate() && "unexpected rvalue kind");
882 }
883 }
884 }
885
886 if (resumeEval)
887 resumeEval->endEvaluation();
888
889 // Returns control back to parent.
890 cir::YieldOp::create(builder, loc);
891 });
892
893 assert((cgf.ehStack.stable_begin() == ehDepth ||
894 (resumeDefersCleanups && !resumeEval)) &&
895 "a resume expression cleanup was pushed in the resume region");
896
897 // Push the destructor right after the cir.await, where the result starts to
898 // exist, and not around it: destroying the coroutine at the suspend point
899 // would destroy the not yet constructed result. The cleanup runs at the end
900 // of the full-expression.
901 if (resultBind)
902 cgf.emitCXXTemporary(resultBind->getTemporary(), resultBind->getType(),
903 aggSlot.getAddress());
904
905 assert(awaitBuild.succeeded() && "Should know how to codegen");
906 return awaitRes;
907}
908
910 const CoroutineSuspendExpr &e,
911 cir::AwaitKind kind, AggValueSlot aggSlot,
912 bool ignoreResult) {
913 RValue rval;
914 mlir::Location scopeLoc = cgf.getLoc(e.getSourceRange());
915
916 // Since we model suspend / resume as an inner region, we must store
917 // resume scalar results in a tmp alloca, and load it after we build the
918 // suspend expression. An alternative way to do this would be to make
919 // every region return a value when promise.return_value() is used, but
920 // it's a bit awkward given that resume is the only region that actually
921 // returns a value.
922 mlir::Block *currEntryBlock = cgf.curLexScope->getEntryBlock();
923 [[maybe_unused]] mlir::Value tmpResumeRValAddr;
924
925 // No need to explicitly wrap this into a scope since the AST already uses a
926 // ExprWithCleanups, which will wrap this into a cir.scope anyways.
927 rval = emitSuspendExpression(cgf, *cgf.curCoro.data, e, kind, aggSlot,
928 ignoreResult, currEntryBlock, tmpResumeRValAddr,
929 /*forLValue*/ false)
930 .rv;
931
932 if (ignoreResult || rval.isIgnored())
933 return rval;
934
935 if (rval.isScalar()) {
936 rval = RValue::get(cir::LoadOp::create(cgf.getBuilder(), scopeLoc,
937 rval.getValue().getType(),
938 tmpResumeRValAddr));
939 } else if (rval.isAggregate()) {
940 return rval;
941 } else { // complex
942 rval = RValue::getComplex(cir::LoadOp::create(
943 cgf.getBuilder(), scopeLoc, rval.getComplexValue().getType(),
944 tmpResumeRValAddr));
945 }
946 return rval;
947}
948
950 AggValueSlot aggSlot,
951 bool ignoreResult) {
952 return emitSuspendExpr(*this, e, curCoro.data->currentAwaitKind, aggSlot,
953 ignoreResult);
954}
955
957 AggValueSlot aggSlot,
958 bool ignoreResult) {
959 return emitSuspendExpr(*this, e, cir::AwaitKind::Yield, aggSlot,
960 ignoreResult);
961}
962
963static QualType
965 const CoroutineSuspendExpr *e) {
966 const auto *re = e->getResumeExpr();
967 assert(isa<CallExpr>(re) && "unexpected suspend expression type");
968 return cast<CallExpr>(re)->getCallReturnType(ctx);
969}
970
972 const CoroutineSuspendExpr *e,
973 cir::AwaitKind kind) {
975 assert(retTy->isReferenceType() &&
976 "Can't have a scalar return unless the return type is a "
977 "reference type!");
978 mlir::Location scopeLoc = cgf.getLoc(e->getSourceRange());
979 mlir::Block *currEntryBlock = cgf.curLexScope->getEntryBlock();
980 mlir::Value tmpResumeLValAddr;
981 LValue lv = emitSuspendExpression(cgf, *cgf.curCoro.data, *e, kind,
983 /*ignoreResult=*/false, currEntryBlock,
984 tmpResumeLValAddr, /*forLValue=*/true)
985 .lv;
986 if (!tmpResumeLValAddr) {
987 assert(isResumeExprInTryCatch(*cgf.curCoro.data, kind, *e) &&
988 "expected resume in try/catch");
989 QualType pointeeTy = retTy->getPointeeType();
990 mlir::Type elemTy = cgf.convertType(pointeeTy);
991 mlir::Value nullPtr = cgf.getBuilder().getNullPtr(
992 cgf.getBuilder().getPointerTo(elemTy), scopeLoc);
993 return cgf.makeNaturalAlignAddrLValue(nullPtr, pointeeTy);
994 }
995
996 Address addr = lv.getAddress();
997 mlir::Value loadedPtr =
998 cir::LoadOp::create(cgf.getBuilder(), scopeLoc,
999 addr.getPointer().getType(), tmpResumeLValAddr);
1000 lv.setAddress(addr.withPointer(loadedPtr));
1001 return lv;
1002}
1003
1005 return emitSuspendLValue(*this, e, curCoro.data->currentAwaitKind);
1006}
1007
1009 return emitSuspendLValue(*this, e, cir::AwaitKind::Yield);
1010}
1011
1012mlir::LogicalResult CIRGenFunction::emitCoreturnStmt(CoreturnStmt const &s) {
1013 ++curCoro.data->coreturnCount;
1014
1015 const Expr *rv = s.getOperand();
1016 if (rv && rv->getType()->isVoidType() && !isa<InitListExpr>(rv)) {
1017 // Make sure to evaluate the non initlist expression of a co_return
1018 // with a void expression for side effects.
1019 RunCleanupsScope cleanupScope(*this);
1020 emitIgnoredExpr(rv);
1021 }
1022
1023 if (emitStmt(s.getPromiseCall(), /*useCurrentScope=*/true).failed())
1024 return mlir::failure();
1025 mlir::Location loc = getLoc(s.getSourceRange());
1026 cir::CoReturnOp::create(builder, loc);
1027 curLexScope->markCoreturnEmitted();
1028
1029 // Statements after a co_return may stil be reachable through a label.
1030 // Continue codegen.
1031 builder.createBlock(builder.getBlock()->getParent());
1032 return mlir::success();
1033}
static void createCoroData(CIRGenFunction &cgf, CIRGenFunction::CGCoroInfo &curCoro, cir::CoroIdOp coroId, CallExpr const *coroIdExpr=nullptr)
static QualType getCoroutineSuspendExprReturnType(const ASTContext &ctx, const CoroutineSuspendExpr *e)
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 LValue emitSuspendLValue(CIRGenFunction &cgf, const CoroutineSuspendExpr *e, cir::AwaitKind kind)
static bool stmtCanThrow(const Stmt *s)
static RValue emitSuspendExpr(CIRGenFunction &cgf, const CoroutineSuspendExpr &e, cir::AwaitKind kind, AggValueSlot aggSlot, bool ignoreResult)
static bool functionCanThrow(const FunctionDecl *d)
static bool isResumeExprInTryCatch(const CGCoroData &coro, cir::AwaitKind kind, const CoroutineSuspendExpr &s)
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)
cir::PointerType getPointerTo(mlir::Type ty)
cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc)
mlir::Value createBitcast(mlir::Value src, mlir::Type newTy)
cir::PointerType getVoidPtrTy(clang::LangAS langAS=clang::LangAS::Default)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:245
CharUnits getTypeAlignInChars(QualType T) const
Return the ABI-specified alignment of a (complete) type T, in characters.
CanQualType BoolTy
Address withPointer(mlir::Value newPtr) const
Return address with different pointer, but same element type and alignment.
Definition Address.h:83
mlir::Value getPointer() const
Definition Address.h:98
static Address invalid()
Definition Address.h:76
bool isValid() const
Definition Address.h:77
mlir::Value emitRawPointer() const
Return the pointer contained in this class after authenticating it and adding offset to it if necessa...
Definition Address.h:112
An aggregate value slot.
IsDestructed_t isExternallyDestructed() const
void setExternallyDestructed(bool destructed=true)
static AggValueSlot ignored()
Returns an aggregate value slot indicating that the aggregate value is being ignored.
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::Type convertType(clang::QualType t)
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.
cir::CoroNoopOp emitCoroNoopBuiltinCall(const CallExpr *e)
Address getAddrOfLocalVar(const clang::VarDecl *vd)
Return the address of a local variable.
cir::CoroSuspendOp emitCoroSuspendBuiltinCall(const CallExpr *e)
LValue makeNaturalAlignAddrLValue(mlir::Value val, QualType ty)
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.
static bool resumeExprDefersCleanups(const CoroutineSuspendExpr &s)
Whether the resume expression of s contains a construct that defers cleanups to the end of the enclos...
RValue emitCoyieldExpr(const CoyieldExpr &e, AggValueSlot aggSlot=AggValueSlot::ignored(), bool ignoreResult=false)
cir::CoroFreeOp emitCoroFreeBuiltin(const CallExpr *e)
LValue emitCoawaitLValue(const CoawaitExpr *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.
AggValueSlot createAggTemp(QualType ty, mlir::Location loc, const Twine &name="tmp", Address *alloca=nullptr)
Create a temporary memory object for the given aggregate type.
mlir::Type convertTypeForMem(QualType t)
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, mlir::Location loc, clang::CharUnits alignment, bool insertIntoFnEntryBlock, mlir::Value arraySize=nullptr)
mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s, cxxTryBodyEmitter &bodyCallback, bool isFnTryBlock=false)
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)
void emitCXXTemporary(const CXXTemporary *temporary, QualType tempType, Address ptr)
Emits all the code to cause the given temporary to be cleaned up.
LValue emitCoyieldLValue(const CoyieldExpr *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::CleanupScopeOp currentFullExprCleanupScope
The cir.cleanup.scope of the innermost FullExprCleanupScope that materialized one,...
cir::CoroDoneOp emitCoroDoneBuiltinCall(const CallExpr *e)
cir::CoroResumeOp emitCoroResumeBuiltinCall(const CallExpr *e)
cir::CoroAlignOp emitCoroAlignBuiltinCall(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.
A saved depth on the scope stack.
stable_iterator stable_begin() const
Create a stable reference to the top of the EH stack.
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
Represents binding an expression to a temporary.
Definition ExprCXX.h:1498
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:3012
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3216
SourceLocation getBeginLoc() const
Definition Expr.h:3346
arg_range arguments()
Definition Expr.h:3264
static CharUnits One()
Construct a CharUnits quantity of one.
Definition CharUnits.h:55
Represents a 'co_await' expression.
Definition ExprCXX.h:5423
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:5309
Expr * getResumeExpr() const
Definition ExprCXX.h:5373
OpaqueValueExpr * getOpaqueValue() const
getOpaqueValue - Return the opaque value placeholder.
Definition ExprCXX.h:5363
Represents a 'co_yield' expression.
Definition ExprCXX.h:5504
A reference to a declared variable, function, enum, etc.
Definition Expr.h:1315
ValueDecl * getDecl()
Definition Expr.h:1383
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition Stmt.h:1655
const Decl * getSingleDecl() const
Definition Stmt.h:1657
This represents one expression.
Definition Expr.h:114
QualType getType() const
Definition Expr.h:146
Represents a function declaration or definition.
Definition Decl.h:2071
Represents a prototype with parameter type info, e.g.
Definition TypeBase.h:5548
A (possibly-)qualified type.
Definition TypeBase.h:938
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
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:355
Exposes information about the current target.
Definition TargetInfo.h:228
unsigned getNewAlign() const
Return the largest alignment for which a suitably-sized allocation with 'operator new(size_t)' is gua...
Definition TargetInfo.h:762
unsigned getCharWidth() const
Definition TargetInfo.h:528
bool isVoidType() const
Definition TypeBase.h:9200
bool isReferenceType() const
Definition TypeBase.h:8839
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:885
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9431
QualType getType() const
Definition Decl.h:725
const Expr * getInit() const
Definition Decl.h:1393
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 coroOutsideFrameMD()
static bool coroGetRetObjManager()
static bool generateDebugInfo()
std::unique_ptr< CGCoroData > data
Represents a scope, including function bodies, compound statements, and the substatements of if/while...
clang::CharUnits getPointerAlign() const
cir::PointerType voidPtrTy
void* in address space 0