[CIR] Emit CIR builtins: coroAlloc, coroBegin, and coroSize (#164180)

This PR adds support for emitting the builtins coroAlloc, coroBegin, and
coroSize.
This commit is contained in:
Andres-Salamanca 2025-10-24 17:55:29 -05:00 committed by GitHub
parent 7e76473d3f
commit 4448ff453d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 108 additions and 10 deletions

View File

@ -150,11 +150,9 @@ struct MissingFeatures {
static bool zeroSizeRecordMembers() { return false; }
// Coroutines
static bool coroAllocBuiltinCall() { return false; }
static bool coroBeginBuiltinCall() { return false; }
static bool coroEndBuiltinCall() { return false; }
static bool coroSizeBuiltinCall() { return false; }
static bool coroutineFrame() { return false; }
static bool emitBodyAndFallthrough() { return false; }
// Various handling of deferred processing in CIRGenModule.
static bool cgmRelease() { return false; }

View File

@ -449,10 +449,15 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
}
case Builtin::BI__builtin_coro_free:
case Builtin::BI__builtin_coro_size: {
cgm.errorNYI(e->getSourceRange(),
"BI__builtin_coro_free, BI__builtin_coro_size NYI");
assert(!cir::MissingFeatures::coroSizeBuiltinCall());
return getUndefRValue(e->getType());
GlobalDecl gd{fd};
mlir::Type ty = cgm.getTypes().getFunctionType(
cgm.getTypes().arrangeGlobalDeclaration(gd));
const auto *nd = cast<NamedDecl>(gd.getDecl());
cir::FuncOp fnOp =
cgm.getOrCreateCIRFunction(nd->getName(), ty, gd, /*ForVTable=*/false);
fnOp.setBuiltin(true);
return emitCall(e->getCallee()->getType(), CIRGenCallee::forDirect(fnOp), e,
returnValue);
}
case Builtin::BI__builtin_prefetch: {
auto evaluateOperandAsInt = [&](const Expr *arg) {

View File

@ -15,6 +15,7 @@
#include "clang/AST/StmtCXX.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"
using namespace clang;
using namespace clang::CIRGen;
@ -23,6 +24,9 @@ struct clang::CIRGen::CGCoroData {
// Stores the __builtin_coro_id emitted in the function so that we can supply
// it as the first argument to other builtins.
cir::CallOp coroId = nullptr;
// Stores the result of __builtin_coro_begin call.
mlir::Value coroBegin = nullptr;
};
// Defining these here allows to keep CGCoroData private to this file.
@ -63,6 +67,46 @@ cir::CallOp CIRGenFunction::emitCoroIDBuiltinCall(mlir::Location loc,
nullPtr, nullPtr, nullPtr});
}
cir::CallOp CIRGenFunction::emitCoroAllocBuiltinCall(mlir::Location loc) {
cir::BoolType boolTy = builder.getBoolTy();
mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroAlloc);
cir::FuncOp fnOp;
if (!builtin) {
fnOp = cgm.createCIRBuiltinFunction(loc, cgm.builtinCoroAlloc,
cir::FuncType::get({UInt32Ty}, boolTy),
/*fd=*/nullptr);
assert(fnOp && "should always succeed");
} else {
fnOp = cast<cir::FuncOp>(builtin);
}
return builder.createCallOp(
loc, fnOp, mlir::ValueRange{curCoro.data->coroId.getResult()});
}
cir::CallOp
CIRGenFunction::emitCoroBeginBuiltinCall(mlir::Location loc,
mlir::Value coroframeAddr) {
mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroBegin);
cir::FuncOp fnOp;
if (!builtin) {
fnOp = cgm.createCIRBuiltinFunction(
loc, cgm.builtinCoroBegin,
cir::FuncType::get({UInt32Ty, VoidPtrTy}, VoidPtrTy),
/*fd=*/nullptr);
assert(fnOp && "should always succeed");
} else {
fnOp = cast<cir::FuncOp>(builtin);
}
return builder.createCallOp(
loc, fnOp,
mlir::ValueRange{curCoro.data->coroId.getResult(), coroframeAddr});
}
mlir::LogicalResult
CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
@ -73,10 +117,39 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);
createCoroData(*this, curCoro, coroId);
assert(!cir::MissingFeatures::coroAllocBuiltinCall());
// Backend is allowed to elide memory allocations, to help it, emit
// auto mem = coro.alloc() ? 0 : ... allocation code ...;
cir::CallOp coroAlloc = emitCoroAllocBuiltinCall(openCurlyLoc);
assert(!cir::MissingFeatures::coroBeginBuiltinCall());
// Initialize address of coroutine frame to null
CanQualType astVoidPtrTy = cgm.getASTContext().VoidPtrTy;
mlir::Type allocaTy = convertTypeForMem(astVoidPtrTy);
Address coroFrame =
createTempAlloca(allocaTy, getContext().getTypeAlignInChars(astVoidPtrTy),
openCurlyLoc, "__coro_frame_addr",
/*ArraySize=*/nullptr);
mlir::Value storeAddr = coroFrame.getPointer();
builder.CIRBaseBuilderTy::createStore(openCurlyLoc, nullPtrCst, storeAddr);
cir::IfOp::create(
builder, openCurlyLoc, coroAlloc.getResult(),
/*withElseRegion=*/false,
/*thenBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
builder.CIRBaseBuilderTy::createStore(
loc, emitScalarExpr(s.getAllocate()), storeAddr);
cir::YieldOp::create(builder, loc);
});
curCoro.data->coroBegin =
emitCoroBeginBuiltinCall(
openCurlyLoc,
cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr))
.getResult();
// Handle allocation failure if 'ReturnStmtOnAllocFailure' was provided.
if (s.getReturnStmtOnAllocFailure())
cgm.errorNYI("handle coroutine return alloc failure");
assert(!cir::MissingFeatures::generateDebugInfo());
assert(!cir::MissingFeatures::emitBodyAndFallthrough());
return mlir::success();
}

View File

@ -1332,6 +1332,9 @@ public:
mlir::LogicalResult emitCoroutineBody(const CoroutineBodyStmt &s);
cir::CallOp emitCoroEndBuiltinCall(mlir::Location loc, mlir::Value nullPtr);
cir::CallOp emitCoroIDBuiltinCall(mlir::Location loc, mlir::Value nullPtr);
cir::CallOp emitCoroAllocBuiltinCall(mlir::Location loc);
cir::CallOp emitCoroBeginBuiltinCall(mlir::Location loc,
mlir::Value coroframeAddr);
void emitDestroy(Address addr, QualType type, Destroyer *destroyer);

View File

@ -496,6 +496,8 @@ public:
bool assumeConvergent = false);
static constexpr const char *builtinCoroId = "__builtin_coro_id";
static constexpr const char *builtinCoroAlloc = "__builtin_coro_alloc";
static constexpr const char *builtinCoroBegin = "__builtin_coro_begin";
/// Given a builtin id for a function like "__builtin_fabsf", return a
/// Function* for "fabsf".

View File

@ -106,6 +106,9 @@ co_invoke_fn co_invoke;
// CIR-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : !rec_folly3A3Acoro3A3Aco_invoke_fn
// CIR: cir.func builtin private @__builtin_coro_id(!u32i, !cir.ptr<!void>, !cir.ptr<!void>, !cir.ptr<!void>) -> !u32i
// CIR: cir.func builtin private @__builtin_coro_alloc(!u32i) -> !cir.bool
// CIR: cir.func builtin private @__builtin_coro_size() -> !u64i
// CIR: cir.func builtin private @__builtin_coro_begin(!u32i, !cir.ptr<!void>) -> !cir.ptr<!void>
using VoidTask = folly::coro::Task<void>;
@ -114,10 +117,24 @@ VoidTask silly_task() {
}
// CIR: cir.func coroutine dso_local @_Z10silly_taskv() -> ![[VoidTask]]
// CHECK: %[[#VoidTaskAddr:]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"]
// CIR: %[[VoidTaskAddr:.*]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"]
// CIR: %[[SavedFrameAddr:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__coro_frame_addr"]
// Get coroutine id with __builtin_coro_id.
// CIR: %[[NullPtr:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
// CIR: %[[Align:.*]] = cir.const #cir.int<16> : !u32i
// CIR: %[[CoroId:.*]] = cir.call @__builtin_coro_id(%[[Align]], %[[NullPtr]], %[[NullPtr]], %[[NullPtr]])
// Perform allocation calling operator 'new' depending on __builtin_coro_alloc and
// call __builtin_coro_begin for the final coroutine frame address.
// CIR: %[[ShouldAlloc:.*]] = cir.call @__builtin_coro_alloc(%[[CoroId]]) : (!u32i) -> !cir.bool
// CIR: cir.store{{.*}} %[[NullPtr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: cir.if %[[ShouldAlloc]] {
// CIR: %[[CoroSize:.*]] = cir.call @__builtin_coro_size() : () -> !u64i
// CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) : (!u64i) -> !cir.ptr<!void>
// CIR: cir.store{{.*}} %[[AllocAddr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: }
// CIR: %[[Load0:.*]] = cir.load{{.*}} %[[SavedFrameAddr]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
// CIR: %[[CoroFrameAddr:.*]] = cir.call @__builtin_coro_begin(%[[CoroId]], %[[Load0]])