Skip to content
Merged
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
113 changes: 113 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,119 @@ def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> {
}];
}

//===----------------------------------------------------------------------===//
// CXX SpecialMemberAttr
//===----------------------------------------------------------------------===//

def CIR_CtorKind : CIR_I32EnumAttr<"CtorKind", "CXX Constructor Kind", [
I32EnumAttrCase<"Custom", 0, "custom">,
I32EnumAttrCase<"Default", 1, "default">,
I32EnumAttrCase<"Copy", 2, "copy">,
I32EnumAttrCase<"Move", 3, "move">,
]> {
let genSpecializedAttr = 0;
}

def CIR_CXXCtorAttr : CIR_Attr<"CXXCtor", "cxx_ctor"> {
let summary = "Marks a function as a C++ constructor";
let description = [{
This attribute identifies a C++ constructor and classifies its kind:

- `custom`: a user-defined constructor
- `default`: a default constructor
- `copy`: a copy constructor
- `move`: a move constructor

Example:
```mlir
#cir.cxx_ctor<!rec_a, copy>
#cir.cxx_ctor<!rec_b, default, trivial>
```
}];

let parameters = (ins
"mlir::Type":$type,
EnumParameter<CIR_CtorKind>:$ctor_kind,
DefaultValuedParameter<"bool", "false">:$is_trivial
);

let builders = [
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
CArg<"CtorKind", "cir::CtorKind::Custom">:$ctorKind,
CArg<"bool", "false">:$isTrivial), [{
return $_get(type.getContext(), type, ctorKind, isTrivial);
}]>,
];

let assemblyFormat = [{
`<` $type `,` $ctor_kind (`,` `trivial` $is_trivial^)? `>`
}];
}

def CIR_CXXDtorAttr : CIR_Attr<"CXXDtor", "cxx_dtor"> {
let summary = "Marks a function as a CXX destructor";
let description = [{
This attribute identifies a C++ destructor.
}];

let parameters = (ins
"mlir::Type":$type,
DefaultValuedParameter<"bool", "false">:$is_trivial
);

let builders = [
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
CArg<"bool", "false">:$isTrivial), [{
return $_get(type.getContext(), type, isTrivial);
}]>
];

let assemblyFormat = [{
`<` $type (`,` `trivial` $is_trivial^)? `>`
}];
}

def CIR_AssignKind : CIR_I32EnumAttr<"AssignKind", "CXX Assignment Operator Kind", [
I32EnumAttrCase<"Copy", 0, "copy">,
I32EnumAttrCase<"Move", 1, "move">,
]> {
let genSpecializedAttr = 0;
}

def CIR_CXXAssignAttr : CIR_Attr<"CXXAssign", "cxx_assign"> {
let summary = "Marks a function as a CXX assignment operator";
let description = [{
This attribute identifies a C++ assignment operator and classifies its kind:

- `copy`: a copy assignment
- `move`: a move assignment
}];

let parameters = (ins
"mlir::Type":$type,
EnumParameter<CIR_AssignKind>:$assign_kind,
DefaultValuedParameter<"bool", "false">:$is_trivial
);

let builders = [
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
CArg<"AssignKind">:$assignKind,
CArg<"bool", "false">:$isTrivial), [{
return $_get(type.getContext(), type, assignKind, isTrivial);
}]>
];

let assemblyFormat = [{
`<` $type `,` $assign_kind (`,` `trivial` $is_trivial^)? `>`
}];
}

def CIR_CXXSpecialMemberAttr : AnyAttrOf<[
CIR_CXXCtorAttr,
CIR_CXXDtorAttr,
CIR_CXXAssignAttr
]>;

//===----------------------------------------------------------------------===//
// BitfieldInfoAttr
//===----------------------------------------------------------------------===//
Expand Down
33 changes: 30 additions & 3 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2533,7 +2533,9 @@ def CIR_FuncOp : CIR_Op<"func", [
OptionalAttr<DictArrayAttr>:$res_attrs,
OptionalAttr<FlatSymbolRefAttr>:$aliasee,
CIR_OptionalPriorityAttr:$global_ctor_priority,
CIR_OptionalPriorityAttr:$global_dtor_priority);
CIR_OptionalPriorityAttr:$global_dtor_priority,
OptionalAttr<CIR_CXXSpecialMemberAttr>:$cxx_special_member
);

let regions = (region AnyRegion:$body);

Expand Down Expand Up @@ -2572,7 +2574,32 @@ def CIR_FuncOp : CIR_Op<"func", [
//===------------------------------------------------------------------===//

bool isDeclaration();
}];

//===------------------------------------------------------------------===//
// C++ Special Member Functions
//===------------------------------------------------------------------===//

/// Returns true if this function is a C++ special member function.
bool isCXXSpecialMemberFunction();

bool isCxxConstructor();
bool isCxxDestructor();

/// Returns true if this function is a copy or move assignment operator.
bool isCxxSpecialAssignment();

/// Returns the kind of constructor this function represents, if any.
std::optional<CtorKind> getCxxConstructorKind();

/// Returns the kind of assignment operator (move, copy) this function
/// represents, if any.
std::optional<AssignKind> getCxxSpecialAssignKind();

/// Returns true if the function is a trivial C++ member functions such as
/// trivial default constructor, copy/move constructor, copy/move assignment,
/// or destructor.
bool isCxxTrivialMemberFunction();
}];

let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
Expand Down Expand Up @@ -4202,7 +4229,7 @@ def CIR_ObjSizeOp : CIR_Op<"objsize", [Pure]> {
When the `min` attribute is present, the operation returns the minimum
guaranteed accessible size. When absent (max mode), it returns the maximum
possible object size. Corresponds to `llvm.objectsize`'s `min` argument.

The `dynamic` attribute determines if the value should be evaluated at
runtime. Corresponds to `llvm.objectsize`'s `dynamic` argument.

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/MissingFeatures.h"

using namespace clang;
Expand Down Expand Up @@ -786,6 +787,8 @@ void CIRGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &args) {
"Body of an implicit assignment operator should be compound stmt.");
const auto *rootCS = cast<CompoundStmt>(rootS);

cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), assignOp);

assert(!cir::MissingFeatures::incrementProfileCounter());
assert(!cir::MissingFeatures::runCleanupsScope());

Expand Down
8 changes: 7 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) {

cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
cir::FuncType funcType) {
const auto funcDecl = cast<FunctionDecl>(gd.getDecl());
const auto *funcDecl = cast<FunctionDecl>(gd.getDecl());
curGD = gd;

if (funcDecl->isInlineBuiltinDeclaration()) {
Expand Down Expand Up @@ -640,6 +640,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
{
LexicalScope lexScope(*this, fusedLoc, entryBB);

// Emit the standard function prologue.
startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin());

// Save parameters for coroutine function.
Expand All @@ -666,6 +667,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
// copy-constructors.
emitImplicitAssignmentOperatorBody(args);
} else if (body) {
// Emit standard function body.
if (mlir::failed(emitFunctionBody(body))) {
return nullptr;
}
Expand Down Expand Up @@ -693,6 +695,8 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) {
ctorType == Ctor_Complete) &&
"can only generate complete ctor for this ABI");

cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), ctor);

if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) &&
cgm.getTarget().getCXXABI().hasConstructorVariants()) {
emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc());
Expand Down Expand Up @@ -731,6 +735,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl());
CXXDtorType dtorType = curGD.getDtorType();

cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), dtor);

// For an abstract class, non-base destructors are never used (and can't
// be emitted in general, because vbase dtors may not have been validated
// by Sema), but the Itanium ABI doesn't make them optional and Clang may
Expand Down
55 changes: 55 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2225,6 +2225,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,

assert(!cir::MissingFeatures::opFuncExtraAttrs());

// Mark C++ special member functions (Constructor, Destructor etc.)
setCXXSpecialMemberAttr(func, funcDecl);

if (!cgf)
theModule.push_back(func);
}
Expand All @@ -2240,6 +2243,58 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
return fnOp;
}

static cir::CtorKind getCtorKindFromDecl(const CXXConstructorDecl *ctor) {
if (ctor->isDefaultConstructor())
return cir::CtorKind::Default;
if (ctor->isCopyConstructor())
return cir::CtorKind::Copy;
if (ctor->isMoveConstructor())
return cir::CtorKind::Move;
return cir::CtorKind::Custom;
}

static cir::AssignKind getAssignKindFromDecl(const CXXMethodDecl *method) {
if (method->isCopyAssignmentOperator())
return cir::AssignKind::Copy;
if (method->isMoveAssignmentOperator())
return cir::AssignKind::Move;
llvm_unreachable("not a copy or move assignment operator");
}

void CIRGenModule::setCXXSpecialMemberAttr(
cir::FuncOp funcOp, const clang::FunctionDecl *funcDecl) {
if (!funcDecl)
return;

if (const auto *dtor = dyn_cast<CXXDestructorDecl>(funcDecl)) {
auto cxxDtor = cir::CXXDtorAttr::get(
convertType(getASTContext().getCanonicalTagType(dtor->getParent())),
dtor->isTrivial());
funcOp.setCxxSpecialMemberAttr(cxxDtor);
return;
}

if (const auto *ctor = dyn_cast<CXXConstructorDecl>(funcDecl)) {
cir::CtorKind kind = getCtorKindFromDecl(ctor);
auto cxxCtor = cir::CXXCtorAttr::get(
convertType(getASTContext().getCanonicalTagType(ctor->getParent())),
kind, ctor->isTrivial());
funcOp.setCxxSpecialMemberAttr(cxxCtor);
return;
}

const auto *method = dyn_cast<CXXMethodDecl>(funcDecl);
if (method && (method->isCopyAssignmentOperator() ||
method->isMoveAssignmentOperator())) {
cir::AssignKind assignKind = getAssignKindFromDecl(method);
auto cxxAssign = cir::CXXAssignAttr::get(
convertType(getASTContext().getCanonicalTagType(method->getParent())),
assignKind, method->isTrivial());
funcOp.setCxxSpecialMemberAttr(cxxAssign);
return;
}
}

cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
StringRef name, mlir::ArrayAttr,
[[maybe_unused]] bool isLocal,
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ class CIRGenModule : public CIRGenTypeCache {
cir::FuncType ty,
const clang::FunctionDecl *fd);

/// Mark the function as a special member (e.g. constructor, destructor)
void setCXXSpecialMemberAttr(cir::FuncOp funcOp,
const clang::FunctionDecl *funcDecl);

cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
mlir::ArrayAttr = {}, bool isLocal = false,
bool assumeConvergent = false);
Expand Down
Loading