clang 22.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
37// Defining these here allows to keep CGCoroData private to this file.
40
41namespace {
42// FIXME: both GetParamRef and ParamReferenceReplacerRAII are good template
43// candidates to be shared among LLVM / CIR codegen.
44
45// Hunts for the parameter reference in the parameter copy/move declaration.
46struct GetParamRef : public StmtVisitor<GetParamRef> {
47public:
48 DeclRefExpr *expr = nullptr;
49 GetParamRef() {}
50 void VisitDeclRefExpr(DeclRefExpr *e) {
51 assert(expr == nullptr && "multilple declref in param move");
52 expr = e;
53 }
54 void VisitStmt(Stmt *s) {
55 for (Stmt *c : s->children()) {
56 if (c)
57 Visit(c);
58 }
59 }
60};
61
62// This class replaces references to parameters to their copies by changing
63// the addresses in CGF.LocalDeclMap and restoring back the original values in
64// its destructor.
65struct ParamReferenceReplacerRAII {
66 CIRGenFunction::DeclMapTy savedLocals;
67 CIRGenFunction::DeclMapTy &localDeclMap;
68
69 ParamReferenceReplacerRAII(CIRGenFunction::DeclMapTy &localDeclMap)
70 : localDeclMap(localDeclMap) {}
71
72 void addCopy(const DeclStmt *pm) {
73 // Figure out what param it refers to.
74
75 assert(pm->isSingleDecl());
76 const VarDecl *vd = static_cast<const VarDecl *>(pm->getSingleDecl());
77 const Expr *initExpr = vd->getInit();
78 GetParamRef visitor;
79 visitor.Visit(const_cast<Expr *>(initExpr));
80 assert(visitor.expr);
81 DeclRefExpr *dreOrig = visitor.expr;
82 auto *pd = dreOrig->getDecl();
83
84 auto it = localDeclMap.find(pd);
85 assert(it != localDeclMap.end() && "parameter is not found");
86 savedLocals.insert({pd, it->second});
87
88 auto copyIt = localDeclMap.find(vd);
89 assert(copyIt != localDeclMap.end() && "parameter copy is not found");
90 it->second = copyIt->getSecond();
91 }
92
93 ~ParamReferenceReplacerRAII() {
94 for (auto &&savedLocal : savedLocals) {
95 localDeclMap.insert({savedLocal.first, savedLocal.second});
96 }
97 }
98};
99} // namespace
102 cir::CallOp coroId) {
103 assert(!curCoro.data && "EmitCoroutineBodyStatement called twice?");
104
105 curCoro.data = std::make_unique<CGCoroData>();
106 curCoro.data->coroId = coroId;
107}
108
109cir::CallOp CIRGenFunction::emitCoroIDBuiltinCall(mlir::Location loc,
110 mlir::Value nullPtr) {
111 cir::IntType int32Ty = builder.getUInt32Ty();
112
113 const TargetInfo &ti = cgm.getASTContext().getTargetInfo();
114 unsigned newAlign = ti.getNewAlign() / ti.getCharWidth();
115
116 mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroId);
117
118 cir::FuncOp fnOp;
119 if (!builtin) {
120 fnOp = cgm.createCIRBuiltinFunction(
121 loc, cgm.builtinCoroId,
122 cir::FuncType::get({int32Ty, voidPtrTy, voidPtrTy, voidPtrTy}, int32Ty),
123 /*FD=*/nullptr);
124 assert(fnOp && "should always succeed");
125 } else {
126 fnOp = cast<cir::FuncOp>(builtin);
127 }
128
129 return builder.createCallOp(loc, fnOp,
130 mlir::ValueRange{builder.getUInt32(newAlign, loc),
131 nullPtr, nullPtr, nullPtr});
132}
133
134cir::CallOp CIRGenFunction::emitCoroAllocBuiltinCall(mlir::Location loc) {
135 cir::BoolType boolTy = builder.getBoolTy();
136
137 mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroAlloc);
138
139 cir::FuncOp fnOp;
140 if (!builtin) {
141 fnOp = cgm.createCIRBuiltinFunction(loc, cgm.builtinCoroAlloc,
142 cir::FuncType::get({uInt32Ty}, boolTy),
143 /*fd=*/nullptr);
144 assert(fnOp && "should always succeed");
145 } else {
146 fnOp = cast<cir::FuncOp>(builtin);
147 }
148
149 return builder.createCallOp(
150 loc, fnOp, mlir::ValueRange{curCoro.data->coroId.getResult()});
151}
152
153cir::CallOp
155 mlir::Value coroframeAddr) {
156 mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroBegin);
157
158 cir::FuncOp fnOp;
159 if (!builtin) {
160 fnOp = cgm.createCIRBuiltinFunction(
161 loc, cgm.builtinCoroBegin,
162 cir::FuncType::get({uInt32Ty, voidPtrTy}, voidPtrTy),
163 /*fd=*/nullptr);
164 assert(fnOp && "should always succeed");
165 } else {
166 fnOp = cast<cir::FuncOp>(builtin);
167 }
168
169 return builder.createCallOp(
170 loc, fnOp,
171 mlir::ValueRange{curCoro.data->coroId.getResult(), coroframeAddr});
172}
173
174mlir::LogicalResult
176 mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
177 cir::ConstantOp nullPtrCst = builder.getNullPtr(voidPtrTy, openCurlyLoc);
178
179 auto fn = mlir::cast<cir::FuncOp>(curFn);
180 fn.setCoroutine(true);
181 cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);
182 createCoroData(*this, curCoro, coroId);
183
184 // Backend is allowed to elide memory allocations, to help it, emit
185 // auto mem = coro.alloc() ? 0 : ... allocation code ...;
186 cir::CallOp coroAlloc = emitCoroAllocBuiltinCall(openCurlyLoc);
187
188 // Initialize address of coroutine frame to null
189 CanQualType astVoidPtrTy = cgm.getASTContext().VoidPtrTy;
190 mlir::Type allocaTy = convertTypeForMem(astVoidPtrTy);
191 Address coroFrame =
192 createTempAlloca(allocaTy, getContext().getTypeAlignInChars(astVoidPtrTy),
193 openCurlyLoc, "__coro_frame_addr",
194 /*ArraySize=*/nullptr);
195
196 mlir::Value storeAddr = coroFrame.getPointer();
197 builder.CIRBaseBuilderTy::createStore(openCurlyLoc, nullPtrCst, storeAddr);
198 cir::IfOp::create(
199 builder, openCurlyLoc, coroAlloc.getResult(),
200 /*withElseRegion=*/false,
201 /*thenBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
202 builder.CIRBaseBuilderTy::createStore(
203 loc, emitScalarExpr(s.getAllocate()), storeAddr);
204 cir::YieldOp::create(builder, loc);
205 });
206 curCoro.data->coroBegin =
208 openCurlyLoc,
209 cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr))
210 .getResult();
211
212 // Handle allocation failure if 'ReturnStmtOnAllocFailure' was provided.
213 if (s.getReturnStmtOnAllocFailure())
214 cgm.errorNYI("handle coroutine return alloc failure");
215
216 {
218 ParamReferenceReplacerRAII paramReplacer(localDeclMap);
219 // Create mapping between parameters and copy-params for coroutine
220 // function.
221 llvm::ArrayRef<const Stmt *> paramMoves = s.getParamMoves();
222 assert((paramMoves.size() == 0 || (paramMoves.size() == fnArgs.size())) &&
223 "ParamMoves and FnArgs should be the same size for coroutine "
224 "function");
225 // For zipping the arg map into debug info.
227
228 // Create parameter copies. We do it before creating a promise, since an
229 // evolution of coroutine TS may allow promise constructor to observe
230 // parameter copies.
232 for (auto *pm : paramMoves) {
233 if (emitStmt(pm, /*useCurrentScope=*/true).failed())
234 return mlir::failure();
235 paramReplacer.addCopy(cast<DeclStmt>(pm));
236 }
237
238 if (emitStmt(s.getPromiseDeclStmt(), /*useCurrentScope=*/true).failed())
239 return mlir::failure();
240 // returnValue should be valid as long as the coroutine's return type
241 // is not void. The assertion could help us to reduce the check later.
242 assert(returnValue.isValid() == (bool)s.getReturnStmt());
243 // Now we have the promise, initialize the GRO.
244 // We need to emit `get_return_object` first. According to:
245 // [dcl.fct.def.coroutine]p7
246 // The call to get_return_­object is sequenced before the call to
247 // initial_suspend and is invoked at most once.
248 //
249 // So we couldn't emit return value when we emit return statment,
250 // otherwise the call to get_return_object wouldn't be in front
251 // of initial_suspend.
252 if (returnValue.isValid())
253 emitAnyExprToMem(s.getReturnValue(), returnValue,
254 s.getReturnValue()->getType().getQualifiers(),
255 /*isInit*/ true);
256
258 // FIXME(cir): EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
259 curCoro.data->currentAwaitKind = cir::AwaitKind::Init;
260 if (emitStmt(s.getInitSuspendStmt(), /*useCurrentScope=*/true).failed())
261 return mlir::failure();
263 }
264 return mlir::success();
265}
266// Given a suspend expression which roughly looks like:
267//
268// auto && x = CommonExpr();
269// if (!x.await_ready()) {
270// x.await_suspend(...); (*)
271// }
272// x.await_resume();
273//
274// where the result of the entire expression is the result of x.await_resume()
275//
276// (*) If x.await_suspend return type is bool, it allows to veto a suspend:
277// if (x.await_suspend(...))
278// llvm_coro_suspend();
279//
280// This is more higher level than LLVM codegen, for that one see llvm's
281// docs/Coroutines.rst for more details.
282namespace {
283struct LValueOrRValue {
284 LValue lv;
285 RValue rv;
286};
287} // namespace
288
289static LValueOrRValue
291 CoroutineSuspendExpr const &s, cir::AwaitKind kind,
292 AggValueSlot aggSlot, bool ignoreResult,
293 mlir::Block *scopeParentBlock,
294 mlir::Value &tmpResumeRValAddr, bool forLValue) {
295 [[maybe_unused]] mlir::LogicalResult awaitBuild = mlir::success();
296 LValueOrRValue awaitRes;
297
299 CIRGenFunction::OpaqueValueMapping(cgf, s.getOpaqueValue());
300 CIRGenBuilderTy &builder = cgf.getBuilder();
301 [[maybe_unused]] cir::AwaitOp awaitOp = cir::AwaitOp::create(
302 builder, cgf.getLoc(s.getSourceRange()), kind,
303 /*readyBuilder=*/
304 [&](mlir::OpBuilder &b, mlir::Location loc) {
305 builder.createCondition(
306 cgf.createDummyValue(loc, cgf.getContext().BoolTy));
307 },
308 /*suspendBuilder=*/
309 [&](mlir::OpBuilder &b, mlir::Location loc) {
310 cir::YieldOp::create(builder, loc);
311 },
312 /*resumeBuilder=*/
313 [&](mlir::OpBuilder &b, mlir::Location loc) {
314 cir::YieldOp::create(builder, loc);
315 });
316
317 assert(awaitBuild.succeeded() && "Should know how to codegen");
318 return awaitRes;
319}
320
322 const CoroutineSuspendExpr &e,
323 cir::AwaitKind kind, AggValueSlot aggSlot,
324 bool ignoreResult) {
325 RValue rval;
326 mlir::Location scopeLoc = cgf.getLoc(e.getSourceRange());
327
328 // Since we model suspend / resume as an inner region, we must store
329 // resume scalar results in a tmp alloca, and load it after we build the
330 // suspend expression. An alternative way to do this would be to make
331 // every region return a value when promise.return_value() is used, but
332 // it's a bit awkward given that resume is the only region that actually
333 // returns a value.
334 mlir::Block *currEntryBlock = cgf.curLexScope->getEntryBlock();
335 [[maybe_unused]] mlir::Value tmpResumeRValAddr;
336
337 // No need to explicitly wrap this into a scope since the AST already uses a
338 // ExprWithCleanups, which will wrap this into a cir.scope anyways.
339 rval = emitSuspendExpression(cgf, *cgf.curCoro.data, e, kind, aggSlot,
340 ignoreResult, currEntryBlock, tmpResumeRValAddr,
341 /*forLValue*/ false)
342 .rv;
343
344 if (ignoreResult || rval.isIgnored())
345 return rval;
346
347 if (rval.isScalar()) {
348 rval = RValue::get(cir::LoadOp::create(cgf.getBuilder(), scopeLoc,
349 rval.getValue().getType(),
350 tmpResumeRValAddr));
351 } else if (rval.isAggregate()) {
352 // This is probably already handled via AggSlot, remove this assertion
353 // once we have a testcase and prove all pieces work.
354 cgf.cgm.errorNYI("emitSuspendExpr Aggregate");
355 } else { // complex
356 cgf.cgm.errorNYI("emitSuspendExpr Complex");
357 }
358 return rval;
359}
360
362 AggValueSlot aggSlot,
363 bool ignoreResult) {
364 return emitSuspendExpr(*this, e, curCoro.data->currentAwaitKind, aggSlot,
365 ignoreResult);
366}
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 void createCoroData(CIRGenFunction &cgf, CIRGenFunction::CGCoroInfo &curCoro, cir::CallOp coroId)
__device__ __2f16 b
__device__ __2f16 float __ockl_bool s
__device__ __2f16 float c
mlir::Value getPointer() const
Definition Address.h:84
An aggregate value slot.
An RAII object to set (and then clear) a mapping for an OpaqueValueExpr.
cir::CallOp emitCoroIDBuiltinCall(mlir::Location loc, mlir::Value nullPtr)
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
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.
mlir::Operation * curFn
The current function or global initializer that is generated code for.
llvm::SmallVector< const ParmVarDecl * > fnArgs
Save Parameter Decl for coroutine.
mlir::Type convertTypeForMem(QualType t)
cir::CallOp emitCoroAllocBuiltinCall(mlir::Location loc)
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)
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={})
DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef)
Helpers to emit "not yet implemented" error diagnostics.
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
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
Represents a 'co_await' expression.
Definition ExprCXX.h:5369
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:5255
A reference to a declared variable, function, enum, etc.
Definition Expr.h:1270
ValueDecl * getDecl()
Definition Expr.h:1338
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition Stmt.h:1623
const Decl * getSingleDecl() const
Definition Stmt.h:1625
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
Stmt - This represents one statement.
Definition Stmt.h:85
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:338
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:764
unsigned getCharWidth() const
Definition TargetInfo.h:518
const Expr * getInit() const
Definition Decl.h:1368
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.
U cast(CodeGen::Address addr)
Definition Address.h:327
static bool ehCleanupScope()
static bool emitBodyAndFallthrough()
static bool coroOutsideFrameMD()
static bool generateDebugInfo()
std::unique_ptr< CGCoroData > data
cir::PointerType voidPtrTy
void* in address space 0