Skip to content

Commit afa84ef

Browse files
Add Special member attribute
1 parent ea66d26 commit afa84ef

File tree

9 files changed

+349
-3
lines changed

9 files changed

+349
-3
lines changed

clang/include/clang/CIR/Dialect/IR/CIRAttrs.td

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,120 @@ def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> {
822822
}];
823823
}
824824

825+
//===----------------------------------------------------------------------===//
826+
// CXX SpecialMemberAttr
827+
//===----------------------------------------------------------------------===//
828+
829+
def CIR_CtorKind : CIR_I32EnumAttr<"CtorKind", "CXX Constructor Kind", [
830+
I32EnumAttrCase<"Custom", 0, "custom">,
831+
I32EnumAttrCase<"Default", 1, "default">,
832+
I32EnumAttrCase<"Copy", 2, "copy">,
833+
I32EnumAttrCase<"Move", 3, "move">,
834+
]> {
835+
let genSpecializedAttr = 0;
836+
}
837+
838+
839+
def CIR_CXXCtorAttr : CIR_Attr<"CXXCtor", "cxx_ctor"> {
840+
let summary = "Marks a function as a C++ constructor";
841+
let description = [{
842+
This attribute identifies a C++ constructor and classifies its kind:
843+
844+
- `custom`: a user-defined constructor
845+
- `default`: a default constructor
846+
- `copy`: a copy constructor
847+
- `move`: a move constructor
848+
849+
Example:
850+
```mlir
851+
#cir.cxx_ctor<!rec_a, copy>
852+
#cir.cxx_ctor<!rec_b, default, trivial>
853+
```
854+
}];
855+
856+
let parameters = (ins
857+
"mlir::Type":$type,
858+
EnumParameter<CIR_CtorKind>:$ctor_kind,
859+
DefaultValuedParameter<"bool", "false">:$is_trivial
860+
);
861+
862+
let builders = [
863+
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
864+
CArg<"CtorKind", "cir::CtorKind::Custom">:$ctorKind,
865+
CArg<"bool", "false">:$isTrivial), [{
866+
return $_get(type.getContext(), type, ctorKind, isTrivial);
867+
}]>,
868+
];
869+
870+
let assemblyFormat = [{
871+
`<` $type `,` $ctor_kind (`,` `trivial` $is_trivial^)? `>`
872+
}];
873+
}
874+
875+
def CIR_CXXDtorAttr : CIR_Attr<"CXXDtor", "cxx_dtor"> {
876+
let summary = "Marks a function as a CXX destructor";
877+
let description = [{
878+
This attribute identifies a C++ destructor.
879+
}];
880+
881+
let parameters = (ins
882+
"mlir::Type":$type,
883+
DefaultValuedParameter<"bool", "false">:$is_trivial
884+
);
885+
886+
let builders = [
887+
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
888+
CArg<"bool", "false">:$isTrivial), [{
889+
return $_get(type.getContext(), type, isTrivial);
890+
}]>
891+
];
892+
893+
let assemblyFormat = [{
894+
`<` $type (`,` `trivial` $is_trivial^)? `>`
895+
}];
896+
}
897+
898+
def CIR_AssignKind : CIR_I32EnumAttr<"AssignKind", "CXX Assignment Operator Kind", [
899+
I32EnumAttrCase<"Copy", 0, "copy">,
900+
I32EnumAttrCase<"Move", 1, "move">,
901+
]> {
902+
let genSpecializedAttr = 0;
903+
}
904+
905+
def CIR_CXXAssignAttr : CIR_Attr<"CXXAssign", "cxx_assign"> {
906+
let summary = "Marks a function as a CXX assignment operator";
907+
let description = [{
908+
This attribute identifies a C++ assignment operator and classifies its kind:
909+
910+
- `copy`: a copy assignment
911+
- `move`: a move assignment
912+
}];
913+
914+
let parameters = (ins
915+
"mlir::Type":$type,
916+
EnumParameter<CIR_AssignKind>:$assign_kind,
917+
DefaultValuedParameter<"bool", "false">:$is_trivial
918+
);
919+
920+
let builders = [
921+
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
922+
CArg<"AssignKind">:$assignKind,
923+
CArg<"bool", "false">:$isTrivial), [{
924+
return $_get(type.getContext(), type, assignKind, isTrivial);
925+
}]>
926+
];
927+
928+
let assemblyFormat = [{
929+
`<` $type `,` $assign_kind (`,` `trivial` $is_trivial^)? `>`
930+
}];
931+
}
932+
933+
def CIR_CXXSpecialMemberAttr : AnyAttrOf<[
934+
CIR_CXXCtorAttr,
935+
CIR_CXXDtorAttr,
936+
CIR_CXXAssignAttr
937+
]>;
938+
825939
//===----------------------------------------------------------------------===//
826940
// BitfieldInfoAttr
827941
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,7 +2533,9 @@ def CIR_FuncOp : CIR_Op<"func", [
25332533
OptionalAttr<DictArrayAttr>:$res_attrs,
25342534
OptionalAttr<FlatSymbolRefAttr>:$aliasee,
25352535
CIR_OptionalPriorityAttr:$global_ctor_priority,
2536-
CIR_OptionalPriorityAttr:$global_dtor_priority);
2536+
CIR_OptionalPriorityAttr:$global_dtor_priority,
2537+
OptionalAttr<CIR_CXXSpecialMemberAttr>:$cxx_special_member
2538+
);
25372539

25382540
let regions = (region AnyRegion:$body);
25392541

@@ -2572,7 +2574,28 @@ def CIR_FuncOp : CIR_Op<"func", [
25722574
//===------------------------------------------------------------------===//
25732575

25742576
bool isDeclaration();
2575-
}];
2577+
2578+
//===------------------------------------------------------------------===//
2579+
// C++ Special Member Functions
2580+
//===------------------------------------------------------------------===//
2581+
2582+
/// Returns true if this function is a C++ special member function.
2583+
bool isCXXSpecialMemberFunction();
2584+
2585+
bool isCxxConstructor();
2586+
2587+
bool isCxxDestructor();
2588+
2589+
/// Returns true if this function is a copy or move assignment operator.
2590+
bool isCxxSpecialAssignment();
2591+
2592+
/// Returns the kind of constructor this function represents, if any.
2593+
std::optional<CtorKind> getCxxConstructorKind();
2594+
2595+
/// Returns the kind of assignment operator (move, copy) this function
2596+
/// represents, if any.
2597+
std::optional<AssignKind> getCxxSpecialAssignKind();
2598+
}];
25762599

25772600
let hasCustomAssemblyFormat = 1;
25782601
let hasVerifier = 1;

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/ExprCXX.h"
1919
#include "clang/AST/RecordLayout.h"
2020
#include "clang/AST/Type.h"
21+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2122
#include "clang/CIR/MissingFeatures.h"
2223

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

790+
cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), assignOp);
791+
789792
assert(!cir::MissingFeatures::incrementProfileCounter());
790793
assert(!cir::MissingFeatures::runCleanupsScope());
791794

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) {
560560

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

566566
if (funcDecl->isInlineBuiltinDeclaration()) {
@@ -630,6 +630,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
630630
{
631631
LexicalScope lexScope(*this, fusedLoc, entryBB);
632632

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

635636
// Save parameters for coroutine function.
@@ -656,6 +657,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
656657
// copy-constructors.
657658
emitImplicitAssignmentOperatorBody(args);
658659
} else if (body) {
660+
// Emit standard function body.
659661
if (mlir::failed(emitFunctionBody(body))) {
660662
return nullptr;
661663
}
@@ -683,6 +685,8 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) {
683685
ctorType == Ctor_Complete) &&
684686
"can only generate complete ctor for this ABI");
685687

688+
cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), ctor);
689+
686690
if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) &&
687691
cgm.getTarget().getCXXABI().hasConstructorVariants()) {
688692
emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc());
@@ -721,6 +725,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
721725
const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl());
722726
CXXDtorType dtorType = curGD.getDtorType();
723727

728+
cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), dtor);
729+
724730
// For an abstract class, non-base destructors are never used (and can't
725731
// be emitted in general, because vbase dtors may not have been validated
726732
// by Sema), but the Itanium ABI doesn't make them optional and Clang may

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,6 +2211,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
22112211

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

2214+
// Mark C++ special member functions (Constructor, Destructor etc.)
2215+
setCXXSpecialMemberAttr(func, funcDecl);
2216+
22142217
if (!cgf)
22152218
theModule.push_back(func);
22162219
}
@@ -2226,6 +2229,54 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
22262229
return fnOp;
22272230
}
22282231

2232+
void CIRGenModule::setCXXSpecialMemberAttr(
2233+
cir::FuncOp funcOp, const clang::FunctionDecl *funcDecl) {
2234+
if (!funcDecl)
2235+
return;
2236+
2237+
if (const auto *dtor = dyn_cast<CXXDestructorDecl>(funcDecl)) {
2238+
auto cxxDtor = cir::CXXDtorAttr::get(
2239+
convertType(getASTContext().getCanonicalTagType(dtor->getParent())),
2240+
dtor->isTrivial());
2241+
funcOp.setCxxSpecialMemberAttr(cxxDtor);
2242+
return;
2243+
}
2244+
2245+
if (const auto *ctor = dyn_cast<CXXConstructorDecl>(funcDecl)) {
2246+
cir::CtorKind ctorKind = cir::CtorKind::Custom;
2247+
if (ctor->isDefaultConstructor())
2248+
ctorKind = cir::CtorKind::Default;
2249+
else if (ctor->isCopyConstructor())
2250+
ctorKind = cir::CtorKind::Copy;
2251+
else if (ctor->isMoveConstructor())
2252+
ctorKind = cir::CtorKind::Move;
2253+
2254+
auto cxxCtor = cir::CXXCtorAttr::get(
2255+
convertType(getASTContext().getCanonicalTagType(ctor->getParent())),
2256+
ctorKind, ctor->isTrivial());
2257+
funcOp.setCxxSpecialMemberAttr(cxxCtor);
2258+
return;
2259+
}
2260+
2261+
const auto *method = dyn_cast<CXXMethodDecl>(funcDecl);
2262+
if (method && (method->isCopyAssignmentOperator() ||
2263+
method->isMoveAssignmentOperator())) {
2264+
cir::AssignKind assignKind;
2265+
if (method->isCopyAssignmentOperator())
2266+
assignKind = cir::AssignKind::Copy;
2267+
else if (method->isMoveAssignmentOperator())
2268+
assignKind = cir::AssignKind::Move;
2269+
else
2270+
llvm_unreachable("unexpected assignment operator kind");
2271+
2272+
auto cxxAssign = cir::CXXAssignAttr::get(
2273+
convertType(getASTContext().getCanonicalTagType(method->getParent())),
2274+
assignKind, method->isTrivial());
2275+
funcOp.setCxxSpecialMemberAttr(cxxAssign);
2276+
return;
2277+
}
2278+
}
2279+
22292280
cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
22302281
StringRef name, mlir::ArrayAttr,
22312282
[[maybe_unused]] bool isLocal,

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,10 @@ class CIRGenModule : public CIRGenTypeCache {
497497
cir::FuncType ty,
498498
const clang::FunctionDecl *fd);
499499

500+
/// Mark the function as a special member (e.g. constructor, destructor)
501+
void setCXXSpecialMemberAttr(cir::FuncOp funcOp,
502+
const clang::FunctionDecl *funcDecl);
503+
500504
cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
501505
mlir::ArrayAttr = {}, bool isLocal = false,
502506
bool assumeConvergent = false);

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
16581658
mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name);
16591659
mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
16601660
mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name);
1661+
mlir::StringAttr specialMemberAttr = getCxxSpecialMemberAttrName(state.name);
16611662

16621663
if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref())))
16631664
state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr());
@@ -1756,6 +1757,20 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
17561757
return success();
17571758
};
17581759

1760+
// Parse CXXSpecialMember attribute
1761+
if (parser.parseOptionalKeyword("special_member").succeeded()) {
1762+
cir::CXXCtorAttr ctorAttr;
1763+
cir::CXXDtorAttr dtorAttr;
1764+
if (parser.parseLess().failed())
1765+
return failure();
1766+
if (parser.parseOptionalAttribute(ctorAttr).has_value())
1767+
state.addAttribute(specialMemberAttr, ctorAttr);
1768+
else if (parser.parseOptionalAttribute(dtorAttr).has_value())
1769+
state.addAttribute(specialMemberAttr, dtorAttr);
1770+
if (parser.parseGreater().failed())
1771+
return failure();
1772+
}
1773+
17591774
if (parseGlobalDtorCtor("global_ctor", [&](std::optional<int> priority) {
17601775
mlir::IntegerAttr globalCtorPriorityAttr =
17611776
builder.getI32IntegerAttr(priority.value_or(65535));
@@ -1833,6 +1848,43 @@ bool cir::FuncOp::isDeclaration() {
18331848
return false;
18341849
}
18351850

1851+
bool cir::FuncOp::isCXXSpecialMemberFunction() {
1852+
return getCxxSpecialMemberAttr() != nullptr;
1853+
}
1854+
1855+
bool cir::FuncOp::isCxxConstructor() {
1856+
auto attr = getCxxSpecialMemberAttr();
1857+
return attr && dyn_cast<CXXCtorAttr>(attr);
1858+
}
1859+
1860+
bool cir::FuncOp::isCxxDestructor() {
1861+
auto attr = getCxxSpecialMemberAttr();
1862+
return attr && dyn_cast<CXXDtorAttr>(attr);
1863+
}
1864+
1865+
bool cir::FuncOp::isCxxSpecialAssignment() {
1866+
auto attr = getCxxSpecialMemberAttr();
1867+
return attr && dyn_cast<CXXAssignAttr>(attr);
1868+
}
1869+
1870+
std::optional<CtorKind> cir::FuncOp::getCxxConstructorKind() {
1871+
auto attr = getCxxSpecialMemberAttr();
1872+
if (attr) {
1873+
if (auto ctor = dyn_cast<CXXCtorAttr>(attr))
1874+
return ctor.getCtorKind();
1875+
}
1876+
return std::nullopt;
1877+
}
1878+
1879+
std::optional<AssignKind> cir::FuncOp::getCxxSpecialAssignKind() {
1880+
auto attr = getCxxSpecialMemberAttr();
1881+
if (attr) {
1882+
if (auto assign = dyn_cast<CXXAssignAttr>(attr))
1883+
return assign.getAssignKind();
1884+
}
1885+
return std::nullopt;
1886+
}
1887+
18361888
mlir::Region *cir::FuncOp::getCallableRegion() {
18371889
// TODO(CIR): This function will have special handling for aliases and a
18381890
// check for an external function, once those features have been upstreamed.
@@ -1883,6 +1935,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
18831935
p << ")";
18841936
}
18851937

1938+
if (auto specialMemberAttr = getCxxSpecialMember()) {
1939+
p << " special_member<";
1940+
p.printAttribute(*specialMemberAttr);
1941+
p << '>';
1942+
}
1943+
18861944
if (auto globalCtorPriority = getGlobalCtorPriority()) {
18871945
p << " global_ctor";
18881946
if (globalCtorPriority.value() != 65535)

0 commit comments

Comments
 (0)