llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: None (Andres-Salamanca) <details> <summary>Changes</summary> This PR adds new `FuncOp` attributes (`coroutine` and `builtin`) and begins the implementation of the `emitCoroutineBody` function. Feature markers were also added for guidance in future PRs. --- Full diff: https://github.com/llvm/llvm-project/pull/161616.diff 12 Files Affected: - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+17) - (modified) clang/include/clang/CIR/MissingFeatures.h (+7) - (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+25) - (added) clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp (+85) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+3) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+18) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+9) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+7) - (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+1) - (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+1) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+13) - (added) clang/test/CIR/CodeGen/coro-task.cpp (+123) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index bb394440bf8d8..c19b90d30bf94 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2326,6 +2326,12 @@ def CIR_FuncOp : CIR_Op<"func", [ The function linkage information is specified by `linkage`, as defined by `GlobalLinkageKind` attribute. + A compiler builtin function must be marked as `builtin` for further + processing when lowering from CIR. + + The `coroutine` keyword is used to mark coroutine function, which requires + at least one `cir.await` instruction to be used in its body. + The `lambda` translates to a C++ `operator()` that implements a lambda, this allow callsites to make certain assumptions about the real function nature when writing analysis. @@ -2347,11 +2353,22 @@ def CIR_FuncOp : CIR_Op<"func", [ // Linkage information cir.func linkonce_odr @some_method(...) ``` + // Builtin function + cir.func builtin @__builtin_coro_end(!cir.ptr<i8>, !cir.bool) -> !cir.bool + // Coroutine + cir.func coroutine @_Z10silly_taskv() -> !CoroTask { + ... + cir.await(...) + ... + } + ``` }]; let arguments = (ins SymbolNameAttr:$sym_name, CIR_VisibilityAttr:$global_visibility, TypeAttrOf<CIR_FuncType>:$function_type, + UnitAttr:$builtin, + UnitAttr:$coroutine, UnitAttr:$lambda, UnitAttr:$no_proto, UnitAttr:$dso_local, diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 0fac1b211239a..73beeacce5de2 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -135,6 +135,13 @@ struct MissingFeatures { static bool recordZeroInitPadding() { return false; } 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; } + // Various handling of deferred processing in CIRGenModule. static bool cgmRelease() { return false; } static bool deferredVtables() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index cf17de144f4d9..512e1b2aa0e86 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -428,6 +428,31 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, return emitUnaryFPBuiltin<cir::ATanOp>(*this, *e); case Builtin::BI__builtin_elementwise_cos: return emitUnaryFPBuiltin<cir::CosOp>(*this, *e); + case Builtin::BI__builtin_coro_id: + case Builtin::BI__builtin_coro_promise: + case Builtin::BI__builtin_coro_resume: + case Builtin::BI__builtin_coro_noop: + case Builtin::BI__builtin_coro_destroy: + case Builtin::BI__builtin_coro_done: + case Builtin::BI__builtin_coro_alloc: + case Builtin::BI__builtin_coro_begin: + case Builtin::BI__builtin_coro_end: + case Builtin::BI__builtin_coro_suspend: + case Builtin::BI__builtin_coro_align: + llvm_unreachable("BI__builtin_coro_id like NYI"); + + case Builtin::BI__builtin_coro_frame: { + cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_frame NYI"); + assert(!cir::MissingFeatures::coroutineFrame()); + return getUndefRValue(e->getType()); + } + 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()); + } } // If this is an alias for a lib function (e.g. __builtin_sin), emit diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp new file mode 100644 index 0000000000000..ee43c221c3cd6 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp @@ -0,0 +1,85 @@ +//===----- CGCoroutine.cpp - Emit CIR Code for C++ coroutines -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains code dealing with C++ code generation of coroutines. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenFunction.h" +#include "mlir/Support/LLVM.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" + +using namespace clang; +using namespace clang::CIRGen; + +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; +}; + +// Defining these here allows to keep CGCoroData private to this file. +CIRGenFunction::CGCoroInfo::CGCoroInfo() {} +CIRGenFunction::CGCoroInfo::~CGCoroInfo() {} + +static void createCoroData(CIRGenFunction &cgf, + CIRGenFunction::CGCoroInfo &curCoro, + cir::CallOp coroId) { + if (curCoro.data) { + llvm_unreachable("EmitCoroutineBodyStatement called twice?"); + return; + } + + curCoro.data = std::unique_ptr<CGCoroData>(new CGCoroData); + curCoro.data->coroId = coroId; +} + +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(); + + 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); + + return builder.createCallOp(loc, fnOp, + mlir::ValueRange{builder.getUInt32(newAlign, loc), + nullPtr, nullPtr, nullPtr}); +} + +mlir::LogicalResult +CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) { + mlir::Location openCurlyLoc = getLoc(s.getBeginLoc()); + cir::ConstantOp nullPtrCst = builder.getNullPtr(VoidPtrTy, openCurlyLoc); + + cir::FuncOp fn = curFn; + assert(fn && "other callables are NYI"); + fn.setCoroutine(true); + cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst); + createCoroData(*this, curCoro, coroId); + + assert(!cir::MissingFeatures::coroAllocBuiltinCall()); + + assert(!cir::MissingFeatures::coroBeginBuiltinCall()); + + assert(!cir::MissingFeatures::generateDebugInfo()); + return mlir::success(); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 0abb21a670719..f435979801af9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -342,6 +342,9 @@ void CIRGenFunction::LexicalScope::cleanup() { cir::ReturnOp CIRGenFunction::LexicalScope::emitReturn(mlir::Location loc) { CIRGenBuilderTy &builder = cgf.getBuilder(); + // If we are on a coroutine, add the coro_end builtin call. + assert(!cir::MissingFeatures::coroEndBuiltinCall()); + if (!cgf.curFn.getFunctionType().hasVoidReturn()) { // Load the value from `__retval` and return it via the `cir.return` op. auto value = builder.create<cir::LoadOp>( diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 166435f9e7e9e..ae8d14b1e98e5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -47,6 +47,8 @@ class LoopOp; namespace clang::CIRGen { +struct CGCoroData; + class CIRGenFunction : public CIRGenTypeCache { public: CIRGenModule &cgm; @@ -66,6 +68,18 @@ class CIRGenFunction : public CIRGenTypeCache { /// The compiler-generated variable that holds the return value. std::optional<mlir::Value> fnRetAlloca; + // Holds coroutine data if the current function is a coroutine. We use a + // wrapper to manage its lifetime, so that we don't have to define CGCoroData + // in this header. + struct CGCoroInfo { + std::unique_ptr<CGCoroData> data; + CGCoroInfo(); + ~CGCoroInfo(); + }; + CGCoroInfo curCoro; + + bool isCoroutine() const { return curCoro.data != nullptr; } + /// The temporary alloca to hold the return value. This is /// invalid iff the function has no return value. Address returnValue = Address::invalid(); @@ -1168,6 +1182,10 @@ 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); + void emitDestroy(Address addr, QualType type, Destroyer *destroyer); void emitDestructorBody(FunctionArgList &args); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index eef23a0ebda7f..e36df8da1d6c0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2043,6 +2043,15 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, return func; } +cir::FuncOp +CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name, + cir::FuncType ty, + const clang::FunctionDecl *fd) { + cir::FuncOp fnOp = createCIRFunction(loc, name, ty, fd); + fnOp.setBuiltin(true); + return fnOp; +} + mlir::SymbolTable::Visibility CIRGenModule::getMLIRVisibility(cir::GlobalOp op) { // MLIR doesn't accept public symbols declarations (only diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 073e8d96b773b..6cf40874e0997 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -448,6 +448,13 @@ class CIRGenModule : public CIRGenTypeCache { cir::FuncType funcType, const clang::FunctionDecl *funcDecl); + /// Create a CIR function with builtin attribute set. + cir::FuncOp createCIRBuiltinFunction(mlir::Location loc, llvm::StringRef name, + cir::FuncType ty, + const clang::FunctionDecl *fd); + + static constexpr const char *builtinCoroId = "__builtin_coro_id"; + /// 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/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index e842892d085d2..22a1c54015e63 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -197,6 +197,7 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s, case Stmt::SEHLeaveStmtClass: case Stmt::SYCLKernelCallStmtClass: case Stmt::CoroutineBodyStmtClass: + return emitCoroutineBody(cast<CoroutineBodyStmt>(*s)); case Stmt::CoreturnStmtClass: case Stmt::CXXTryStmtClass: case Stmt::IndirectGotoStmtClass: diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index c1f27ec8ba858..73fddb184a792 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -14,6 +14,7 @@ add_clang_library(clangCIR CIRGenCall.cpp CIRGenClass.cpp CIRGenCleanup.cpp + CIRGenCoroutine.cpp CIRGenCXX.cpp CIRGenCXXABI.cpp CIRGenBuiltin.cpp diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 58ef500446aa7..dae9162214e6c 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1546,12 +1546,19 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { llvm::SMLoc loc = parser.getCurrentLocation(); mlir::Builder &builder = parser.getBuilder(); + mlir::StringAttr builtinNameAttr = getBuiltinAttrName(state.name); + mlir::StringAttr coroutineNameAttr = getCoroutineAttrName(state.name); mlir::StringAttr lambdaNameAttr = getLambdaAttrName(state.name); mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name); mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name); mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name); mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name); + if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref()))) + state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr()); + if (::mlir::succeeded( + parser.parseOptionalKeyword(coroutineNameAttr.strref()))) + state.addAttribute(coroutineNameAttr, parser.getBuilder().getUnitAttr()); if (::mlir::succeeded(parser.parseOptionalKeyword(lambdaNameAttr.strref()))) state.addAttribute(lambdaNameAttr, parser.getBuilder().getUnitAttr()); if (parser.parseOptionalKeyword(noProtoNameAttr).succeeded()) @@ -1661,6 +1668,12 @@ mlir::Region *cir::FuncOp::getCallableRegion() { } void cir::FuncOp::print(OpAsmPrinter &p) { + if (getBuiltin()) + p << " builtin"; + + if (getCoroutine()) + p << " coroutine"; + if (getLambda()) p << " lambda"; diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp new file mode 100644 index 0000000000000..1fc7d77be2bce --- /dev/null +++ b/clang/test/CIR/CodeGen/coro-task.cpp @@ -0,0 +1,123 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR + +namespace std { + +template<typename T> struct remove_reference { typedef T type; }; +template<typename T> struct remove_reference<T &> { typedef T type; }; +template<typename T> struct remove_reference<T &&> { typedef T type; }; + +template<typename T> +typename remove_reference<T>::type &&move(T &&t) noexcept; + +template <class Ret, typename... T> +struct coroutine_traits { using promise_type = typename Ret::promise_type; }; + +template <class Promise = void> +struct coroutine_handle { + static coroutine_handle from_address(void *) noexcept; +}; +template <> +struct coroutine_handle<void> { + template <class PromiseType> + coroutine_handle(coroutine_handle<PromiseType>) noexcept; + static coroutine_handle from_address(void *); +}; + +struct suspend_always { + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + +struct suspend_never { + bool await_ready() noexcept { return true; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; + +} // namespace std + +namespace folly { +namespace coro { + +using std::suspend_always; +using std::suspend_never; +using std::coroutine_handle; + +using SemiFuture = int; + +template<class T> +struct Task { + struct promise_type { + Task<T> get_return_object() noexcept; + suspend_always initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_value(T); + void unhandled_exception(); + auto yield_value(Task<T>) noexcept { return final_suspend(); } + }; + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + T await_resume(); +}; + +template<> +struct Task<void> { + struct promise_type { + Task<void> get_return_object() noexcept; + suspend_always initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_void() noexcept; + void unhandled_exception() noexcept; + auto yield_value(Task<void>) noexcept { return final_suspend(); } + }; + bool await_ready() noexcept { return false; } + void await_suspend(coroutine_handle<>) noexcept {} + void await_resume() noexcept {} + SemiFuture semi(); +}; + +// FIXME: add CIRGen support here. +// struct blocking_wait_fn { +// template <typename T> +// T operator()(Task<T>&& awaitable) const { +// return T(); +// } +// }; + +// inline constexpr blocking_wait_fn blocking_wait{}; +// static constexpr blocking_wait_fn const& blockingWait = blocking_wait; + +struct co_invoke_fn { + template <typename F, typename... A> + Task<void> operator()(F&& f, A&&... a) const { + return Task<void>(); + } +}; + +co_invoke_fn co_invoke; + +}} // namespace folly::coro + +// CIR-DAG: ![[VoidTask:.*]] = !cir.record<struct "folly::coro::Task<void>" padded {!u8i}> + +// 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 + +using VoidTask = folly::coro::Task<void>; + +VoidTask silly_task() { + co_await std::suspend_always(); +} + +// CIR: cir.func coroutine dso_local @_Z10silly_taskv() -> ![[VoidTask]] +// CHECK: %[[#VoidTaskAddr:]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"] + +// 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]]) `````````` </details> https://github.com/llvm/llvm-project/pull/161616 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
