[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:
parent
7e76473d3f
commit
4448ff453d
@ -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; }
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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".
|
||||
|
||||
@ -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]])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user