Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
104 changes: 102 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 (Stmt *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(const DeclStmt *pm) {
// Figure out what param it refers to.

assert(pm->isSingleDecl());
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);
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) {
Expand Down Expand Up @@ -149,7 +209,47 @@ 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.
assert(!cir::MissingFeatures::coroOutsideFrameMD());
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();
}
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a good reason for the 4 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is mainly a matter of personal preference, depending on how many parameters functions typically have. I don’t think there’s a specific reason behind choosing 4. Do you think it would be better to leave it as 0? I tested it, and the default capacity appears to be 6.


using DeclMapTy = llvm::DenseMap<const clang::Decl *, Address>;
/// This keeps track of the CIR allocas or globals for local C
/// declarations.
Expand Down
36 changes: 35 additions & 1 deletion clang/test/CIR/CodeGen/coro-task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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

Expand All @@ -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.

Expand All @@ -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]]