clang 23.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::CallOp 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::CallOp 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
54// Defining these here allows to keep CGCoroData private to this file.
57
58namespace {
59// FIXME: both GetParamRef and ParamReferenceReplacerRAII are good template
60// candidates to be shared among LLVM / CIR codegen.
61
62// Hunts for the parameter reference in the parameter copy/move declaration.
63struct GetParamRef : public StmtVisitor<GetParamRef> {
64public:
65 DeclRefExpr *expr = nullptr;
66 GetParamRef() {}
67 void VisitDeclRefExpr(DeclRefExpr *e) {
68 assert(expr == nullptr && "multilple declref in param move");
69 expr = e;
70 }
71 void VisitStmt(Stmt *s) {
72 for (Stmt *c : s->children()) {
73 if (c)
74 Visit(c);
75 }
76 }
77};
78
79// This class replaces references to parameters to their copies by changing
80// the addresses in CGF.LocalDeclMap and restoring back the original values in
81// its destructor.
82struct ParamReferenceReplacerRAII {
83 CIRGenFunction::DeclMapTy savedLocals;
84 CIRGenFunction::DeclMapTy &localDeclMap;
85
86 ParamReferenceReplacerRAII(CIRGenFunction::DeclMapTy &localDeclMap)
87 : localDeclMap(localDeclMap) {}
88
89 void addCopy(const DeclStmt *pm) {
90 // Figure out what param it refers to.
91
92 assert(pm->isSingleDecl());
93 const VarDecl *vd = static_cast<const VarDecl *>(pm->getSingleDecl());
94 const Expr *initExpr = vd->getInit();
95 GetParamRef visitor;
96 visitor.Visit(const_cast<Expr *>(initExpr));
97 assert(visitor.expr);
98 DeclRefExpr *dreOrig = visitor.expr;
99 auto *pd = dreOrig->getDecl();
100
101 auto it = localDeclMap.find(pd);
102 assert(it != localDeclMap.end() && "parameter is not found");
103 savedLocals.insert({pd, it->second});
104
105 auto copyIt = localDeclMap.find(vd);
106 assert(copyIt != localDeclMap.end() && "parameter copy is not found");
107 it->second = copyIt->getSecond();
108 }
109
110 ~ParamReferenceReplacerRAII() {
111 for (auto &&savedLocal : savedLocals) {
112 localDeclMap.insert({savedLocal.first, savedLocal.second});
113 }
114 }
115};
116} // namespace
117
118namespace {
119// Make sure to call coro.delete on scope exit.
120struct CallCoroDelete final : public EHScopeStack::Cleanup {
121 Stmt *deallocate;
122
123 // Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
124
125 // Note: That deallocation will be emitted twice: once for a normal exit and
126 // once for exceptional exit. This usage is safe because Deallocate does not
127 // contain any declarations. The SubStmtBuilder::makeNewAndDeleteExpr()
128 // builds a single call to a deallocation function which is safe to emit
129 // multiple times.
130 void emit(CIRGenFunction &cgf, Flags) override {
131 // Remember the current point, as we are going to emit deallocation code
132 // first to get to coro.free instruction that is an argument to a delete
133 // call.
134
135 if (cgf.emitStmt(deallocate, /*useCurrentScope=*/true).failed()) {
136 cgf.cgm.error(deallocate->getBeginLoc(),
137 "failed to emit coroutine deallocation expression");
138 return;
139 }
140
141 CIRGenBuilderTy &builder = cgf.getBuilder();
142 cir::CallOp coroFree = cgf.curCoro.data->lastCoroFree;
143
144 if (!coroFree) {
145 cgf.cgm.error(deallocate->getBeginLoc(),
146 "Deallocation expression does not refer to coro.free");
147 return;
148 }
149
150 builder.setInsertionPointAfter(coroFree);
151 mlir::Value isPtrNotNull = builder.createPtrIsNotNull(coroFree.getResult());
152
153 llvm::SmallVector<mlir::Operation *> opsToMove;
154 mlir::Block *block = builder.getInsertionBlock();
155 mlir::Block::iterator it(isPtrNotNull.getDefiningOp());
156
157 for (++it; it != block->end(); ++it)
158 opsToMove.push_back(&*it);
159
160 auto ifOp =
161 cir::IfOp::create(builder, cgf.getLoc(deallocate->getSourceRange()),
162 isPtrNotNull, /*withElseRegion*/ false,
163 [&](mlir::OpBuilder &builder, mlir::Location loc) {
164 cir::YieldOp::create(builder, loc);
165 });
166
167 mlir::Operation *yieldOp = ifOp.getThenRegion().back().getTerminator();
168 for (auto *op : opsToMove)
169 op->moveBefore(yieldOp);
170 }
171 explicit CallCoroDelete(Stmt *deallocStmt) : deallocate(deallocStmt) {}
172};
173} // namespace
174
176 if (curCoro.data && curCoro.data->coroBegin) {
177 return RValue::get(curCoro.data->coroBegin);
178 }
179 cgm.errorNYI("NYI");
180 return RValue();
181}
182
185 cir::CallOp coroId) {
186 assert(!curCoro.data && "EmitCoroutineBodyStatement called twice?");
187
188 curCoro.data = std::make_unique<CGCoroData>();
189 curCoro.data->coroId = coroId;
190}
191
192static mlir::LogicalResult
194 Stmt *body,
195 const CIRGenFunction::LexicalScope *currLexScope) {
196 if (cgf.emitStmt(body, /*useCurrentScope=*/true).failed())
197 return mlir::failure();
198 // Note that classic codegen checks CanFallthrough by looking into the
199 // availability of the insert block which is kinda brittle and unintuitive,
200 // seems to be related with how landing pads are handled.
201 //
202 // CIRGen handles this by checking pre-existing co_returns in the current
203 // scope instead.
204
205 // From LLVM IR Gen: const bool CanFallthrough = Builder.GetInsertBlock();
206 const bool canFallthrough = !currLexScope->hasCoreturn();
207 if (canFallthrough)
208 if (Stmt *onFallthrough = s.getFallthroughHandler())
209 if (cgf.emitStmt(onFallthrough, /*useCurrentScope=*/true).failed())
210 return mlir::failure();
211
212 return mlir::success();
213}
214
215cir::CallOp CIRGenFunction::emitCoroIDBuiltinCall(mlir::Location loc,
216 mlir::Value nullPtr) {
217 cir::IntType int32Ty = builder.getUInt32Ty();
218
219 const TargetInfo &ti = cgm.getASTContext().getTargetInfo();
220 unsigned newAlign = ti.getNewAlign() / ti.getCharWidth();
221
222 mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroId);
223
224 cir::FuncOp fnOp;
225 if (!builtin) {
226 fnOp = cgm.createCIRBuiltinFunction(
227 loc, cgm.builtinCoroId,
228 cir::FuncType::get({int32Ty, voidPtrTy, voidPtrTy, voidPtrTy}, int32Ty),
229 /*FD=*/nullptr);
230 assert(fnOp && "should always succeed");
231 } else {
232 fnOp = cast<cir::FuncOp>(builtin);
233 }
234
235 return builder.createCallOp(loc, fnOp,
236 mlir::ValueRange{builder.getUInt32(newAlign, loc),
237 nullPtr, nullPtr, nullPtr});
238}
239
240cir::CallOp CIRGenFunction::emitCoroAllocBuiltinCall(mlir::Location loc) {
241 cir::BoolType boolTy = builder.getBoolTy();
242
243 mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroAlloc);
244
245 cir::FuncOp fnOp;
246 if (!builtin) {
247 fnOp = cgm.createCIRBuiltinFunction(loc, cgm.builtinCoroAlloc,
248 cir::FuncType::get({uInt32Ty}, boolTy),
249 /*fd=*/nullptr);
250 assert(fnOp && "should always succeed");
251 } else {
252 fnOp = cast<cir::FuncOp>(builtin);
253 }
254
255 return builder.createCallOp(
256 loc, fnOp, mlir::ValueRange{curCoro.data->coroId.getResult()});
257}
258
259cir::CallOp
261 mlir::Value coroframeAddr) {
262 mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroBegin);
263
264 cir::FuncOp fnOp;
265 if (!builtin) {
266 fnOp = cgm.createCIRBuiltinFunction(
267 loc, cgm.builtinCoroBegin,
268 cir::FuncType::get({uInt32Ty, voidPtrTy}, voidPtrTy),
269 /*fd=*/nullptr);
270 assert(fnOp && "should always succeed");
271 } else {
272 fnOp = cast<cir::FuncOp>(builtin);
273 }
274
275 return builder.createCallOp(
276 loc, fnOp,
277 mlir::ValueRange{curCoro.data->coroId.getResult(), coroframeAddr});
278}
279
280cir::CallOp CIRGenFunction::emitCoroEndBuiltinCall(mlir::Location loc,
281 mlir::Value nullPtr) {
282 cir::BoolType boolTy = builder.getBoolTy();
283 mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroEnd);
284
285 cir::FuncOp fnOp;
286 if (!builtin) {
287 fnOp = cgm.createCIRBuiltinFunction(
288 loc, cgm.builtinCoroEnd,
289 cir::FuncType::get({voidPtrTy, boolTy}, boolTy),
290 /*fd=*/nullptr);
291 assert(fnOp && "should always succeed");
292 } else {
293 fnOp = cast<cir::FuncOp>(builtin);
294 }
295
296 return builder.createCallOp(
297 loc, fnOp, mlir::ValueRange{nullPtr, builder.getBool(false, loc)});
298}
299
301 mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroFree);
302 mlir::Location loc = getLoc(e->getBeginLoc());
303 cir::FuncOp fnOp;
304 if (!builtin) {
305 fnOp = cgm.createCIRBuiltinFunction(
306 loc, cgm.builtinCoroFree,
307 cir::FuncType::get({uInt32Ty, voidPtrTy}, voidPtrTy),
308 /*fd=*/nullptr);
309 assert(fnOp && "should always succeed");
310 } else {
311 fnOp = cast<cir::FuncOp>(builtin);
312 }
313 cir::CallOp coroFree =
314 builder.createCallOp(loc, fnOp,
315 mlir::ValueRange{curCoro.data->coroId.getResult(),
316 curCoro.data->coroBegin});
317
318 curCoro.data->lastCoroFree = coroFree;
319 return coroFree;
320}
321
322static mlir::LogicalResult
324
325 CXXCatchStmt catchStmt(s.getBeginLoc(), /*exDecl=*/nullptr,
326 cgf.curCoro.data->exceptionHandler);
327 auto *tryStmt = CXXTryStmt::Create(cgf.getContext(), s.getBeginLoc(),
328 s.getBody(), &catchStmt);
329 struct handlerEmitter final : CIRGenFunction::cxxTryBodyEmitter {
330 const CoroutineBodyStmt &s;
331
332 handlerEmitter(const CoroutineBodyStmt &s) : s(s) /*, scope(scope)*/ {}
333 mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
334 return emitBodyAndFallthrough(cgf, s, s.getBody(), cgf.curLexScope);
335 }
336 ~handlerEmitter() override = default;
337 } emitter{s};
338
339 mlir::LogicalResult res = cgf.emitCXXTryStmt(*tryStmt, emitter);
340
341 return res;
342}
343
344mlir::LogicalResult
346 mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
347 cir::ConstantOp nullPtrCst = builder.getNullPtr(voidPtrTy, openCurlyLoc);
348
349 auto fn = mlir::cast<cir::FuncOp>(curFn);
350 fn.setCoroutine(true);
351 cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);
352 createCoroData(*this, curCoro, coroId);
353
354 // Backend is allowed to elide memory allocations, to help it, emit
355 // auto mem = coro.alloc() ? 0 : ... allocation code ...;
356 cir::CallOp coroAlloc = emitCoroAllocBuiltinCall(openCurlyLoc);
357
358 // Initialize address of coroutine frame to null
359 CanQualType astVoidPtrTy = cgm.getASTContext().VoidPtrTy;
360 mlir::Type allocaTy = convertTypeForMem(astVoidPtrTy);
361 Address coroFrame =
362 createTempAlloca(allocaTy, getContext().getTypeAlignInChars(astVoidPtrTy),
363 openCurlyLoc, "__coro_frame_addr",
364 /*ArraySize=*/nullptr);
365
366 mlir::Value storeAddr = coroFrame.getPointer();
367 builder.CIRBaseBuilderTy::createStore(openCurlyLoc, nullPtrCst, storeAddr);
368 cir::IfOp::create(
369 builder, openCurlyLoc, coroAlloc.getResult(),
370 /*withElseRegion=*/false,
371 /*thenBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
372 builder.CIRBaseBuilderTy::createStore(
373 loc, emitScalarExpr(s.getAllocate()), storeAddr);
374 cir::YieldOp::create(builder, loc);
375 });
376 curCoro.data->coroBegin =
378 openCurlyLoc,
379 cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr))
380 .getResult();
381
382 // Handle allocation failure if 'ReturnStmtOnAllocFailure' was provided.
383 if (s.getReturnStmtOnAllocFailure())
384 cgm.errorNYI("handle coroutine return alloc failure");
385
386 {
388 ParamReferenceReplacerRAII paramReplacer(localDeclMap);
389 RunCleanupsScope resumeScope(*this);
390 ehStack.pushCleanup<CallCoroDelete>(NormalAndEHCleanup, s.getDeallocate());
391 // Create mapping between parameters and copy-params for coroutine
392 // function.
393 llvm::ArrayRef<const Stmt *> paramMoves = s.getParamMoves();
394 assert((paramMoves.size() == 0 || (paramMoves.size() == fnArgs.size())) &&
395 "ParamMoves and FnArgs should be the same size for coroutine "
396 "function");
397 // For zipping the arg map into debug info.
399
400 // Create parameter copies. We do it before creating a promise, since an
401 // evolution of coroutine TS may allow promise constructor to observe
402 // parameter copies.
404 for (auto *pm : paramMoves) {
405 if (emitStmt(pm, /*useCurrentScope=*/true).failed())
406 return mlir::failure();
407 paramReplacer.addCopy(cast<DeclStmt>(pm));
408 }
409
410 if (emitStmt(s.getPromiseDeclStmt(), /*useCurrentScope=*/true).failed())
411 return mlir::failure();
412 // returnValue should be valid as long as the coroutine's return type
413 // is not void. The assertion could help us to reduce the check later.
414 assert(returnValue.isValid() == (bool)s.getReturnStmt());
415 // Now we have the promise, initialize the GRO.
416 // We need to emit `get_return_object` first. According to:
417 // [dcl.fct.def.coroutine]p7
418 // The call to get_return_­object is sequenced before the call to
419 // initial_suspend and is invoked at most once.
420 //
421 // So we couldn't emit return value when we emit return statment,
422 // otherwise the call to get_return_object wouldn't be in front
423 // of initial_suspend.
424 if (returnValue.isValid())
425 emitAnyExprToMem(s.getReturnValue(), returnValue,
426 s.getReturnValue()->getType().getQualifiers(),
427 /*isInit*/ true);
428
430
431 curCoro.data->currentAwaitKind = cir::AwaitKind::Init;
432 curCoro.data->exceptionHandler = s.getExceptionHandler();
433
434 if (emitStmt(s.getInitSuspendStmt(), /*useCurrentScope=*/true).failed())
435 return mlir::failure();
436
437 curCoro.data->currentAwaitKind = cir::AwaitKind::User;
438
439 mlir::OpBuilder::InsertPoint userBody;
440 auto coroBodyOp =
441 cir::CoroBodyOp::create(builder, openCurlyLoc, /*scopeBuilder=*/
442 [&](mlir::OpBuilder &b, mlir::Location loc) {
443 userBody = b.saveInsertionPoint();
444 });
445 {
446 mlir::OpBuilder::InsertionGuard guard(builder);
447 builder.restoreInsertionPoint(userBody);
448 if (curCoro.data->exceptionHandler) {
449 // This bit of code is supposed to do:
450 //
451 // if (await-resume-didnt-throw-exception) {
452 // try {
453 // coroutine-body
454 // } catch (...) {
455 // unhandled_exception();
456 // }
457 // }
458 //
459 // IF resume couldn't have thrown an exception(await_resume is
460 // noexcept), we skip the 'if'.
461 //
462 // Note that we've reversed the condition of the 'if' from classic
463 // codegen so that we don't need an 'else' block.
464 if (curCoro.data->resumeEHVar.isValid()) {
465 mlir::Value shouldSkip = builder.createFlagLoad(
466 openCurlyLoc, curCoro.data->resumeEHVar.getPointer());
467 mlir::LogicalResult res = mlir::success();
468 cir::IfOp::create(builder, openCurlyLoc, shouldSkip,
469 /*withElseRegion=*/false,
470 [&](mlir::OpBuilder &b, mlir::Location loc) {
471 res = coroutineBodyExceptionHelper(*this, s);
472 builder.createYield(openCurlyLoc);
473 });
474
475 if (res.failed())
476 return mlir::failure();
477
478 } else if (coroutineBodyExceptionHelper(*this, s).failed()) {
479 return mlir::failure();
480 }
481 } else if (emitBodyAndFallthrough(*this, s, s.getBody(), curLexScope)
482 .failed()) {
483 return mlir::failure();
484 }
485 }
486
487 mlir::Block &coroBodyBlock = coroBodyOp.getBody().back();
488 if (!coroBodyBlock.mightHaveTerminator()) {
489 mlir::OpBuilder::InsertionGuard guard(builder);
490 builder.setInsertionPointToEnd(&coroBodyBlock);
491 cir::YieldOp::create(builder, openCurlyLoc);
492 }
493
494 // Note that LLVM checks CanFallthrough by looking into the availability
495 // of the insert block which is kinda brittle and unintuitive, seems to be
496 // related with how landing pads are handled.
497 //
498 // CIRGen handles this by checking pre-existing co_returns in the current
499 // scope instead.
500 //
501 // From LLVM IR Gen: const bool CanFallthrough = Builder.GetInsertBlock();
502 const bool canFallthrough = curLexScope->hasCoreturn();
503 const bool hasCoreturns = curCoro.data->coreturnCount > 0;
504 if (canFallthrough || hasCoreturns) {
505 curCoro.data->currentAwaitKind = cir::AwaitKind::Final;
506 {
507 mlir::OpBuilder::InsertionGuard guard(builder);
508 if (emitStmt(s.getFinalSuspendStmt(), /*useCurrentScope=*/true)
509 .failed())
510 return mlir::failure();
511 }
512 }
513 }
514
516 openCurlyLoc, builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc));
517 if (auto *ret = cast_or_null<ReturnStmt>(s.getReturnStmt())) {
518 // Since we already emitted the return value above, so we shouldn't
519 // emit it again here.
520 Expr *previousRetValue = ret->getRetValue();
521 ret->setRetValue(nullptr);
522 if (emitStmt(ret, /*useCurrentScope=*/true).failed())
523 return mlir::failure();
524 // Set the return value back. The code generator, as the AST **Consumer**,
525 // shouldn't change the AST.
526 ret->setRetValue(previousRetValue);
527 }
528 return mlir::success();
529}
530
531static bool memberCallExpressionCanThrow(const Expr *e) {
532 if (const auto *ce = dyn_cast<CXXMemberCallExpr>(e))
533 if (const auto *proto =
534 ce->getMethodDecl()->getType()->getAs<FunctionProtoType>())
535 if (isNoexceptExceptionSpec(proto->getExceptionSpecType()) &&
536 proto->canThrow() == CT_Cannot)
537 return false;
538 return true;
539}
540
541// Given a suspend expression which roughly looks like:
542//
543// auto && x = CommonExpr();
544// if (!x.await_ready()) {
545// x.await_suspend(...); (*)
546// }
547// x.await_resume();
548//
549// where the result of the entire expression is the result of x.await_resume()
550//
551// (*) If x.await_suspend return type is bool, it allows to veto a suspend:
552// if (x.await_suspend(...))
553// llvm_coro_suspend();
554//
555// This is more higher level than LLVM codegen, for that one see llvm's
556// docs/Coroutines.rst for more details.
557namespace {
558struct LValueOrRValue {
559 LValue lv;
560 RValue rv;
561};
562} // namespace
563
564static LValueOrRValue
566 CoroutineSuspendExpr const &s, cir::AwaitKind kind,
567 AggValueSlot aggSlot, bool ignoreResult,
568 mlir::Block *scopeParentBlock,
569 mlir::Value &tmpResumeRValAddr, bool forLValue) {
570 [[maybe_unused]] mlir::LogicalResult awaitBuild = mlir::success();
571 LValueOrRValue awaitRes;
572
574 CIRGenFunction::OpaqueValueMapping(cgf, s.getOpaqueValue());
575 CIRGenBuilderTy &builder = cgf.getBuilder();
576 [[maybe_unused]] cir::AwaitOp awaitOp = cir::AwaitOp::create(
577 builder, cgf.getLoc(s.getSourceRange()), kind,
578 /*readyBuilder=*/
579 [&](mlir::OpBuilder &b, mlir::Location loc) {
580 Expr *condExpr = s.getReadyExpr()->IgnoreParens();
581 builder.createCondition(cgf.evaluateExprAsBool(condExpr));
582 },
583 /*suspendBuilder=*/
584 [&](mlir::OpBuilder &b, mlir::Location loc) {
585 // Note that differently from LLVM codegen we do not emit coro.save
586 // and coro.suspend here, that should be done as part of lowering this
587 // to LLVM dialect (or some other MLIR dialect)
588
589 // A invalid suspendRet indicates "void returning await_suspend"
590 mlir::Value suspendRet = cgf.emitScalarExpr(s.getSuspendExpr());
591
592 // Veto suspension if requested by bool returning await_suspend.
593 if (suspendRet) {
594 cgf.cgm.errorNYI("Veto await_suspend");
595 }
596
597 // Signals the parent that execution flows to next region.
598 cir::YieldOp::create(builder, loc);
599 },
600 /*resumeBuilder=*/
601 [&](mlir::OpBuilder &b, mlir::Location loc) {
602 // Exception handling requires additional IR. If the 'await_resume'
603 // function is marked as 'noexcept', we avoid generating this additional
604 // IR.
605 if (coro.exceptionHandler && kind == cir::AwaitKind::Init &&
606 memberCallExpressionCanThrow(s.getResumeExpr())) {
607 // we are basically just emitting:
608 // resumeEh = false;
609 // try {
610 // resumeExpr();
611 // resumeEh = true;
612 // } catch(...) {
613 // exceptionHandler();
614 // }
615 // Note the values of resumeEh are reversed from classic codegen,
616 // simply so we can use an 'IfOp' without a 'else' later.
617 ASTContext &ctx = cgf.getContext();
618 SourceLocation resumeLoc = s.getResumeExpr()->getExprLoc();
619 mlir::Location mlirLoc = cgf.getLoc(resumeLoc);
620 coro.resumeEHVar = cgf.createTempAlloca(
621 builder.getBoolTy(), ctx.getTypeAlignInChars(ctx.BoolTy), mlirLoc,
622 "resume.eh");
623 builder.createFlagStore(mlirLoc, false,
624 coro.resumeEHVar.getPointer());
625
626 CXXCatchStmt catchStmt(resumeLoc,
627 /*exDecl=*/nullptr, coro.exceptionHandler);
628 auto *tryBody =
629 CompoundStmt::Create(ctx, s.getResumeExpr(), FPOptionsOverride(),
630 resumeLoc, resumeLoc);
631 CXXTryStmt *tryStmt =
632 CXXTryStmt::Create(ctx, resumeLoc, tryBody, &catchStmt);
633
634 struct resumeEmitter final : CIRGenFunction::cxxTryBodyEmitter {
635 const CXXTryStmt &tryStmt;
636 mlir::Location loc;
637 mlir::Value resumeEHVar;
638 resumeEmitter(const CXXTryStmt &tryStmt, mlir::Location loc,
639 Address resumeEHVar)
640 : tryStmt(tryStmt), loc(loc),
641 resumeEHVar(resumeEHVar.getPointer()) {}
642
643 mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
644 mlir::LogicalResult res =
645 cgf.emitStmt(tryStmt.getTryBlock(), /*useCurrentScope=*/true);
646 cgf.getBuilder().createFlagStore(loc, true, resumeEHVar);
647 return res;
648 }
649
650 ~resumeEmitter() override = default;
651 } emitter{*tryStmt, mlirLoc, coro.resumeEHVar};
652
653 awaitBuild = cgf.emitCXXTryStmt(*tryStmt, emitter);
654 // We are not supposed to obtain the value from init suspend
655 // await_resume().
656 awaitRes.rv = RValue::getIgnored();
657 } else if (forLValue) {
658 // FIXME(cir): the alloca for the resume expr should be placed in the
659 // enclosing cir.scope instead.
660 awaitRes.lv = cgf.emitLValue(s.getResumeExpr());
661 } else {
662 awaitRes.rv =
663 cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult);
664 if (!awaitRes.rv.isIgnored()) {
665 // Create the alloca in the block before the scope wrapping
666 // cir.await.
667 mlir::Value value;
668 RValue rv = awaitRes.rv;
669 if (rv.isScalar()) {
670 value = rv.getValue();
671 } else if (rv.isComplex()) {
672 value = rv.getComplexValue();
673 } else {
674 cgf.cgm.errorNYI("emitSuspendExpression: Aggregate value");
675 return;
676 }
677
678 tmpResumeRValAddr = cgf.emitAlloca(
679 "__coawait_resume_rval", value.getType(), loc, CharUnits::One(),
680 builder.getBestAllocaInsertPoint(scopeParentBlock));
681 // Store the rvalue so we can reload it before the promise call.
682 builder.CIRBaseBuilderTy::createStore(loc, value,
683 tmpResumeRValAddr);
684 }
685 }
686
687 // Returns control back to parent.
688 cir::YieldOp::create(builder, loc);
689 });
690
691 assert(awaitBuild.succeeded() && "Should know how to codegen");
692 return awaitRes;
693}
694
696 const CoroutineSuspendExpr &e,
697 cir::AwaitKind kind, AggValueSlot aggSlot,
698 bool ignoreResult) {
699 RValue rval;
700 mlir::Location scopeLoc = cgf.getLoc(e.getSourceRange());
701
702 // Since we model suspend / resume as an inner region, we must store
703 // resume scalar results in a tmp alloca, and load it after we build the
704 // suspend expression. An alternative way to do this would be to make
705 // every region return a value when promise.return_value() is used, but
706 // it's a bit awkward given that resume is the only region that actually
707 // returns a value.
708 mlir::Block *currEntryBlock = cgf.curLexScope->getEntryBlock();
709 [[maybe_unused]] mlir::Value tmpResumeRValAddr;
710
711 // No need to explicitly wrap this into a scope since the AST already uses a
712 // ExprWithCleanups, which will wrap this into a cir.scope anyways.
713 rval = emitSuspendExpression(cgf, *cgf.curCoro.data, e, kind, aggSlot,
714 ignoreResult, currEntryBlock, tmpResumeRValAddr,
715 /*forLValue*/ false)
716 .rv;
717
718 if (ignoreResult || rval.isIgnored())
719 return rval;
720
721 if (rval.isScalar()) {
722 rval = RValue::get(cir::LoadOp::create(cgf.getBuilder(), scopeLoc,
723 rval.getValue().getType(),
724 tmpResumeRValAddr));
725 } else if (rval.isAggregate()) {
726 // This is probably already handled via AggSlot, remove this assertion
727 // once we have a testcase and prove all pieces work.
728 cgf.cgm.errorNYI("emitSuspendExpr Aggregate");
729 } else { // complex
730 rval = RValue::getComplex(cir::LoadOp::create(
731 cgf.getBuilder(), scopeLoc, rval.getComplexValue().getType(),
732 tmpResumeRValAddr));
733 }
734 return rval;
735}
736
738 AggValueSlot aggSlot,
739 bool ignoreResult) {
740 return emitSuspendExpr(*this, e, curCoro.data->currentAwaitKind, aggSlot,
741 ignoreResult);
742}
743
745 AggValueSlot aggSlot,
746 bool ignoreResult) {
747 return emitSuspendExpr(*this, e, cir::AwaitKind::Yield, aggSlot,
748 ignoreResult);
749}
750
752 ++curCoro.data->coreturnCount;
753 curLexScope->setCoreturn();
754
755 const Expr *rv = s.getOperand();
756 if (rv && rv->getType()->isVoidType() && !isa<InitListExpr>(rv)) {
757 // Make sure to evaluate the non initlist expression of a co_return
758 // with a void expression for side effects.
759 RunCleanupsScope cleanupScope(*this);
760 emitIgnoredExpr(rv);
761 }
762
763 if (emitStmt(s.getPromiseCall(), /*useCurrentScope=*/true).failed())
764 return mlir::failure();
765 // Create a new return block (if not existent) and add a branch to
766 // it. The actual return instruction is only inserted during current
767 // scope cleanup handling.
768 mlir::Location loc = getLoc(s.getSourceRange());
769 cir::CoReturnOp::create(builder, loc);
770
771 return mlir::success();
772}
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 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)
static void createCoroData(CIRGenFunction &cgf, CIRGenFunction::CGCoroInfo &curCoro, cir::CallOp coroId)
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
__device__ __2f16 b
__device__ __2f16 float __ockl_bool s
__device__ __2f16 float c
cir::StoreOp createFlagStore(mlir::Location loc, bool val, mlir::Value dst)
mlir::Value createPtrIsNotNull(mlir::Value ptr)
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
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.
cir::CallOp emitCoroEndBuiltinCall(mlir::Location loc, mlir::Value nullPtr)
cir::CallOp emitCoroIDBuiltinCall(mlir::Location loc, mlir::Value nullPtr)
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,...
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.
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.
cir::CallOp emitCoroFreeBuiltin(const CallExpr *e)
RValue emitCoyieldExpr(const CoyieldExpr &e, AggValueSlot aggSlot=AggValueSlot::ignored(), bool ignoreResult=false)
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)
cir::CallOp emitCoroAllocBuiltinCall(mlir::Location loc)
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, mlir::Location loc, clang::CharUnits alignment, bool insertIntoFnEntryBlock, mlir::Value arraySize=nullptr)
Address returnValue
The temporary alloca to hold the return value.
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.
clang::ASTContext & getContext() const
mlir::LogicalResult emitCoroutineBody(const CoroutineBodyStmt &s)
cir::CallOp emitCoroBeginBuiltinCall(mlir::Location loc, mlir::Value coroframeAddr)
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.
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:28
static CXXTryStmt * Create(const ASTContext &C, SourceLocation tryLoc, CompoundStmt *tryBlock, ArrayRef< Stmt * > handlers)
Definition StmtCXX.cpp:25
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2946
SourceLocation getBeginLoc() const
Definition Expr.h:3280
static CharUnits One()
One - Construct a CharUnits quantity of one.
Definition CharUnits.h:58
Represents a 'co_await' expression.
Definition ExprCXX.h:5365
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:473
Represents the body of a coroutine.
Definition StmtCXX.h:320
Represents an expression that might suspend coroutine execution; either a co_await or co_yield expres...
Definition ExprCXX.h:5251
Represents a 'co_yield' expression.
Definition ExprCXX.h:5446
A reference to a declared variable, function, enum, etc.
Definition Expr.h:1273
ValueDecl * getDecl()
Definition Expr.h:1341
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition Stmt.h:1654
const Decl * getSingleDecl() const
Definition Stmt.h:1656
This represents one expression.
Definition Expr.h:112
QualType getType() const
Definition Expr.h:144
Represents a prototype with parameter type info, e.g.
Definition TypeBase.h:5369
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
Stmt - This represents one statement.
Definition Stmt.h:86
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:227
unsigned getNewAlign() const
Return the largest alignment for which a suitably-sized allocation with 'operator new(size_t)' is gua...
Definition TargetInfo.h:767
unsigned getCharWidth() const
Definition TargetInfo.h:521
bool isVoidType() const
Definition TypeBase.h:9048
const Expr * getInit() const
Definition Decl.h:1381
Defines the clang::TargetInfo interface.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
The JSON file list parser is used to communicate input to InstallAPI.
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