[clang] [CIR] Initial support for emitting coroutine body (PR #161616)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 2 16:46:31 PDT 2025


https://github.com/Andres-Salamanca updated https://github.com/llvm/llvm-project/pull/161616

>From 860bfd20d7e05005a2d0fdea96908d99a9e8fcfc Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Wed, 1 Oct 2025 21:09:53 -0500
Subject: [PATCH 1/4] [CIR] Initial support for emitting coroutine body

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td |  17 +++
 clang/include/clang/CIR/MissingFeatures.h    |   7 ++
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp      |  25 ++++
 clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp    |  85 +++++++++++++
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp     |   3 +
 clang/lib/CIR/CodeGen/CIRGenFunction.h       |  18 +++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp       |   9 ++
 clang/lib/CIR/CodeGen/CIRGenModule.h         |   7 ++
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp         |   1 +
 clang/lib/CIR/CodeGen/CMakeLists.txt         |   1 +
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp      |  13 ++
 clang/test/CIR/CodeGen/coro-task.cpp         | 123 +++++++++++++++++++
 12 files changed, 309 insertions(+)
 create mode 100644 clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
 create mode 100644 clang/test/CIR/CodeGen/coro-task.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 0a78492aa9a86..3c79d9c501a39 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2341,6 +2341,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.
@@ -2362,11 +2368,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 7a6c084f51cd7..1d9f37ea295f4 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -137,6 +137,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 b26b4f2500579..27ee6decbed4d 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());
+
   auto fn = dyn_cast<cir::FuncOp>(cgf.curFn);
   assert(fn && "emitReturn from non-function");
   if (!fn.getFunctionType().hasVoidReturn()) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index c0ed8b4006ec5..aeedd0ce05fe8 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();
@@ -1174,6 +1188,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 2bd2729f0b0fb..f5facfa970253 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2065,6 +2065,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 2c4c6dd14e2ff..d252bc0379ef7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -473,6 +473,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 644c383693e37..0b8f8bfdfb046 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 3ebf460f7d34c..36db4bd9bab25 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 6b5cc808e9a29..fba094f42414f 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1632,12 +1632,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())
@@ -1747,6 +1754,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]])

>From b9fdcce5f8bb10b2bf6f33b5cb330048b6206876 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Wed, 1 Oct 2025 22:42:45 -0500
Subject: [PATCH 2/4] Fix issues after rebase

---
 clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index ee43c221c3cd6..bc42ab1ebfb38 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -70,7 +70,7 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
   mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
   cir::ConstantOp nullPtrCst = builder.getNullPtr(VoidPtrTy, openCurlyLoc);
 
-  cir::FuncOp fn = curFn;
+  auto fn = dyn_cast<cir::FuncOp>(curFn);
   assert(fn && "other callables are NYI");
   fn.setCoroutine(true);
   cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);

>From 03d718c0d49c22b1a7e7b7b09c791323f7772440 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 2 Oct 2025 16:55:30 -0500
Subject: [PATCH 3/4] Apply review feedback

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td |  2 +-
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp      |  2 +-
 clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp    | 13 +++++--------
 clang/test/CIR/IR/func.cir                   | 11 +++++++++++
 4 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 3c79d9c501a39..7f2e55d14b51c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2344,7 +2344,7 @@ def CIR_FuncOp : CIR_Op<"func", [
     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
+    The `coroutine` keyword is used to mark a 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
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 512e1b2aa0e86..6b85fb8eabc33 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -439,7 +439,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
   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");
+    llvm_unreachable("Error NYI");
 
   case Builtin::BI__builtin_coro_frame: {
     cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_frame NYI");
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index bc42ab1ebfb38..c25cce4ab33b3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -32,12 +32,9 @@ CIRGenFunction::CGCoroInfo::~CGCoroInfo() {}
 static void createCoroData(CIRGenFunction &cgf,
                            CIRGenFunction::CGCoroInfo &curCoro,
                            cir::CallOp coroId) {
-  if (curCoro.data) {
-    llvm_unreachable("EmitCoroutineBodyStatement called twice?");
-    return;
-  }
+  assert(!curCoro.data && "EmitCoroutineBodyStatement called twice?");
 
-  curCoro.data = std::unique_ptr<CGCoroData>(new CGCoroData);
+  curCoro.data = std::make_unique<CGCoroData>();
   curCoro.data->coroId = coroId;
 }
 
@@ -57,8 +54,9 @@ cir::CallOp CIRGenFunction::emitCoroIDBuiltinCall(mlir::Location loc,
         cir::FuncType::get({int32Ty, VoidPtrTy, VoidPtrTy, VoidPtrTy}, int32Ty),
         /*FD=*/nullptr);
     assert(fnOp && "should always succeed");
-  } else
+  } else {
     fnOp = cast<cir::FuncOp>(builtin);
+  }
 
   return builder.createCallOp(loc, fnOp,
                               mlir::ValueRange{builder.getUInt32(newAlign, loc),
@@ -70,8 +68,7 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
   mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
   cir::ConstantOp nullPtrCst = builder.getNullPtr(VoidPtrTy, openCurlyLoc);
 
-  auto fn = dyn_cast<cir::FuncOp>(curFn);
-  assert(fn && "other callables are NYI");
+  auto fn = mlir::cast<cir::FuncOp>(curFn);
   fn.setCoroutine(true);
   cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);
   createCoroData(*this, curCoro, coroId);
diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir
index 0e9a92fcf8201..48c8510fd9a87 100644
--- a/clang/test/CIR/IR/func.cir
+++ b/clang/test/CIR/IR/func.cir
@@ -99,4 +99,15 @@ cir.func @ullfunc() -> !u64i {
 // CHECK:   %[[VAL:.*]] = cir.const #cir.int<42> : !u64i
 // CHECK:   cir.return %[[VAL:.*]] : !u64i
 // CHECK: }
+
+cir.func coroutine @coro() {
+  cir.return
+}
+// CHECK: cir.func{{.*}} coroutine @coro()
+
+cir.func builtin @builtin() {
+  cir.return
+}
+// CHECK: cir.func{{.*}} builtin @builtin()
+
 }

>From b14cfc3bc10cd98b51955c1cc606eebce1f686f3 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 2 Oct 2025 18:46:00 -0500
Subject: [PATCH 4/4] Replace llvm_unreachable with errorNYI

---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 6b85fb8eabc33..4cfa91e09efb4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -439,7 +439,8 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
   case Builtin::BI__builtin_coro_end:
   case Builtin::BI__builtin_coro_suspend:
   case Builtin::BI__builtin_coro_align:
-    llvm_unreachable("Error NYI");
+    cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_id like NYI");
+    return getUndefRValue(e->getType());
 
   case Builtin::BI__builtin_coro_frame: {
     cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_frame NYI");



More information about the cfe-commits mailing list