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

>From 79970c689a18c2c0a0a6f24c1ac0ab084faa0155 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <[email protected]>
Date: Wed, 5 Nov 2025 20:16:54 -0500
Subject: [PATCH 1/2] [CIR] Emit promise declaration in coroutine

---
 clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 103 +++++++++++++++++++++-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp  |   4 +
 clang/lib/CIR/CodeGen/CIRGenFunction.h    |   3 +
 clang/test/CIR/CodeGen/coro-task.cpp      |  36 +++++++-
 4 files changed, 143 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 930ae55405756..133628045c8fb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -13,6 +13,7 @@
 #include "CIRGenFunction.h"
 #include "mlir/Support/LLVM.h"
 #include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
 #include "clang/CIR/MissingFeatures.h"
@@ -33,6 +34,65 @@ struct clang::CIRGen::CGCoroData {
 CIRGenFunction::CGCoroInfo::CGCoroInfo() {}
 CIRGenFunction::CGCoroInfo::~CGCoroInfo() {}
 
+namespace {
+// FIXME: both GetParamRef and ParamReferenceReplacerRAII are good template
+// candidates to be shared among LLVM / CIR codegen.
+
+// Hunts for the parameter reference in the parameter copy/move declaration.
+struct GetParamRef : public StmtVisitor<GetParamRef> {
+public:
+  DeclRefExpr *expr = nullptr;
+  GetParamRef() {}
+  void VisitDeclRefExpr(DeclRefExpr *e) {
+    assert(expr == nullptr && "multilple declref in param move");
+    expr = e;
+  }
+  void VisitStmt(Stmt *s) {
+    for (auto *c : s->children()) {
+      if (c)
+        Visit(c);
+    }
+  }
+};
+
+// This class replaces references to parameters to their copies by changing
+// the addresses in CGF.LocalDeclMap and restoring back the original values in
+// its destructor.
+struct ParamReferenceReplacerRAII {
+  CIRGenFunction::DeclMapTy savedLocals;
+  CIRGenFunction::DeclMapTy &localDeclMap;
+
+  ParamReferenceReplacerRAII(CIRGenFunction::DeclMapTy &localDeclMap)
+      : localDeclMap(localDeclMap) {}
+
+  void addCopy(DeclStmt const *pm) {
+    // Figure out what param it refers to.
+
+    assert(pm->isSingleDecl());
+    VarDecl const *vd = static_cast<VarDecl const *>(pm->getSingleDecl());
+    Expr const *initExpr = vd->getInit();
+    GetParamRef visitor;
+    visitor.Visit(const_cast<Expr *>(initExpr));
+    assert(visitor.expr);
+    DeclRefExpr *dreOrig = visitor.expr;
+    auto *pd = dreOrig->getDecl();
+
+    auto it = localDeclMap.find(pd);
+    assert(it != localDeclMap.end() && "parameter is not found");
+    savedLocals.insert({pd, it->second});
+
+    auto copyIt = localDeclMap.find(vd);
+    assert(copyIt != localDeclMap.end() && "parameter copy is not found");
+    it->second = copyIt->getSecond();
+  }
+
+  ~ParamReferenceReplacerRAII() {
+    for (auto &&savedLocal : savedLocals) {
+      localDeclMap.insert({savedLocal.first, savedLocal.second});
+    }
+  }
+};
+} // namespace
 static void createCoroData(CIRGenFunction &cgf,
                            CIRGenFunction::CGCoroInfo &curCoro,
                            cir::CallOp coroId) {
@@ -149,7 +209,46 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt 
&s) {
   if (s.getReturnStmtOnAllocFailure())
     cgm.errorNYI("handle coroutine return alloc failure");
 
-  assert(!cir::MissingFeatures::generateDebugInfo());
-  assert(!cir::MissingFeatures::emitBodyAndFallthrough());
+  {
+    assert(!cir::MissingFeatures::generateDebugInfo());
+    ParamReferenceReplacerRAII paramReplacer(localDeclMap);
+    // Create mapping between parameters and copy-params for coroutine
+    // function.
+    llvm::ArrayRef<const Stmt *> paramMoves = s.getParamMoves();
+    assert((paramMoves.size() == 0 || (paramMoves.size() == fnArgs.size())) &&
+           "ParamMoves and FnArgs should be the same size for coroutine "
+           "function");
+    // For zipping the arg map into debug info.
+    assert(!cir::MissingFeatures::generateDebugInfo());
+
+    // Create parameter copies. We do it before creating a promise, since an
+    // evolution of coroutine TS may allow promise constructor to observe
+    // parameter copies.
+    for (auto *pm : paramMoves) {
+      if (emitStmt(pm, /*useCurrentScope=*/true).failed())
+        return mlir::failure();
+      paramReplacer.addCopy(cast<DeclStmt>(pm));
+    }
+
+    if (emitStmt(s.getPromiseDeclStmt(), /*useCurrentScope=*/true).failed())
+      return mlir::failure();
+    // returnValue should be valid as long as the coroutine's return type
+    // is not void. The assertion could help us to reduce the check later.
+    assert(returnValue.isValid() == (bool)s.getReturnStmt());
+    // Now we have the promise, initialize the GRO.
+    // We need to emit `get_return_object` first. According to:
+    // [dcl.fct.def.coroutine]p7
+    // The call to get_return_­object is sequenced before the call to
+    // initial_suspend and is invoked at most once.
+    //
+    // So we couldn't emit return value when we emit return statment,
+    // otherwise the call to get_return_object wouldn't be in front
+    // of initial_suspend.
+    if (returnValue.isValid())
+      emitAnyExprToMem(s.getReturnValue(), returnValue,
+                       s.getReturnValue()->getType().getQualifiers(),
+                       /*isInit*/ true);
+    assert(!cir::MissingFeatures::emitBodyAndFallthrough());
+  }
   return mlir::success();
 }
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 71ff20a3b0e43..9925ad0a9f6d9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -620,6 +620,10 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl 
gd, cir::FuncOp fn,
 
     startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin());
 
+    // Save parameters for coroutine function.
+    if (body && isa_and_nonnull<CoroutineBodyStmt>(body))
+      llvm::append_range(fnArgs, funcDecl->parameters());
+
     if (isa<CXXDestructorDecl>(funcDecl)) {
       emitDestructorBody(args);
     } else if (isa<CXXConstructorDecl>(funcDecl)) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index c3fcd1a69a88e..cb3c91bf2f0e5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -152,6 +152,9 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// global initializers.
   mlir::Operation *curFn = nullptr;
 
+  /// Save Parameter Decl for coroutine.
+  llvm::SmallVector<const ParmVarDecl *, 4> fnArgs;
+
   using DeclMapTy = llvm::DenseMap<const clang::Decl *, Address>;
   /// This keeps track of the CIR allocas or globals for local C
   /// declarations.
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp 
b/clang/test/CIR/CodeGen/coro-task.cpp
index 265325f82d7f7..5738c815909ea 100644
--- a/clang/test/CIR/CodeGen/coro-task.cpp
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -36,6 +36,12 @@ struct suspend_never {
   void await_resume() noexcept {}
 };
 
+struct string {
+  int size() const;
+  string();
+  string(char const *s);
+};
+
 } // namespace std
 
 namespace folly {
@@ -101,7 +107,10 @@ co_invoke_fn co_invoke;
 }} // namespace folly::coro
 
 // CIR-DAG: ![[VoidTask:.*]] = !cir.record<struct "folly::coro::Task<void>" 
padded {!u8i}>
-
+// CIR-DAG: ![[IntTask:.*]] = !cir.record<struct "folly::coro::Task<int>" 
padded {!u8i}>
+// CIR-DAG: ![[VoidPromisse:.*]] = !cir.record<struct 
"folly::coro::Task<void>::promise_type" padded {!u8i}>
+// CIR-DAG: ![[IntPromisse:.*]] = !cir.record<struct 
"folly::coro::Task<int>::promise_type" padded {!u8i}>
+// CIR-DAG: ![[StdString:.*]] = !cir.record<struct "std::string" padded {!u8i}>
 // CIR: module {{.*}} {
 // CIR-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : 
!rec_folly3A3Acoro3A3Aco_invoke_fn
 
@@ -119,6 +128,7 @@ VoidTask silly_task() {
 // CIR: cir.func coroutine dso_local @_Z10silly_taskv() -> ![[VoidTask]]
 // CIR: %[[VoidTaskAddr:.*]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"]
 // CIR: %[[SavedFrameAddr:.*]] = cir.alloca !cir.ptr<!void>, 
!cir.ptr<!cir.ptr<!void>>, ["__coro_frame_addr"]
+// CIR: %[[VoidPromisseAddr:.*]] = cir.alloca ![[VoidPromisse]], {{.*}}, 
["__promise"]
 
 // Get coroutine id with __builtin_coro_id.
 
@@ -138,3 +148,27 @@ VoidTask silly_task() {
 // CIR: }
 // CIR: %[[Load0:.*]] = cir.load{{.*}} %[[SavedFrameAddr]] : 
!cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
 // CIR: %[[CoroFrameAddr:.*]] = cir.call @__builtin_coro_begin(%[[CoroId]], 
%[[Load0]])
+
+// Call promise.get_return_object() to retrieve the task object.
+
+// CIR: %[[RetObj:.*]] = cir.call 
@_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]])
 nothrow : {{.*}} -> ![[VoidTask]]
+// CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]]
+
+folly::coro::Task<int> byRef(const std::string& s) {
+  co_return s.size();
+}
+
+// CIR:  cir.func coroutine dso_local @_Z5byRefRKSt6string(%[[ARG:.*]]: 
!cir.ptr<![[StdString]]> {{.*}}) -> ![[IntTask]]
+// CIR:    %[[AllocaParam:.*]] = cir.alloca !cir.ptr<![[StdString]]>, {{.*}}, 
["s", init, const]
+// CIR:    %[[IntTaskAddr:.*]] = cir.alloca ![[IntTask]], {{.*}}, ["__retval"]
+// CIR:    %[[SavedFrameAddr:.*]]  = cir.alloca !cir.ptr<!void>, 
!cir.ptr<!cir.ptr<!void>>, ["__coro_frame_addr"]
+// CIR:    %[[AllocaFnUse:.*]] = cir.alloca !cir.ptr<![[StdString]]>, {{.*}}, 
["s", init, const]
+// CIR:    %[[IntPromisseAddr:.*]] = cir.alloca ![[IntPromisse]], {{.*}}, 
["__promise"]
+// CIR:    cir.store %[[ARG]], %[[AllocaParam]] : !cir.ptr<![[StdString]]>, 
{{.*}}
+
+// Call promise.get_return_object() to retrieve the task object.
+
+// CIR:    %[[LOAD:.*]] = cir.load %[[AllocaParam]] : 
!cir.ptr<!cir.ptr<![[StdString]]>>, !cir.ptr<![[StdString]]>
+// CIR:    cir.store {{.*}} %[[LOAD]], %[[AllocaFnUse]] : 
!cir.ptr<![[StdString]]>, !cir.ptr<!cir.ptr<![[StdString]]>>
+// CIR:    %[[RetObj:.*]] = cir.call 
@_ZN5folly4coro4TaskIiE12promise_type17get_return_objectEv(%4) nothrow : {{.*}} 
-> ![[IntTask]]
+// CIR:    cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]]

>From 2732abe24175252084330b1d39d6b36f7cc701c4 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <[email protected]>
Date: Sat, 8 Nov 2025 17:20:06 -0500
Subject: [PATCH 2/2] Apply Reviews

---
 clang/include/clang/CIR/MissingFeatures.h | 1 +
 clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 9 +++++----
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 48ef8be9fb782..afc4ead8a8085 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -153,6 +153,7 @@ struct MissingFeatures {
   static bool coroEndBuiltinCall() { return false; }
   static bool coroutineFrame() { return false; }
   static bool emitBodyAndFallthrough() { return false; }
+  static bool coroOutsideFrameMD() { return false; }
 
   // Various handling of deferred processing in CIRGenModule.
   static bool cgmRelease() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 133628045c8fb..05fb1aedcbf4a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -48,7 +48,7 @@ struct GetParamRef : public StmtVisitor<GetParamRef> {
     expr = e;
   }
   void VisitStmt(Stmt *s) {
-    for (auto *c : s->children()) {
+    for (Stmt *c : s->children()) {
       if (c)
         Visit(c);
     }
@@ -65,12 +65,12 @@ struct ParamReferenceReplacerRAII {
   ParamReferenceReplacerRAII(CIRGenFunction::DeclMapTy &localDeclMap)
       : localDeclMap(localDeclMap) {}
 
-  void addCopy(DeclStmt const *pm) {
+  void addCopy(const DeclStmt *pm) {
     // Figure out what param it refers to.
 
     assert(pm->isSingleDecl());
-    VarDecl const *vd = static_cast<VarDecl const *>(pm->getSingleDecl());
-    Expr const *initExpr = vd->getInit();
+    const VarDecl *vd = static_cast<const VarDecl *>(pm->getSingleDecl());
+    const Expr *initExpr = vd->getInit();
     GetParamRef visitor;
     visitor.Visit(const_cast<Expr *>(initExpr));
     assert(visitor.expr);
@@ -224,6 +224,7 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt 
&s) {
     // Create parameter copies. We do it before creating a promise, since an
     // evolution of coroutine TS may allow promise constructor to observe
     // parameter copies.
+    assert(!cir::MissingFeatures::coroOutsideFrameMD());
     for (auto *pm : paramMoves) {
       if (emitStmt(pm, /*useCurrentScope=*/true).failed())
         return mlir::failure();

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to