Skip to content

Commit 32eacb5

Browse files
[CIR][CodeGen] Introduce CIR CXXSpecialMember attribute (#1711)
I think this one is self-explanatory, so I will not write much πŸ™‚β€ Adding this attribute helps in optimizations like [#1653](#1653), and using the attribute it's easy to create operations like `cir.std.vector.ctor`/`cir.std.vector.dtor` by just modifying `IdiomRecognizer` a bit. I believe it will also be useful for future optimizations. Finally, I updated quite a number of tests so they now reflect this attribute. Please, let me know if you see any issues.
1 parent b2a8ee9 commit 32eacb5

File tree

12 files changed

+162
-25
lines changed

12 files changed

+162
-25
lines changed

β€Žclang/include/clang/CIR/Dialect/IR/CIRAttrs.td

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,60 @@ def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> {
12711271
}];
12721272
}
12731273

1274+
def CIR_CtorKind : CIR_I32EnumAttr<"CtorKind", "CXX Constructor Kind", [
1275+
I32EnumAttrCase<"Custom", 0, "custom">,
1276+
I32EnumAttrCase<"Default", 1, "default">,
1277+
I32EnumAttrCase<"Copy", 2, "copy">,
1278+
]> {
1279+
let genSpecializedAttr = 0;
1280+
}
1281+
1282+
def CIR_CXXCtorAttr : CIR_Attr<"CXXCtor", "cxx_ctor"> {
1283+
let summary = "Marks a function as a CXX constructor";
1284+
let description = [{
1285+
Functions with this attribute are CXX constructors.
1286+
The `custom` kind is used if the constructor is a custom constructor.
1287+
The `default` kind is used if the constructor is a default constructor.
1288+
The `copy` kind is used if the constructor is a copy constructor.
1289+
}];
1290+
let parameters = (ins "mlir::Type":$type,
1291+
EnumParameter<CIR_CtorKind>:$ctorKind);
1292+
1293+
let assemblyFormat = [{
1294+
`<` $type `,` $ctorKind `>`
1295+
}];
1296+
1297+
let builders = [
1298+
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
1299+
CArg<"CtorKind", "cir::CtorKind::Custom">:$ctorKind), [{
1300+
return $_get(type.getContext(), type, ctorKind);
1301+
}]>
1302+
];
1303+
}
1304+
1305+
def CIR_CXXDtorAttr : CIR_Attr<"CXXDtor", "cxx_dtor"> {
1306+
let summary = "Marks a function as a CXX destructor";
1307+
let description = [{
1308+
Functions with this attribute are CXX destructors
1309+
}];
1310+
let parameters = (ins "mlir::Type":$type);
1311+
1312+
let assemblyFormat = [{
1313+
`<` $type `>`
1314+
}];
1315+
1316+
let builders = [
1317+
AttrBuilderWithInferredContext<(ins "mlir::Type":$type), [{
1318+
return $_get(type.getContext(), type);
1319+
}]>
1320+
];
1321+
}
1322+
1323+
def CIR_CXXSpecialMemberAttr : AnyAttrOf<[
1324+
CIR_CXXCtorAttr,
1325+
CIR_CXXDtorAttr
1326+
]>;
1327+
12741328
def CIR_BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
12751329
let summary = "Represents a bit field info";
12761330
let description = [{

β€Žclang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3776,6 +3776,7 @@ def FuncOp : CIR_Op<"func", [
37763776
CIR_OptionalPriorityAttr:$global_ctor_priority,
37773777
CIR_OptionalPriorityAttr:$global_dtor_priority,
37783778
OptionalAttr<ArrayAttr>:$annotations,
3779+
OptionalAttr<CIR_CXXSpecialMemberAttr>:$cxx_special_member,
37793780
OptionalAttr<AnyASTFunctionDeclAttr>:$ast
37803781
);
37813782

β€Žclang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -764,15 +764,29 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
764764
// Generate the body of the function.
765765
// TODO: PGO.assignRegionCounters
766766
assert(!cir::MissingFeatures::shouldInstrumentFunction());
767-
if (isa<CXXDestructorDecl>(fd))
767+
if (auto dtor = dyn_cast<CXXDestructorDecl>(fd)) {
768+
auto cxxDtor = cir::CXXDtorAttr::get(
769+
convertType(getContext().getRecordType(dtor->getParent())));
770+
fn.setCxxSpecialMemberAttr(cxxDtor);
771+
768772
emitDestructorBody(args);
769-
else if (isa<CXXConstructorDecl>(fd))
773+
} else if (auto ctor = dyn_cast<CXXConstructorDecl>(fd)) {
774+
cir::CtorKind ctorKind = cir::CtorKind::Custom;
775+
if (ctor->isDefaultConstructor())
776+
ctorKind = cir::CtorKind::Default;
777+
if (ctor->isCopyConstructor())
778+
ctorKind = cir::CtorKind::Copy;
779+
780+
auto cxxCtor = cir::CXXCtorAttr::get(
781+
convertType(getContext().getRecordType(ctor->getParent())), ctorKind);
782+
fn.setCxxSpecialMemberAttr(cxxCtor);
783+
770784
emitConstructorBody(args);
771-
else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice &&
772-
fd->hasAttr<CUDAGlobalAttr>())
785+
} else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice &&
786+
fd->hasAttr<CUDAGlobalAttr>()) {
773787
CGM.getCUDARuntime().emitDeviceStub(*this, fn, args);
774-
else if (isa<CXXMethodDecl>(fd) &&
775-
cast<CXXMethodDecl>(fd)->isLambdaStaticInvoker()) {
788+
} else if (isa<CXXMethodDecl>(fd) &&
789+
cast<CXXMethodDecl>(fd)->isLambdaStaticInvoker()) {
776790
// The lambda static invoker function is special, because it forwards or
777791
// clones the body of the function call operator (but is actually
778792
// static).
@@ -788,8 +802,9 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
788802
fn.erase();
789803
return nullptr;
790804
}
791-
} else
805+
} else {
792806
llvm_unreachable("no definition for emitted function");
807+
}
793808

794809
assert(builder.getInsertionBlock() && "Should be valid");
795810

β€Žclang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2735,6 +2735,27 @@ cir::FuncOp CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
27352735
f.setExtraAttrsAttr(
27362736
cir::ExtraFuncAttributesAttr::get(builder.getDictionaryAttr({})));
27372737

2738+
if (fd) {
2739+
if (auto dtor = dyn_cast<CXXDestructorDecl>(fd)) {
2740+
auto cxxDtor = cir::CXXDtorAttr::get(
2741+
convertType(getASTContext().getRecordType(dtor->getParent())));
2742+
f.setCxxSpecialMemberAttr(cxxDtor);
2743+
}
2744+
2745+
if (auto ctor = dyn_cast<CXXConstructorDecl>(fd)) {
2746+
cir::CtorKind ctorKind = cir::CtorKind::Custom;
2747+
if (ctor->isDefaultConstructor())
2748+
ctorKind = cir::CtorKind::Default;
2749+
if (ctor->isCopyConstructor())
2750+
ctorKind = cir::CtorKind::Copy;
2751+
2752+
auto cxxCtor = cir::CXXCtorAttr::get(
2753+
convertType(getASTContext().getRecordType(ctor->getParent())),
2754+
ctorKind);
2755+
f.setCxxSpecialMemberAttr(cxxCtor);
2756+
}
2757+
}
2758+
27382759
if (!curCGF)
27392760
theModule.push_back(f);
27402761
}

β€Žclang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2529,6 +2529,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
25292529
auto visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
25302530
auto dsoLocalNameAttr = getDsoLocalAttrName(state.name);
25312531
auto annotationsNameAttr = getAnnotationsAttrName(state.name);
2532+
auto cxxSpecialMemberAttr = getCxxSpecialMemberAttrName(state.name);
25322533
if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref())))
25332534
state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr());
25342535
if (::mlir::succeeded(
@@ -2609,6 +2610,20 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
26092610
state.addAttribute(annotationsNameAttr, annotations);
26102611
}
26112612

2613+
// Parse CXXSpecialMember attribute
2614+
if (parser.parseOptionalKeyword("special_member").succeeded()) {
2615+
cir::CXXCtorAttr ctorAttr;
2616+
cir::CXXDtorAttr dtorAttr;
2617+
if (parser.parseLess().failed())
2618+
return failure();
2619+
if (parser.parseOptionalAttribute(ctorAttr).has_value())
2620+
state.addAttribute(cxxSpecialMemberAttr, ctorAttr);
2621+
if (parser.parseOptionalAttribute(dtorAttr).has_value())
2622+
state.addAttribute(cxxSpecialMemberAttr, dtorAttr);
2623+
if (parser.parseGreater().failed())
2624+
return failure();
2625+
}
2626+
26122627
// If additional attributes are present, parse them.
26132628
if (parser.parseOptionalAttrDictWithKeyword(state.attributes))
26142629
return failure();
@@ -2789,6 +2804,13 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
27892804
p.printAttribute(annotations);
27902805
}
27912806

2807+
if (auto specialMemberAttr = getCxxSpecialMember()) {
2808+
assert((mlir::isa<cir::CXXCtorAttr, cir::CXXDtorAttr>(*specialMemberAttr)));
2809+
p << " special_member<";
2810+
p.printAttribute(*specialMemberAttr);
2811+
p << '>';
2812+
}
2813+
27922814
function_interface_impl::printFunctionAttributes(
27932815
p, *this,
27942816
// These are all omitted since they are custom printed already.
@@ -2799,7 +2821,7 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
27992821
getLinkageAttrName(), getCallingConvAttrName(), getNoProtoAttrName(),
28002822
getSymVisibilityAttrName(), getArgAttrsAttrName(), getResAttrsAttrName(),
28012823
getComdatAttrName(), getGlobalVisibilityAttrName(),
2802-
getAnnotationsAttrName()});
2824+
getAnnotationsAttrName(), getCxxSpecialMemberAttrName()});
28032825

28042826
if (auto aliaseeName = getAliasee()) {
28052827
p << " alias(";

β€Žclang/lib/CIR/Dialect/Transforms/LifetimeCheck.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ struct LifetimeCheckPass : public LifetimeCheckBase<LifetimeCheckPass> {
7676
void checkLambdaCaptureStore(StoreOp storeOp);
7777
void trackCallToCoroutine(CallOp callOp);
7878

79-
void checkCtor(CallOp callOp, ASTCXXConstructorDeclInterface ctor);
79+
void checkCtor(CallOp callOp, cir::CXXCtorAttr ctor);
8080
void checkMoveAssignment(CallOp callOp, ASTCXXMethodDeclInterface m);
8181
void checkCopyAssignment(CallOp callOp, ASTCXXMethodDeclInterface m);
8282
void checkNonConstUseOfOwner(mlir::Value ownerAddr, mlir::Location loc);
@@ -1555,8 +1555,7 @@ bool LifetimeCheckPass::isCtorInitPointerFromOwner(CallOp callOp) {
15551555
return false;
15561556
}
15571557

1558-
void LifetimeCheckPass::checkCtor(CallOp callOp,
1559-
ASTCXXConstructorDeclInterface ctor) {
1558+
void LifetimeCheckPass::checkCtor(CallOp callOp, cir::CXXCtorAttr ctor) {
15601559
// TODO: zero init
15611560
// 2.4.2 if the initialization is default initialization or zero
15621561
// initialization, example:
@@ -1565,7 +1564,7 @@ void LifetimeCheckPass::checkCtor(CallOp callOp,
15651564
// string_view p;
15661565
//
15671566
// both results in pset(p) == {null}
1568-
if (ctor.isDefaultConstructor()) {
1567+
if (ctor.getCtorKind() == cir::CtorKind::Default) {
15691568
// First argument passed is always the alloca for the 'this' ptr.
15701569

15711570
// Currently two possible actions:
@@ -1589,7 +1588,7 @@ void LifetimeCheckPass::checkCtor(CallOp callOp,
15891588
}
15901589

15911590
// User defined copy ctor calls ...
1592-
if (ctor.isCopyConstructor()) {
1591+
if (ctor.getCtorKind() == cir::CtorKind::Copy) {
15931592
llvm_unreachable("NYI");
15941593
}
15951594

@@ -1794,8 +1793,13 @@ void LifetimeCheckPass::checkCall(CallOp callOp) {
17941793

17951794
// From this point on only owner and pointer class methods handling,
17961795
// starting from special methods.
1797-
if (auto ctor = dyn_cast<ASTCXXConstructorDeclInterface>(methodDecl))
1798-
return checkCtor(callOp, ctor);
1796+
if (auto fnName = callOp.getCallee()) {
1797+
auto calleeFuncOp = getCalleeFromSymbol(theModule, *fnName);
1798+
if (calleeFuncOp && calleeFuncOp.getCxxSpecialMember())
1799+
if (auto cxxCtor =
1800+
dyn_cast<cir::CXXCtorAttr>(*calleeFuncOp.getCxxSpecialMember()))
1801+
return checkCtor(callOp, cxxCtor);
1802+
}
17991803
if (methodDecl.isMoveAssignmentOperator())
18001804
return checkMoveAssignment(callOp, methodDecl);
18011805
if (methodDecl.isCopyAssignmentOperator())

β€Žclang/test/CIR/CodeGen/ctor-alias.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ B::B() {
3737
// CHECK: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
3838
// CHECK: cir.return
3939
// CHECK: }
40-
// CHECK: cir.func private dso_local @_ZN1BC1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BC2Ev)
40+
// CHECK: cir.func private dso_local @_ZN1BC1Ev(!cir.ptr<!rec_B>) special_member<#cir.cxx_ctor<!rec_B, default>> alias(@_ZN1BC2Ev)

β€Žclang/test/CIR/CodeGen/static.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ static Init __ioinit2(false);
1818

1919
// BEFORE: module {{.*}} {
2020
// BEFORE-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!rec_Init>, !cir.bool)
21-
// BEFORE-NEXT: cir.func private @_ZN4InitD1Ev(!cir.ptr<!rec_Init>)
21+
// BEFORE-NEXT: cir.func private @_ZN4InitD1Ev(!cir.ptr<!rec_Init>) special_member<#cir.cxx_dtor<!rec_Init>>
2222
// BEFORE-NEXT: cir.global "private" internal dso_local @_ZL8__ioinit = ctor : !rec_Init {
2323
// BEFORE-NEXT: %0 = cir.get_global @_ZL8__ioinit : !cir.ptr<!rec_Init>
2424
// BEFORE-NEXT: %1 = cir.const #true
@@ -42,7 +42,7 @@ static Init __ioinit2(false);
4242
// AFTER-NEXT: cir.global "private" external @__dso_handle : i8
4343
// AFTER-NEXT: cir.func private @__cxa_atexit(!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, !cir.ptr<i8>)
4444
// AFTER-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!rec_Init>, !cir.bool)
45-
// AFTER-NEXT: cir.func private @_ZN4InitD1Ev(!cir.ptr<!rec_Init>)
45+
// AFTER-NEXT: cir.func private @_ZN4InitD1Ev(!cir.ptr<!rec_Init>) special_member<#cir.cxx_dtor<!rec_Init>>
4646
// AFTER-NEXT: cir.global "private" internal dso_local @_ZL8__ioinit = #cir.zero : !rec_Init {alignment = 1 : i64, ast = #cir.var.decl.ast}
4747
// AFTER-NEXT: cir.func internal private @__cxx_global_var_init()
4848
// AFTER-NEXT: %0 = cir.get_global @_ZL8__ioinit : !cir.ptr<!rec_Init>

β€Žclang/test/CIR/CodeGen/temporaries.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ void f() {
1414
!E();
1515
}
1616

17-
// CIR: cir.func private @_ZN1EC1Ev(!cir.ptr<!rec_E>) extra(#fn_attr)
17+
// CIR: cir.func private @_ZN1EC1Ev(!cir.ptr<!rec_E>) special_member<#cir.cxx_ctor<!rec_E, default>> extra(#fn_attr)
1818
// CIR-NEXT: cir.func private @_ZN1EntEv(!cir.ptr<!rec_E>) -> !rec_E
19-
// CIR-NEXT: cir.func private @_ZN1ED1Ev(!cir.ptr<!rec_E>) extra(#fn_attr)
19+
// CIR-NEXT: cir.func private @_ZN1ED1Ev(!cir.ptr<!rec_E>) special_member<#cir.cxx_dtor<!rec_E>> extra(#fn_attr)
2020
// CIR-NEXT: cir.func dso_local @_Z1fv() extra(#fn_attr1) {
2121
// CIR-NEXT: cir.scope {
2222
// CIR-NEXT: %[[ONE:[0-9]+]] = cir.alloca !rec_E, !cir.ptr<!rec_E>, ["agg.tmp.ensured"] {alignment = 1 : i64}

β€Žclang/test/CIR/CodeGen/tempref.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
struct A { ~A(); };
77
A &&a = dynamic_cast<A&&>(A{});
88

9-
// CHECK: cir.func private @_ZN1AD1Ev(!cir.ptr<!rec_A>) extra(#fn_attr)
9+
// CHECK: cir.func private @_ZN1AD1Ev(!cir.ptr<!rec_A>) special_member<#cir.cxx_dtor<!rec_A>> extra(#fn_attr)
1010
// CHECK-NEXT: cir.global external @a = #cir.ptr<null> : !cir.ptr<!rec_A> {alignment = 8 : i64, ast = #cir.var.decl.ast}
1111
// CHECK-NEXT: cir.func internal private @__cxx_global_var_init() {
1212
// CHECK-NEXT: cir.scope {

0 commit comments

Comments
Β (0)