Skip to content

Commit 03f4d4d

Browse files
[CIR] Add CxxCTorAttr, CxxDTorAttr, CxxAssignAttr, CxxSpecialMemberAttr to cir::FuncOp (#167975)
This PR adds a special member attribute to `cir::FuncOp`. This attribute is also present in the incubator repo. Additionally, I added a "is_trivial" flag, to mark trivial members. I think that might be useful when trying to replace calls to the copy constructor with memcpy for example, but please let me know your thoughts on this. [Here in the incubator repo](https://github.com/llvm/clangir/blob/823e943d1b9aaba0fc46f880c5a6ac8c29fc761d/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp#L1537-L1550) this function is called `LowerTrivialConstructorCall`, but I don't see a check that ensures the constructor is actually trivial.
1 parent 1278d47 commit 03f4d4d

File tree

10 files changed

+383
-4
lines changed

10 files changed

+383
-4
lines changed

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

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

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

Lines changed: 30 additions & 3 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,32 @@ 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+
bool isCxxDestructor();
2587+
2588+
/// Returns true if this function is a copy or move assignment operator.
2589+
bool isCxxSpecialAssignment();
2590+
2591+
/// Returns the kind of constructor this function represents, if any.
2592+
std::optional<CtorKind> getCxxConstructorKind();
2593+
2594+
/// Returns the kind of assignment operator (move, copy) this function
2595+
/// represents, if any.
2596+
std::optional<AssignKind> getCxxSpecialAssignKind();
2597+
2598+
/// Returns true if the function is a trivial C++ member functions such as
2599+
/// trivial default constructor, copy/move constructor, copy/move assignment,
2600+
/// or destructor.
2601+
bool isCxxTrivialMemberFunction();
2602+
}];
25762603

25772604
let hasCustomAssemblyFormat = 1;
25782605
let hasVerifier = 1;
@@ -4362,7 +4389,7 @@ def CIR_ObjSizeOp : CIR_Op<"objsize", [Pure]> {
43624389
When the `min` attribute is present, the operation returns the minimum
43634390
guaranteed accessible size. When absent (max mode), it returns the maximum
43644391
possible object size. Corresponds to `llvm.objectsize`'s `min` argument.
4365-
4392+
43664393
The `dynamic` attribute determines if the value should be evaluated at
43674394
runtime. Corresponds to `llvm.objectsize`'s `dynamic` argument.
43684395

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
@@ -570,7 +570,7 @@ static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) {
570570

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

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

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

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

698+
cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), ctor);
699+
696700
if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) &&
697701
cgm.getTarget().getCXXABI().hasConstructorVariants()) {
698702
emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc());
@@ -731,6 +735,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
731735
const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl());
732736
CXXDtorType dtorType = curGD.getDtorType();
733737

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

clang/lib/CIR/CodeGen/CIRGenModule.cpp

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

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

2228+
// Mark C++ special member functions (Constructor, Destructor etc.)
2229+
setCXXSpecialMemberAttr(func, funcDecl);
2230+
22282231
if (!cgf)
22292232
theModule.push_back(func);
22302233
}
@@ -2240,6 +2243,58 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
22402243
return fnOp;
22412244
}
22422245

2246+
static cir::CtorKind getCtorKindFromDecl(const CXXConstructorDecl *ctor) {
2247+
if (ctor->isDefaultConstructor())
2248+
return cir::CtorKind::Default;
2249+
if (ctor->isCopyConstructor())
2250+
return cir::CtorKind::Copy;
2251+
if (ctor->isMoveConstructor())
2252+
return cir::CtorKind::Move;
2253+
return cir::CtorKind::Custom;
2254+
}
2255+
2256+
static cir::AssignKind getAssignKindFromDecl(const CXXMethodDecl *method) {
2257+
if (method->isCopyAssignmentOperator())
2258+
return cir::AssignKind::Copy;
2259+
if (method->isMoveAssignmentOperator())
2260+
return cir::AssignKind::Move;
2261+
llvm_unreachable("not a copy or move assignment operator");
2262+
}
2263+
2264+
void CIRGenModule::setCXXSpecialMemberAttr(
2265+
cir::FuncOp funcOp, const clang::FunctionDecl *funcDecl) {
2266+
if (!funcDecl)
2267+
return;
2268+
2269+
if (const auto *dtor = dyn_cast<CXXDestructorDecl>(funcDecl)) {
2270+
auto cxxDtor = cir::CXXDtorAttr::get(
2271+
convertType(getASTContext().getCanonicalTagType(dtor->getParent())),
2272+
dtor->isTrivial());
2273+
funcOp.setCxxSpecialMemberAttr(cxxDtor);
2274+
return;
2275+
}
2276+
2277+
if (const auto *ctor = dyn_cast<CXXConstructorDecl>(funcDecl)) {
2278+
cir::CtorKind kind = getCtorKindFromDecl(ctor);
2279+
auto cxxCtor = cir::CXXCtorAttr::get(
2280+
convertType(getASTContext().getCanonicalTagType(ctor->getParent())),
2281+
kind, ctor->isTrivial());
2282+
funcOp.setCxxSpecialMemberAttr(cxxCtor);
2283+
return;
2284+
}
2285+
2286+
const auto *method = dyn_cast<CXXMethodDecl>(funcDecl);
2287+
if (method && (method->isCopyAssignmentOperator() ||
2288+
method->isMoveAssignmentOperator())) {
2289+
cir::AssignKind assignKind = getAssignKindFromDecl(method);
2290+
auto cxxAssign = cir::CXXAssignAttr::get(
2291+
convertType(getASTContext().getCanonicalTagType(method->getParent())),
2292+
assignKind, method->isTrivial());
2293+
funcOp.setCxxSpecialMemberAttr(cxxAssign);
2294+
return;
2295+
}
2296+
}
2297+
22432298
cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
22442299
StringRef name, mlir::ArrayAttr,
22452300
[[maybe_unused]] bool isLocal,

clang/lib/CIR/CodeGen/CIRGenModule.h

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

506+
/// Mark the function as a special member (e.g. constructor, destructor)
507+
void setCXXSpecialMemberAttr(cir::FuncOp funcOp,
508+
const clang::FunctionDecl *funcDecl);
509+
506510
cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
507511
mlir::ArrayAttr = {}, bool isLocal = false,
508512
bool assumeConvergent = false);

0 commit comments

Comments
 (0)