https://github.com/Andres-Salamanca updated https://github.com/llvm/llvm-project/pull/211699
>From cd87691f4eda3bd7330350a7e9ebe0bb491940d2 Mon Sep 17 00:00:00 2001 From: Andres Salamanca <[email protected]> Date: Thu, 23 Jul 2026 19:07:57 -0500 Subject: [PATCH 1/3] [CIR] Change previous coroutine builtins to have their own coro intrinsic ops --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 125 +++++++++++ clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 48 +++-- clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 204 +++++++++--------- clang/lib/CIR/CodeGen/CIRGenFunction.h | 15 +- clang/lib/CIR/CodeGen/CIRGenModule.h | 6 - .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 42 ++++ clang/test/CIR/CodeGen/coro-builtins-err.cpp | 10 + clang/test/CIR/CodeGen/coro-builtins.cpp | 51 +++++ .../test/CIR/CodeGenCoroutines/coro-task.cpp | 22 +- 9 files changed, 378 insertions(+), 145 deletions(-) create mode 100644 clang/test/CIR/CodeGen/coro-builtins-err.cpp create mode 100644 clang/test/CIR/CodeGen/coro-builtins.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index b0f654a54bacd..e62bc98c4a341 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4702,6 +4702,131 @@ def CIR_CoReturnOp : CIR_Op<"co_return", [ let hasLLVMLowering = false; } +//===----------------------------------------------------------------------===// +// Coroutine intrinsics +//===----------------------------------------------------------------------===// + +class CIR_CoroIntrinsicOp<string mnem, dag ins, dag outs, + list<Trait> traits = []> + : CIR_Op<"coro.intrinsic." # mnem, traits> { + let hasLLVMLowering = 1; + let arguments = ins; + let results = outs; + + let assemblyFormat = [{ + `(` operands `)` `:` functional-type(operands, results) attr-dict + }]; +} +//===----------------------------------------------------------------------===// +// Coroutine intrinsic IdOp +//===----------------------------------------------------------------------===// + +// TODO: This operation should return an MLIR `token` once the type becomes +// available. `CIR_UInt32` is used as a temporary placeholder. +def CIR_CoroIntrinsicIdOp : CIR_CoroIntrinsicOp<"id", + (ins CIR_AnyIntType:$align, CIR_VoidPtrType:$promise, + CIR_VoidPtrType:$coroaddr, CIR_VoidPtrType:$fnaddrs), + (outs CIR_UInt32:$result)> { + let summary = "Represents llvm.coro.id"; + let description = [{ + Marks the beginning of a coroutine's lifetime and identifies it to the + rest of the coroutine intrinsics. Takes the required alignment of the + coroutine frame, a pointer to the coroutine promise (or null if none), + the address of the coroutine function itself, and an opaque pointer used + by the frontend to convey additional information to the coroutine + lowering passes (or null). + + The result is a token that must be passed to every other + `coro.intrinsic.*` operation associated with this coroutine + (`coro.alloc`, `coro.begin`, `coro.free`, etc.), tying them all to the + same coroutine instance. + }]; +} + +//===----------------------------------------------------------------------===// +// Coroutine intrinsic AllocOp +//===----------------------------------------------------------------------===// + +def CIR_CoroIntrinsicAllocOp : CIR_CoroIntrinsicOp<"alloc", + (ins CIR_AnyIntType:$id), + (outs CIR_AnyBoolType:$result)> { + let summary = "Represents llvm.coro.alloc"; + let description = [{ + Queries whether the coroutine identified by `id` needs a dynamically + allocated frame. Returns `true` if the coroutine frame must be allocated, + or `false` otherwise. + }]; +} + +// TODO: Replace the `id` operand with MLIR's `token` type once it becomes +// available. This operand corresponds to the token produced by `llvm.coro.id`. +//===----------------------------------------------------------------------===// +// Coroutine intrinsic BeginOp +//===----------------------------------------------------------------------===// +def CIR_CoroIntrinsicBeginOp : CIR_CoroIntrinsicOp<"begin", + (ins CIR_UInt32:$id, CIR_VoidPtrType:$coroframeAddr), + (outs CIR_VoidPtrType:$result)> { + let summary = "Represents llvm.coro.begin"; + let description = [{ + Initializes the coroutine frame using `coroframeAddr`. `id` is the token + from `coro.intrinsic.id`, and `coroframeAddr` points to the memory used + for the coroutine frame. Returns the coroutine handle. + }]; +} + +//===----------------------------------------------------------------------===// +// Coroutine intrinsic EndOp +//===----------------------------------------------------------------------===// +def CIR_CoroIntrinsicEndOp : CIR_CoroIntrinsicOp<"end", + (ins CIR_VoidPtrType:$handle, CIR_AnyBoolType:$unwind), + (outs CIR_AnyBoolType:$result)> { + let summary = "Represents llvm.coro.end"; + let description = [{ + Marks a point at which a coroutine must be suspended or destroyed for the + last time, e.g. right before the coroutine returns control to its caller + for the final time, or along an exceptional unwind path. `handle` is the + coroutine handle produced by `coro.intrinsic.begin`, and `unwind` + indicates whether this occurrence of `coro.intrinsic.end` lies on the + unwind path (`true`) or the normal control-flow path (`false`). + }]; +} + +// TODO: Replace the `id` operand with MLIR's `token` type once it becomes +// available. This operand corresponds to the token produced by `llvm.coro.id`. +//===----------------------------------------------------------------------===// +// Coroutine intrinsic FreeOp +//===----------------------------------------------------------------------===// +def CIR_CoroIntrinsicFreeOp : CIR_CoroIntrinsicOp<"free", + (ins CIR_AnyIntType:$id, CIR_VoidPtrType:$coroframe), + (outs CIR_VoidPtrType:$result)> { + let summary = "Represents llvm.coro.free"; + let description = [{ + Given the coroutine identified by `id` and its frame pointer `coroframe` + (the handle from `coro.intrinsic.begin`), returns the pointer that must + be passed to the deallocation function to free the coroutine frame, or a + null pointer if the coroutine frame was not dynamically allocated. + }]; +} + +//===----------------------------------------------------------------------===// +// Coroutine intrinsic SizeOp +//===----------------------------------------------------------------------===// +def CIR_CoroIntrinsicSizeOp : CIR_CoroIntrinsicOp<"size", + (ins), (outs CIR_UInt64:$result)> { + let summary = "Represents llvm.coro.size"; + let description = [{ + Returns the size, in bytes, of the coroutine frame. + }]; +} + +//===----------------------------------------------------------------------===// +// Coroutine intrinsic IsInRampOp +//===----------------------------------------------------------------------===// + +def CIR_CoroIntrinsicIsInRampOp : CIR_CoroIntrinsicOp<"is_in_ramp", + (ins), (outs CIR_AnyBoolType:$result), [Pure]> { + let summary = ""; +} //===----------------------------------------------------------------------===// // CopyOp diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index d67134583d48a..36bdefc79a2c8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -1361,34 +1361,54 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, return emitRotate(e, /*isRotateLeft=*/false); case Builtin::BI__builtin_coro_id: + return RValue::get(emitCoroIDBuiltinCall(e).getResult()); + case Builtin::BI__builtin_coro_alloc: { + cir::CoroIntrinsicAllocOp coroAlloc = emitCoroAllocBuiltinCall(e); + return coroAlloc ? RValue::get(coroAlloc.getResult()) + : getUndefRValue(e->getType()); + } + case Builtin::BI__builtin_coro_begin: { + cir::CoroIntrinsicBeginOp coroBeg = emitCoroBeginBuiltinCall(e); + return coroBeg ? RValue::get(coroBeg.getResult()) + : getUndefRValue(e->getType()); + } + case Builtin::BI__builtin_coro_promise: + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_promise NYI"); + return getUndefRValue(e->getType()); case Builtin::BI__builtin_coro_resume: + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_resume NYI"); + return getUndefRValue(e->getType()); case Builtin::BI__builtin_coro_noop: + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_noop NYI"); + return getUndefRValue(e->getType()); case Builtin::BI__builtin_coro_destroy: + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_destroy NYI"); + return getUndefRValue(e->getType()); case Builtin::BI__builtin_coro_done: - case Builtin::BI__builtin_coro_alloc: - case Builtin::BI__builtin_coro_begin: + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_done NYI"); + return getUndefRValue(e->getType()); case Builtin::BI__builtin_coro_end: + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_end NYI"); + return getUndefRValue(e->getType()); case Builtin::BI__builtin_coro_suspend: + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_suspend NYI"); + return getUndefRValue(e->getType()); case Builtin::BI__builtin_coro_align: - cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_id like NYI"); + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_align NYI"); return getUndefRValue(e->getType()); case Builtin::BI__builtin_coro_frame: { return emitCoroutineFrame(); } - case Builtin::BI__builtin_coro_free: - return RValue::get(emitCoroFreeBuiltin(e).getResult()); + case Builtin::BI__builtin_coro_free: { + cir::CoroIntrinsicFreeOp coroFree = emitCoroFreeBuiltin(e); + return coroFree ? RValue::get(coroFree.getResult()) + : getUndefRValue(e->getType()); + } + case Builtin::BI__builtin_coro_size: { - 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); + return RValue::get(emitCoroSizeBuiltinCall(e).getResult()); } case Builtin::BI__builtin_constant_p: { diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp index 3dd71a8ad3b6c..2aa199c347152 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp @@ -28,7 +28,7 @@ struct clang::CIRGen::CGCoroData { cir::AwaitKind currentAwaitKind = cir::AwaitKind::Init; // 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; + cir::CoroIntrinsicIdOp coroId = nullptr; // Stores the result of __builtin_coro_begin call. mlir::Value coroBegin = nullptr; @@ -42,13 +42,18 @@ struct clang::CIRGen::CGCoroData { // Stores the last emitted coro.free for the deallocate expressions, we use it // to wrap dealloc code with if(auto mem = coro.free) dealloc(mem). - cir::CallOp lastCoroFree = nullptr; + cir::CoroIntrinsicFreeOp lastCoroFree = nullptr; // A temporary bool alloca that stores whether 'await_resume' threw an // exception. If it did, 'true' is stored in this variable, and the coroutine // body must be skipped. If the promise type does not define an exception // handler, this is null. Address resumeEHVar = Address::invalid(); + // + // If coro.id came from the builtin, remember the expression to give better + // diagnostic. If CoroIdExpr is nullptr, the coro.id was created by + // EmitCoroutineBody. + CallExpr const *coroIdExpr = nullptr; }; // Defining these here allows to keep CGCoroData private to this file. @@ -139,7 +144,7 @@ struct CallCoroDelete final : public EHScopeStack::Cleanup { } CIRGenBuilderTy &builder = cgf.getBuilder(); - cir::CallOp coroFree = cgf.curCoro.data->lastCoroFree; + cir::CoroIntrinsicFreeOp coroFree = cgf.curCoro.data->lastCoroFree; if (!coroFree) { cgf.cgm.error(deallocate->getBeginLoc(), @@ -182,11 +187,22 @@ RValue CIRGenFunction::emitCoroutineFrame() { static void createCoroData(CIRGenFunction &cgf, CIRGenFunction::CGCoroInfo &curCoro, - cir::CallOp coroId) { - assert(!curCoro.data && "EmitCoroutineBodyStatement called twice?"); + cir::CoroIntrinsicIdOp coroId, + CallExpr const *coroIdExpr = nullptr) { + + if (curCoro.data) { + if (curCoro.data->coroIdExpr) + cgf.cgm.error(coroIdExpr->getBeginLoc(), + "only one __builtin_coro_id can be used in a function"); + else + llvm_unreachable("EmitCoroutineBodyStatement called twice?"); + + return; + } curCoro.data = std::make_unique<CGCoroData>(); curCoro.data->coroId = coroId; + curCoro.data->coroIdExpr = coroIdExpr; } static mlir::LogicalResult @@ -212,113 +228,84 @@ emitBodyAndFallthrough(CIRGenFunction &cgf, const CoroutineBodyStmt &s, return mlir::success(); } -cir::CallOp CIRGenFunction::emitCoroIDBuiltinCall(mlir::Location loc, - mlir::Value nullPtr) { - cir::IntType int32Ty = builder.getUInt32Ty(); - - const TargetInfo &ti = cgm.getASTContext().getTargetInfo(); - unsigned newAlign = ti.getNewAlign() / ti.getCharWidth(); +cir::CoroIntrinsicIdOp +CIRGenFunction::emitCoroIDBuiltinCall(const CallExpr *e) { + mlir::Location loc = getLoc(e->getBeginLoc()); - mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroId); - - cir::FuncOp fnOp; - if (!builtin) { - fnOp = cgm.createCIRBuiltinFunction( - loc, cgm.builtinCoroId, - cir::FuncType::get({int32Ty, voidPtrTy, voidPtrTy, voidPtrTy}, int32Ty), - /*FD=*/nullptr); - assert(fnOp && "should always succeed"); - } else { - fnOp = cast<cir::FuncOp>(builtin); + llvm::SmallVector<mlir::Value, 4> args; + for (auto const *arg : e->arguments()) { + args.push_back(emitScalarExpr(arg)); } - - return builder.createCallOp(loc, fnOp, - mlir::ValueRange{builder.getUInt32(newAlign, loc), - nullPtr, nullPtr, nullPtr}); + auto coroId = cir::CoroIntrinsicIdOp::create(cgm.getBuilder(), loc, args); + createCoroData(*this, curCoro, coroId, e); + return coroId; } -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); +cir::CoroIntrinsicAllocOp +CIRGenFunction::emitCoroAllocBuiltinCall(const CallExpr *e) { + mlir::Location loc = getLoc(e->getBeginLoc()); + if (!curCoro.data || !curCoro.data->coroId) { + cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has" + " been used earlier in this function"); + return {}; } - return builder.createCallOp( - loc, fnOp, mlir::ValueRange{curCoro.data->coroId.getResult()}); + return cir::CoroIntrinsicAllocOp::create( + cgm.getBuilder(), loc, + 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}); -} +cir::CoroIntrinsicBeginOp +CIRGenFunction::emitCoroBeginBuiltinCall(const CallExpr *e) { -cir::CallOp CIRGenFunction::emitCoroEndBuiltinCall(mlir::Location loc, - mlir::Value nullPtr) { - cir::BoolType boolTy = builder.getBoolTy(); - mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroEnd); - - cir::FuncOp fnOp; - if (!builtin) { - fnOp = cgm.createCIRBuiltinFunction( - loc, cgm.builtinCoroEnd, - cir::FuncType::get({voidPtrTy, boolTy}, boolTy), - /*fd=*/nullptr); - assert(fnOp && "should always succeed"); - } else { - fnOp = cast<cir::FuncOp>(builtin); + mlir::Location loc = getLoc(e->getBeginLoc()); + if (!curCoro.data || !curCoro.data->coroId) { + cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has" + " been used earlier in this function"); + return {}; + } + llvm::SmallVector<mlir::Value, 2> args; + args.push_back(curCoro.data->coroId.getResult()); + for (auto const *arg : e->arguments()) { + args.push_back(emitScalarExpr(arg)); } + auto coroBegin = + cir::CoroIntrinsicBeginOp::create(cgm.getBuilder(), loc, args); + curCoro.data->coroBegin = coroBegin; + return coroBegin; +} - return builder.createCallOp( - loc, fnOp, mlir::ValueRange{nullPtr, builder.getBool(false, loc)}); +cir::CoroIntrinsicEndOp +CIRGenFunction::emitCoroEndBuiltinCall(mlir::Location loc, + mlir::Value nullPtr) { + return cir::CoroIntrinsicEndOp::create( + cgm.getBuilder(), loc, + mlir::ValueRange{nullPtr, builder.getBool(false, loc)}); } -cir::CallOp CIRGenFunction::emitCoroFreeBuiltin(const CallExpr *e) { - mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroFree); +cir::CoroIntrinsicFreeOp +CIRGenFunction::emitCoroFreeBuiltin(const CallExpr *e) { mlir::Location loc = getLoc(e->getBeginLoc()); - cir::FuncOp fnOp; - if (!builtin) { - fnOp = cgm.createCIRBuiltinFunction( - loc, cgm.builtinCoroFree, - cir::FuncType::get({uInt32Ty, voidPtrTy}, voidPtrTy), - /*fd=*/nullptr); - assert(fnOp && "should always succeed"); - } else { - fnOp = cast<cir::FuncOp>(builtin); + if (!curCoro.data || !curCoro.data->coroId) { + cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has" + " been used earlier in this function"); + return {}; } - cir::CallOp coroFree = - builder.createCallOp(loc, fnOp, - mlir::ValueRange{curCoro.data->coroId.getResult(), - curCoro.data->coroBegin}); - - curCoro.data->lastCoroFree = coroFree; + auto coroFree = cir::CoroIntrinsicFreeOp::create( + cgm.getBuilder(), loc, + mlir::ValueRange{curCoro.data->coroId.getResult(), + curCoro.data->coroBegin}); + if (curCoro.data) + curCoro.data->lastCoroFree = coroFree; return coroFree; } +cir::CoroIntrinsicSizeOp +CIRGenFunction::emitCoroSizeBuiltinCall(const CallExpr *e) { + mlir::Location loc = getLoc(e->getBeginLoc()); + return cir::CoroIntrinsicSizeOp::create(cgm.getBuilder(), loc); +} + static mlir::LogicalResult coroutineBodyExceptionHelper(CIRGenFunction &cgf, const CoroutineBodyStmt &s) { @@ -348,12 +335,20 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) { auto fn = mlir::cast<cir::FuncOp>(curFn); fn.setCoroutine(true); - cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst); + const TargetInfo &ti = cgm.getASTContext().getTargetInfo(); + unsigned newAlign = ti.getNewAlign() / ti.getCharWidth(); + + cir::CoroIntrinsicIdOp coroId = cir::CoroIntrinsicIdOp::create( + cgm.getBuilder(), openCurlyLoc, + mlir::ValueRange{builder.getUInt32(newAlign, openCurlyLoc), nullPtrCst, + nullPtrCst, nullPtrCst}); createCoroData(*this, curCoro, coroId); // Backend is allowed to elide memory allocations, to help it, emit // auto mem = coro.alloc() ? 0 : ... allocation code ...; - cir::CallOp coroAlloc = emitCoroAllocBuiltinCall(openCurlyLoc); + cir::CoroIntrinsicAllocOp coroAlloc = cir::CoroIntrinsicAllocOp::create( + cgm.getBuilder(), openCurlyLoc, + mlir::ValueRange{curCoro.data->coroId.getResult()}); // Initialize address of coroutine frame to null CanQualType astVoidPtrTy = cgm.getASTContext().VoidPtrTy; @@ -373,11 +368,11 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) { loc, emitScalarExpr(s.getAllocate()), storeAddr); cir::YieldOp::create(builder, loc); }); - curCoro.data->coroBegin = - emitCoroBeginBuiltinCall( - openCurlyLoc, - cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr)) - .getResult(); + curCoro.data->coroBegin = cir::CoroIntrinsicBeginOp::create( + cgm.getBuilder(), openCurlyLoc, + mlir::ValueRange{ + curCoro.data->coroId.getResult(), + cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr)}); // Handle allocation failure if 'ReturnStmtOnAllocFailure' was provided. if (s.getReturnStmtOnAllocFailure()) @@ -511,9 +506,10 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) { } } } - - emitCoroEndBuiltinCall( - openCurlyLoc, builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc)); + cir::CoroIntrinsicEndOp::create( + cgm.getBuilder(), openCurlyLoc, + mlir::ValueRange{builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc), + builder.getBool(false, openCurlyLoc)}); if (auto *ret = cast_or_null<ReturnStmt>(s.getReturnStmt())) { // Since we already emitted the return value above, so we shouldn't // emit it again here. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index d88f38eefa99a..e28df42eb08a9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1862,13 +1862,14 @@ class CIRGenFunction : public CIRGenTypeCache { void emitConstructorBody(FunctionArgList &args); 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); - - cir::CallOp emitCoroFreeBuiltin(const CallExpr *e); + cir::CoroIntrinsicEndOp emitCoroEndBuiltinCall(mlir::Location loc, + mlir::Value nullPtr); + cir::CoroIntrinsicIdOp emitCoroIDBuiltinCall(const CallExpr *e); + cir::CoroIntrinsicAllocOp emitCoroAllocBuiltinCall(const CallExpr *e); + cir::CoroIntrinsicBeginOp emitCoroBeginBuiltinCall(const CallExpr *e); + + cir::CoroIntrinsicSizeOp emitCoroSizeBuiltinCall(const CallExpr *e); + cir::CoroIntrinsicFreeOp emitCoroFreeBuiltin(const CallExpr *e); RValue emitCoroutineFrame(); void emitDestroy(Address addr, QualType type, Destroyer *destroyer); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index f5ac70a33aef5..49fa7a236763a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -815,12 +815,6 @@ class CIRGenModule : public CIRGenTypeCache { bool isLocal = false, 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"; - static constexpr const char *builtinCoroEnd = "__builtin_coro_end"; - static constexpr const char *builtinCoroFree = "__builtin_coro_free"; - /// Given a builtin id for a function like "__builtin_fabsf", return a /// Function* for "fabsf". cir::FuncOp getBuiltinLibFunction(const FunctionDecl *fd, unsigned builtinID); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 979aed513f595..ce1e442be40b7 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -5167,6 +5167,48 @@ mlir::LogicalResult CIRToLLVMIndirectBrOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMCoroIntrinsicIsInRampOpLowering::matchAndRewrite( + cir::CoroIntrinsicIsInRampOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return mlir::failure(); +} + +mlir::LogicalResult CIRToLLVMCoroIntrinsicFreeOpLowering::matchAndRewrite( + cir::CoroIntrinsicFreeOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return mlir::failure(); +} + +mlir::LogicalResult CIRToLLVMCoroIntrinsicEndOpLowering::matchAndRewrite( + cir::CoroIntrinsicEndOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return mlir::failure(); +} + +mlir::LogicalResult CIRToLLVMCoroIntrinsicAllocOpLowering::matchAndRewrite( + cir::CoroIntrinsicAllocOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return mlir::failure(); +} + +mlir::LogicalResult CIRToLLVMCoroIntrinsicBeginOpLowering::matchAndRewrite( + cir::CoroIntrinsicBeginOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return mlir::failure(); +} + +mlir::LogicalResult CIRToLLVMCoroIntrinsicIdOpLowering::matchAndRewrite( + cir::CoroIntrinsicIdOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return mlir::failure(); +} + +mlir::LogicalResult CIRToLLVMCoroIntrinsicSizeOpLowering::matchAndRewrite( + cir::CoroIntrinsicSizeOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + return mlir::failure(); +} + mlir::LogicalResult CIRToLLVMCpuIdOpLowering::matchAndRewrite( cir::CpuIdOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/test/CIR/CodeGen/coro-builtins-err.cpp b/clang/test/CIR/CodeGen/coro-builtins-err.cpp new file mode 100644 index 0000000000000..ffbbd706b61bf --- /dev/null +++ b/clang/test/CIR/CodeGen/coro-builtins-err.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines -fclangir -emit-cir %s -o %t.cir -verify + +void a(void) { + __builtin_coro_alloc(); // expected-error {{this builtin expect that __builtin_coro_id has been used earlier in this function}} + __builtin_coro_begin(0); // expected-error {{this builtin expect that __builtin_coro_id has been used earlier in this function}} + __builtin_coro_free(0); // expected-error {{this builtin expect that __builtin_coro_id has been used earlier in this function}} + + __builtin_coro_id(32, 0, 0, 0); + __builtin_coro_id(32, 0, 0, 0); // expected-error {{only one __builtin_coro_id}} +} diff --git a/clang/test/CIR/CodeGen/coro-builtins.cpp b/clang/test/CIR/CodeGen/coro-builtins.cpp new file mode 100644 index 0000000000000..f92f4d996c460 --- /dev/null +++ b/clang/test/CIR/CodeGen/coro-builtins.cpp @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR + +void *myAlloc(long long); + +// CIR: cir.func {{.*}} @_Z1fi +void f(int n) { + int promise; + // CIR: %[[ADDR:.*]] = cir.alloca "n" + // CIR: %[[PROMISE:.*]] = cir.alloca "promise" + + __builtin_coro_id(32, &promise, 0, 0); + // CIR: %[[CORO_ID_ALIGN:.*]] = cir.const #cir.int<32> + // CIR: %[[CAS_PROM:.*]] = cir.cast bitcast %[[PROMISE]] + // CIR: %[[COROID:.*]] = cir.coro.intrinsic.id(%[[CORO_ID_ALIGN]], %[[CAS_PROM]], {{.*}}, {{.*}}) + + __builtin_coro_alloc(); + // CIR: cir.coro.intrinsic.alloc(%[[COROID]]) + + // TODO + //__builtin_coro_noop(); + + __builtin_coro_begin(myAlloc(__builtin_coro_size())); + // TODO(CIR): Support both variants of the coroutine size intrinsic, matching + // `llvm.coro.size.i32` and `llvm.coro.size.i64`. + // CIR: %[[SIZE:.*]] = cir.coro.intrinsic.size() + // CIR: %[[CAST_SIZE:.*]] = cir.cast integral %[[SIZE]] : !u64i -> !s64i + // CIR: %[[MEM:.*]] = cir.call @_Z7myAllocx(%[[CAST_SIZE]]) + // CIR: %[[FRAME:.*]] = cir.coro.intrinsic.begin(%[[COROID]], %[[MEM]]) + + // TODO(CIR): + //__builtin_coro_resume(__builtin_coro_frame()); + + // TODO(CIR): + //__builtin_coro_destroy(__builtin_coro_frame()); + + // TODO(CIR): + //__builtin_coro_done(__builtin_coro_frame()); + + // TODO(CIR): + //__builtin_coro_promise(__builtin_coro_frame(), 48, 0); + + __builtin_coro_free(__builtin_coro_frame()); + // CIR: cir.coro.intrinsic.free(%[[COROID]], %[[FRAME]]) + + // TODO(CIR): + //__builtin_coro_end(__builtin_coro_frame(), 0); + + // TODO(CIR): + //__builtin_coro_suspend(1); +} diff --git a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp index afaf66b8a8d06..fb088d110d0d8 100644 --- a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp +++ b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp @@ -22,11 +22,6 @@ // CIR: module {{.*}} { // 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>; VoidTask silly_task() { @@ -49,22 +44,21 @@ VoidTask silly_task() { // 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]]) - +// CIR: %[[CoroId:.*]] = cir.coro.intrinsic.id(%[[Align]], %[[NullPtr]], %[[NullPtr]], %[[NullPtr]]) : (!u32i, !cir.ptr<!void>, !cir.ptr<!void>, !cir.ptr<!void>) -> !u32i // OGCG: %[[CoroId:.*]] = call token @llvm.coro.id(i32 16, ptr %[[VoidPromisseAddr]], ptr null, ptr null) // 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: %[[ShouldAlloc:.*]] = cir.coro.intrinsic.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 {llvm.noundef}) +// CIR: %[[CoroSize:.*]] = cir.coro.intrinsic.size() : () -> !u64i // CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) {allocsize = array<i32: 0>} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef}) // 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]]) +// CIR: %[[CoroFrameAddr:.*]] = cir.coro.intrinsic.begin(%[[CoroId]], %[[Load0]]) : (!u32i, !cir.ptr<!void>) -> !cir.ptr<!void> // OGCG: %[[ShouldAlloc:.*]] = call i1 @llvm.coro.alloc(token %[[CoroId]]) // OGCG: br i1 %[[ShouldAlloc]], label %coro.alloc, label %coro.init @@ -196,11 +190,11 @@ VoidTask silly_task() { // The `if` ensures we only call delete on non-null. // CIR: } cleanup normal { -// CIR: %[[FreeMem:.*]] = cir.call @__builtin_coro_free(%[[CoroId]], %[[CoroFrameAddr]]) +// CIR: %[[FreeMem:.*]] = cir.coro.intrinsic.free(%[[CoroId]], %[[CoroFrameAddr]]) : (!u32i, !cir.ptr<!void>) -> !cir.ptr<!void> // CIR: %[[NullPtr2:.*]] = cir.const #cir.ptr<null> // CIR: %[[Cond:.*]] = cir.cmp ne %[[FreeMem]], %[[NullPtr2]] // CIR: cir.if %[[Cond]] { -// CIR: %[[Size:.*]] = cir.call @__builtin_coro_size() +// CIR: %[[Size:.*]] = cir.coro.intrinsic.size() // CIR: cir.call @_ZdlPvm(%[[FreeMem]], %[[Size]]) // CIR: } // CIR: cir.yield @@ -220,7 +214,7 @@ VoidTask silly_task() { // CIR: %[[CoroEndArg0:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void> // CIR: %[[CoroEndArg1:.*]] = cir.const #false -// CIR: = cir.call @__builtin_coro_end(%[[CoroEndArg0]], %[[CoroEndArg1]]) +// CIR: = cir.coro.intrinsic.end(%[[CoroEndArg0]], %[[CoroEndArg1]]) : (!cir.ptr<!void>, !cir.bool) -> !cir.bool // CIR: %[[Tmp1:.*]] = cir.load{{.*}} %[[VoidTaskAddr]] // CIR: cir.return %[[Tmp1]] @@ -408,7 +402,7 @@ folly::coro::Task<void> yield1() { // CIR: cir.yield // CIR: } cleanup normal { // CIR: } -// CIR: = cir.call @__builtin_coro_end(%{{.*}}, %{{.*}}){{.*}} +// CIR: = cir.coro.intrinsic.end(%{{.*}}, %{{.*}}) // CIR: %[[RETLOAD:.*]] = cir.load{{.*}} %[[RETVAL]] // CIR: cir.return %[[RETLOAD]] // CIR: } >From 0115a8b040d38d7cb37c2ea8a2554b4cb0e2bc1f Mon Sep 17 00:00:00 2001 From: Andres Salamanca <[email protected]> Date: Sun, 26 Jul 2026 19:16:04 -0500 Subject: [PATCH 2/3] Address some review comments --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 22 ++++++++------------ clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 14 +++++++------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index e62bc98c4a341..3e0e145ba858d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4717,6 +4717,7 @@ class CIR_CoroIntrinsicOp<string mnem, dag ins, dag outs, `(` operands `)` `:` functional-type(operands, results) attr-dict }]; } + //===----------------------------------------------------------------------===// // Coroutine intrinsic IdOp //===----------------------------------------------------------------------===// @@ -4758,11 +4759,12 @@ def CIR_CoroIntrinsicAllocOp : CIR_CoroIntrinsicOp<"alloc", }]; } -// TODO: Replace the `id` operand with MLIR's `token` type once it becomes -// available. This operand corresponds to the token produced by `llvm.coro.id`. //===----------------------------------------------------------------------===// // Coroutine intrinsic BeginOp //===----------------------------------------------------------------------===// + +// TODO: Replace the `id` operand with MLIR's `token` type once it becomes +// available. This operand corresponds to the token produced by `llvm.coro.id`. def CIR_CoroIntrinsicBeginOp : CIR_CoroIntrinsicOp<"begin", (ins CIR_UInt32:$id, CIR_VoidPtrType:$coroframeAddr), (outs CIR_VoidPtrType:$result)> { @@ -4777,6 +4779,7 @@ def CIR_CoroIntrinsicBeginOp : CIR_CoroIntrinsicOp<"begin", //===----------------------------------------------------------------------===// // Coroutine intrinsic EndOp //===----------------------------------------------------------------------===// + def CIR_CoroIntrinsicEndOp : CIR_CoroIntrinsicOp<"end", (ins CIR_VoidPtrType:$handle, CIR_AnyBoolType:$unwind), (outs CIR_AnyBoolType:$result)> { @@ -4791,11 +4794,12 @@ def CIR_CoroIntrinsicEndOp : CIR_CoroIntrinsicOp<"end", }]; } -// TODO: Replace the `id` operand with MLIR's `token` type once it becomes -// available. This operand corresponds to the token produced by `llvm.coro.id`. //===----------------------------------------------------------------------===// // Coroutine intrinsic FreeOp //===----------------------------------------------------------------------===// + +// TODO: Replace the `id` operand with MLIR's `token` type once it becomes +// available. This operand corresponds to the token produced by `llvm.coro.id`. def CIR_CoroIntrinsicFreeOp : CIR_CoroIntrinsicOp<"free", (ins CIR_AnyIntType:$id, CIR_VoidPtrType:$coroframe), (outs CIR_VoidPtrType:$result)> { @@ -4811,6 +4815,7 @@ def CIR_CoroIntrinsicFreeOp : CIR_CoroIntrinsicOp<"free", //===----------------------------------------------------------------------===// // Coroutine intrinsic SizeOp //===----------------------------------------------------------------------===// + def CIR_CoroIntrinsicSizeOp : CIR_CoroIntrinsicOp<"size", (ins), (outs CIR_UInt64:$result)> { let summary = "Represents llvm.coro.size"; @@ -4819,15 +4824,6 @@ def CIR_CoroIntrinsicSizeOp : CIR_CoroIntrinsicOp<"size", }]; } -//===----------------------------------------------------------------------===// -// Coroutine intrinsic IsInRampOp -//===----------------------------------------------------------------------===// - -def CIR_CoroIntrinsicIsInRampOp : CIR_CoroIntrinsicOp<"is_in_ramp", - (ins), (outs CIR_AnyBoolType:$result), [Pure]> { - let summary = ""; -} - //===----------------------------------------------------------------------===// // CopyOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp index 2aa199c347152..ed54661aa3ef7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp @@ -233,9 +233,9 @@ CIRGenFunction::emitCoroIDBuiltinCall(const CallExpr *e) { mlir::Location loc = getLoc(e->getBeginLoc()); llvm::SmallVector<mlir::Value, 4> args; - for (auto const *arg : e->arguments()) { + for (const Expr *arg : e->arguments()) args.push_back(emitScalarExpr(arg)); - } + auto coroId = cir::CoroIntrinsicIdOp::create(cgm.getBuilder(), loc, args); createCoroData(*this, curCoro, coroId, e); return coroId; @@ -266,9 +266,9 @@ CIRGenFunction::emitCoroBeginBuiltinCall(const CallExpr *e) { } llvm::SmallVector<mlir::Value, 2> args; args.push_back(curCoro.data->coroId.getResult()); - for (auto const *arg : e->arguments()) { + for (const Expr *arg : e->arguments()) args.push_back(emitScalarExpr(arg)); - } + auto coroBegin = cir::CoroIntrinsicBeginOp::create(cgm.getBuilder(), loc, args); curCoro.data->coroBegin = coroBegin; @@ -286,17 +286,19 @@ CIRGenFunction::emitCoroEndBuiltinCall(mlir::Location loc, cir::CoroIntrinsicFreeOp CIRGenFunction::emitCoroFreeBuiltin(const CallExpr *e) { mlir::Location loc = getLoc(e->getBeginLoc()); + if (!curCoro.data || !curCoro.data->coroId) { cgm.error(e->getBeginLoc(), "this builtin expect that __builtin_coro_id has" " been used earlier in this function"); return {}; } + auto coroFree = cir::CoroIntrinsicFreeOp::create( cgm.getBuilder(), loc, mlir::ValueRange{curCoro.data->coroId.getResult(), curCoro.data->coroBegin}); - if (curCoro.data) - curCoro.data->lastCoroFree = coroFree; + + curCoro.data->lastCoroFree = coroFree; return coroFree; } >From e68bd7f774219140c5831519080359922e461bd4 Mon Sep 17 00:00:00 2001 From: Andres Salamanca <[email protected]> Date: Sun, 26 Jul 2026 21:40:42 -0500 Subject: [PATCH 3/3] Fix after rebase. --- clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index ce1e442be40b7..ce0cc08e30c17 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -5167,12 +5167,6 @@ mlir::LogicalResult CIRToLLVMIndirectBrOpLowering::matchAndRewrite( return mlir::success(); } -mlir::LogicalResult CIRToLLVMCoroIntrinsicIsInRampOpLowering::matchAndRewrite( - cir::CoroIntrinsicIsInRampOp op, OpAdaptor adaptor, - mlir::ConversionPatternRewriter &rewriter) const { - return mlir::failure(); -} - mlir::LogicalResult CIRToLLVMCoroIntrinsicFreeOpLowering::matchAndRewrite( cir::CoroIntrinsicFreeOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
