Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
17 changes: 17 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The `coroutine` keyword is used to mark coroutine function, which requires
The `coroutine` keyword is used to mark a coroutine function, which requires

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

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.
Expand All @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Copy link
Contributor

Choose a reason for hiding this comment

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

errorNYI

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


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
Expand Down
85 changes: 85 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (curCoro.data) {
llvm_unreachable("EmitCoroutineBodyStatement called twice?");
return;
}
assert(!curCoro.data && "EmitCoroutineBodyStatement called twice?");

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


curCoro.data = std::unique_ptr<CGCoroData>(new CGCoroData);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
curCoro.data = std::unique_ptr<CGCoroData>(new CGCoroData);
curCoro.data = std::make_unique<CGCoroData>();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

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
Copy link
Contributor

Choose a reason for hiding this comment

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

The else needs braces since the if requires them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

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);

auto fn = dyn_cast<cir::FuncOp>(curFn);
assert(fn && "other callables are NYI");
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
auto fn = dyn_cast<cir::FuncOp>(curFn);
assert(fn && "other callables are NYI");
auto fn = mlir::cast<cir::FuncOp>(curFn);

I don't think they're just NYI. I don't think it's allowed. The other thing this can be is a global variable for which we are generating a ctor region. Using cast makes the assertion part of the cast.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Yes, before I rebased, curFn was a cir::FuncOp, so it was a quick change. Sorry about that.

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();
}
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class LoopOp;

namespace clang::CIRGen {

struct CGCoroData;

class CIRGenFunction : public CIRGenTypeCache {
public:
CIRGenModule &cgm;
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_clang_library(clangCIR
CIRGenCall.cpp
CIRGenClass.cpp
CIRGenCleanup.cpp
CIRGenCoroutine.cpp
CIRGenCXX.cpp
CIRGenCXXABI.cpp
CIRGenBuiltin.cpp
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())))
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add an IR test to verify the parsing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

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())
Expand Down Expand Up @@ -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";

Expand Down
Loading