Skip to content

Commit c2cd002

Browse files
Andres-Salamancadvbuka
authored andcommitted
[CIR] Emit CIR builtins: coroAlloc, coroBegin, and coroSize (llvm#164180)
This PR adds support for emitting the builtins coroAlloc, coroBegin, and coroSize.
1 parent e304553 commit c2cd002

File tree

6 files changed

+109
-11
lines changed

6 files changed

+109
-11
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,9 @@ struct MissingFeatures {
150150
static bool zeroSizeRecordMembers() { return false; }
151151

152152
// Coroutines
153-
static bool coroAllocBuiltinCall() { return false; }
154-
static bool coroBeginBuiltinCall() { return false; }
155153
static bool coroEndBuiltinCall() { return false; }
156-
static bool coroSizeBuiltinCall() { return false; }
157154
static bool coroutineFrame() { return false; }
155+
static bool emitBodyAndFallthrough() { return false; }
158156

159157
// Various handling of deferred processing in CIRGenModule.
160158
static bool cgmRelease() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,10 +449,15 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
449449
}
450450
case Builtin::BI__builtin_coro_free:
451451
case Builtin::BI__builtin_coro_size: {
452-
cgm.errorNYI(e->getSourceRange(),
453-
"BI__builtin_coro_free, BI__builtin_coro_size NYI");
454-
assert(!cir::MissingFeatures::coroSizeBuiltinCall());
455-
return getUndefRValue(e->getType());
452+
GlobalDecl gd{fd};
453+
mlir::Type ty = cgm.getTypes().getFunctionType(
454+
cgm.getTypes().arrangeGlobalDeclaration(gd));
455+
const auto *nd = cast<NamedDecl>(gd.getDecl());
456+
cir::FuncOp fnOp =
457+
cgm.getOrCreateCIRFunction(nd->getName(), ty, gd, /*ForVTable=*/false);
458+
fnOp.setBuiltin(true);
459+
return emitCall(e->getCallee()->getType(), CIRGenCallee::forDirect(fnOp), e,
460+
returnValue);
456461
}
457462
case Builtin::BI__builtin_prefetch: {
458463
auto evaluateOperandAsInt = [&](const Expr *arg) {

clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "clang/AST/StmtCXX.h"
1616
#include "clang/Basic/TargetInfo.h"
1717
#include "clang/CIR/Dialect/IR/CIRTypes.h"
18+
#include "clang/CIR/MissingFeatures.h"
1819

1920
using namespace clang;
2021
using namespace clang::CIRGen;
@@ -23,6 +24,9 @@ struct clang::CIRGen::CGCoroData {
2324
// Stores the __builtin_coro_id emitted in the function so that we can supply
2425
// it as the first argument to other builtins.
2526
cir::CallOp coroId = nullptr;
27+
28+
// Stores the result of __builtin_coro_begin call.
29+
mlir::Value coroBegin = nullptr;
2630
};
2731

2832
// Defining these here allows to keep CGCoroData private to this file.
@@ -63,6 +67,46 @@ cir::CallOp CIRGenFunction::emitCoroIDBuiltinCall(mlir::Location loc,
6367
nullPtr, nullPtr, nullPtr});
6468
}
6569

70+
cir::CallOp CIRGenFunction::emitCoroAllocBuiltinCall(mlir::Location loc) {
71+
cir::BoolType boolTy = builder.getBoolTy();
72+
73+
mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroAlloc);
74+
75+
cir::FuncOp fnOp;
76+
if (!builtin) {
77+
fnOp = cgm.createCIRBuiltinFunction(loc, cgm.builtinCoroAlloc,
78+
cir::FuncType::get({UInt32Ty}, boolTy),
79+
/*fd=*/nullptr);
80+
assert(fnOp && "should always succeed");
81+
} else {
82+
fnOp = cast<cir::FuncOp>(builtin);
83+
}
84+
85+
return builder.createCallOp(
86+
loc, fnOp, mlir::ValueRange{curCoro.data->coroId.getResult()});
87+
}
88+
89+
cir::CallOp
90+
CIRGenFunction::emitCoroBeginBuiltinCall(mlir::Location loc,
91+
mlir::Value coroframeAddr) {
92+
mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroBegin);
93+
94+
cir::FuncOp fnOp;
95+
if (!builtin) {
96+
fnOp = cgm.createCIRBuiltinFunction(
97+
loc, cgm.builtinCoroBegin,
98+
cir::FuncType::get({UInt32Ty, VoidPtrTy}, VoidPtrTy),
99+
/*fd=*/nullptr);
100+
assert(fnOp && "should always succeed");
101+
} else {
102+
fnOp = cast<cir::FuncOp>(builtin);
103+
}
104+
105+
return builder.createCallOp(
106+
loc, fnOp,
107+
mlir::ValueRange{curCoro.data->coroId.getResult(), coroframeAddr});
108+
}
109+
66110
mlir::LogicalResult
67111
CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
68112
mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
@@ -73,10 +117,39 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
73117
cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);
74118
createCoroData(*this, curCoro, coroId);
75119

76-
assert(!cir::MissingFeatures::coroAllocBuiltinCall());
77-
78-
assert(!cir::MissingFeatures::coroBeginBuiltinCall());
120+
// Backend is allowed to elide memory allocations, to help it, emit
121+
// auto mem = coro.alloc() ? 0 : ... allocation code ...;
122+
cir::CallOp coroAlloc = emitCoroAllocBuiltinCall(openCurlyLoc);
123+
124+
// Initialize address of coroutine frame to null
125+
CanQualType astVoidPtrTy = cgm.getASTContext().VoidPtrTy;
126+
mlir::Type allocaTy = convertTypeForMem(astVoidPtrTy);
127+
Address coroFrame =
128+
createTempAlloca(allocaTy, getContext().getTypeAlignInChars(astVoidPtrTy),
129+
openCurlyLoc, "__coro_frame_addr",
130+
/*ArraySize=*/nullptr);
131+
132+
mlir::Value storeAddr = coroFrame.getPointer();
133+
builder.CIRBaseBuilderTy::createStore(openCurlyLoc, nullPtrCst, storeAddr);
134+
cir::IfOp::create(
135+
builder, openCurlyLoc, coroAlloc.getResult(),
136+
/*withElseRegion=*/false,
137+
/*thenBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
138+
builder.CIRBaseBuilderTy::createStore(
139+
loc, emitScalarExpr(s.getAllocate()), storeAddr);
140+
cir::YieldOp::create(builder, loc);
141+
});
142+
curCoro.data->coroBegin =
143+
emitCoroBeginBuiltinCall(
144+
openCurlyLoc,
145+
cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr))
146+
.getResult();
147+
148+
// Handle allocation failure if 'ReturnStmtOnAllocFailure' was provided.
149+
if (s.getReturnStmtOnAllocFailure())
150+
cgm.errorNYI("handle coroutine return alloc failure");
79151

80152
assert(!cir::MissingFeatures::generateDebugInfo());
153+
assert(!cir::MissingFeatures::emitBodyAndFallthrough());
81154
return mlir::success();
82155
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,9 @@ class CIRGenFunction : public CIRGenTypeCache {
13321332
mlir::LogicalResult emitCoroutineBody(const CoroutineBodyStmt &s);
13331333
cir::CallOp emitCoroEndBuiltinCall(mlir::Location loc, mlir::Value nullPtr);
13341334
cir::CallOp emitCoroIDBuiltinCall(mlir::Location loc, mlir::Value nullPtr);
1335+
cir::CallOp emitCoroAllocBuiltinCall(mlir::Location loc);
1336+
cir::CallOp emitCoroBeginBuiltinCall(mlir::Location loc,
1337+
mlir::Value coroframeAddr);
13351338

13361339
void emitDestroy(Address addr, QualType type, Destroyer *destroyer);
13371340

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,8 @@ class CIRGenModule : public CIRGenTypeCache {
496496
bool assumeConvergent = false);
497497

498498
static constexpr const char *builtinCoroId = "__builtin_coro_id";
499+
static constexpr const char *builtinCoroAlloc = "__builtin_coro_alloc";
500+
static constexpr const char *builtinCoroBegin = "__builtin_coro_begin";
499501

500502
/// Given a builtin id for a function like "__builtin_fabsf", return a
501503
/// Function* for "fabsf".

clang/test/CIR/CodeGen/coro-task.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ co_invoke_fn co_invoke;
106106
// CIR-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : !rec_folly3A3Acoro3A3Aco_invoke_fn
107107

108108
// CIR: cir.func builtin private @__builtin_coro_id(!u32i, !cir.ptr<!void>, !cir.ptr<!void>, !cir.ptr<!void>) -> !u32i
109+
// CIR: cir.func builtin private @__builtin_coro_alloc(!u32i) -> !cir.bool
110+
// CIR: cir.func builtin private @__builtin_coro_size() -> !u64i
111+
// CIR: cir.func builtin private @__builtin_coro_begin(!u32i, !cir.ptr<!void>) -> !cir.ptr<!void>
109112

110113
using VoidTask = folly::coro::Task<void>;
111114

@@ -114,10 +117,24 @@ VoidTask silly_task() {
114117
}
115118

116119
// CIR: cir.func coroutine dso_local @_Z10silly_taskv() -> ![[VoidTask]]
117-
// CHECK: %[[#VoidTaskAddr:]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"]
120+
// CIR: %[[VoidTaskAddr:.*]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"]
121+
// CIR: %[[SavedFrameAddr:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__coro_frame_addr"]
118122

119123
// Get coroutine id with __builtin_coro_id.
120124

121125
// CIR: %[[NullPtr:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
122126
// CIR: %[[Align:.*]] = cir.const #cir.int<16> : !u32i
123127
// CIR: %[[CoroId:.*]] = cir.call @__builtin_coro_id(%[[Align]], %[[NullPtr]], %[[NullPtr]], %[[NullPtr]])
128+
129+
// Perform allocation calling operator 'new' depending on __builtin_coro_alloc and
130+
// call __builtin_coro_begin for the final coroutine frame address.
131+
132+
// CIR: %[[ShouldAlloc:.*]] = cir.call @__builtin_coro_alloc(%[[CoroId]]) : (!u32i) -> !cir.bool
133+
// CIR: cir.store{{.*}} %[[NullPtr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
134+
// CIR: cir.if %[[ShouldAlloc]] {
135+
// CIR: %[[CoroSize:.*]] = cir.call @__builtin_coro_size() : () -> !u64i
136+
// CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) : (!u64i) -> !cir.ptr<!void>
137+
// CIR: cir.store{{.*}} %[[AllocAddr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
138+
// CIR: }
139+
// CIR: %[[Load0:.*]] = cir.load{{.*}} %[[SavedFrameAddr]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
140+
// CIR: %[[CoroFrameAddr:.*]] = cir.call @__builtin_coro_begin(%[[CoroId]], %[[Load0]])

0 commit comments

Comments
 (0)