Skip to content

[CIR][CodeGen] Introduce CIR CXXSpecialMember attribute #1711

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

bruteforceboy
Copy link
Contributor

I think this one is self-explanatory, so I will not write much 🙂‍

Adding this attribute helps in optimizations like #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.

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Thanks for adding more of the building blocks here. In the future it'd be nice to have a representation for struct/class more closer to C++ and it will probably be easier to extract this type of information, but to prevent over engineering early, it feels right to incrementally add pieces that can enable us to better analyze C++ code and make transformations easier to write.

Can you look into changing LifetimeChecker.cpp to use this attribute instead of the current AST approach?

@bcardosolopes
Copy link
Member

(@andykaylor @erichkeane @dkolsen-pgi in case you have any extra thoughts here)

Copy link
Collaborator

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

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

Do we also want to set this attribute on cir.func definitions/declarations?

Copy link

github-actions bot commented Jun 25, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@bruteforceboy
Copy link
Contributor Author

A few updates:

  1. The attribute is now attached to the FuncOp (like ctor<type>) instead of the call
  2. The attribute now stores Type instead of a string
  3. I updated LifetimeChecker.cpp to make use of this attribute instead of the AST approach
  4. I added a couple of traits for the ctor attribute, and I think we can extend as necessary in the future
  5. CIR_CXXCtorDtor is now CIR_CXXSpecialMember
  6. Updated a couple of tests to reflect the ctor/dtor attribute.

cc: @bcardosolopes, @erichkeane, @andykaylor

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

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

1 comment, else seems reasonable, I'm happy when the others are.

@bruteforceboy bruteforceboy changed the title [CIR][CodeGen] Introduce CIR CXXCtorDtor attribute [CIR][CodeGen] Introduce CIR CXXSpecialMember attribute Jun 25, 2025
Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Thanks for the update, one more round of suggestions

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Thanks for the updates, one more round!

@@ -2798,6 +2813,15 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
p.printAttribute(annotations);
}

if (getCxxSpecialMember()) {
p << " cxx_special_member<";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need to wrap it in cxx_special_member? It is apparent from cir.cxx_ctor/dtor that this is the special member function.

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Thanks for your patience, more comments!

@@ -2618,6 +2620,37 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
state.addAttribute(annotationsNameAttr, annotations);
}

// Parse CXXSpecialMember attribute
if (mlir::succeeded(parser.parseOptionalKeyword("cxx_ctor"))) {
Copy link
Member

Choose a reason for hiding this comment

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

Still sounds simple enough that we shouldn't need these to be hand written, what specific problems are you hitting?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can update this once the syntax of the attribute is approved. I am also happy with member_function_of<type, ctor<copy>>

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! I'll defer this to @xlauko cause I'm going to be out until next week. One important part here is not to have a custom parser/printer because this is simple enough that it doesn't need one (see more examples in the LLVM dialect for inspiration).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bcardosolopes I tried working around these custom printers/parsers, and I ran into some issues.

Using the CXXCtorAttr attribute as an example, the format we want to use is member_function_of<type, ctor<Kind>>. The issue with using parseOptionalAttribute following a printAttribute, for example, to avoid manual parsing is that it tries to parse the full attribute, starting with its attribute mnemonic #cir.cxx_ctor!!

Both printAttribute and parseAttribute/parseOptionalAttribute start with the attribute mnemonic!

No matter how much you change the assembly format the mnemonic starts anyway, because CXXCtorAttr inherits from CIR_Attr. This is why I arrived at this version using the "neater parseOptionalAttribute" previously.

I think the easiest solution is having custom printers/parsers for these attributes and for CXXCtorAttr (as an example) we can print/parse the type and the ctorKind as we want, using whatever format.

I played around with these printers/parers a bit and I wasn't able to find alternatives, unfortunately.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for your patience and many different attempts here. Let's try to expedite this so you can move on to more interesting work. What I'd like to see:

  • No custom printing/parsing. You can drop the special_member_of and just have a #cir.cxx_ctor if that's what it takes. If you introduce an alias for it you can reduce it further, but I would leave that to another PR to land this faster.
  • Follow the TBAA route of attribute inheritance and drop the AnyAttrOf for the SpecialMemberOf attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback.

I have updated the PR and now there is no custom printing/parsing. The constructor attribute, for example, looks like special_member<#cir.cxx_ctor<Type, Kind>>.

For AnyAttrOf, the idea was actually gotten following the TBAA route. I do not think we are able to use direct attribute inheritance. I tried and got some crashes related to attribute constraints.

// CIR-NEXT: cir.func private @_ZN1EntEv(!cir.ptr<!rec_E>) -> !rec_E
// CIR-NEXT: cir.func private @_ZN1ED1Ev(!cir.ptr<!rec_E>) extra(#fn_attr)
// CIR-NEXT: cir.func private @_ZN1ED1Ev(!cir.ptr<!rec_E>) cxx_dtor<!rec_E> extra(#fn_attr)
Copy link
Member

Choose a reason for hiding this comment

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

Sounds like we could also drop "cxx": dtor<!rec_E> is probably good enough? No need to change this right now, let's converge on the syntax first (see other comment).

Comment on lines 1362 to 1363
def CIR_CXXCtorAttr
: CIR_Attr<"CXXCtor", "cxx_ctor"> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def CIR_CXXCtorAttr
: CIR_Attr<"CXXCtor", "cxx_ctor"> {
def CIR_CXXCtorAttr : CIR_Attr<"CXXCtor", "cxx_ctor"> {

Comment on lines 1386 to 1387
def CIR_CXXDtorAttr
: CIR_Attr<"CXXDtor", "cxx_dtor"> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def CIR_CXXDtorAttr
: CIR_Attr<"CXXDtor", "cxx_dtor"> {
def CIR_CXXDtorAttr : CIR_Attr<"CXXDtor", "cxx_dtor"> {

let assemblyFormat = [{
`<` $type `>`
}];
// Printing and parsing also available in CIRDialect.cpp
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Printing and parsing also available in CIRDialect.cpp

let assemblyFormat = [{
`<` $type `,` $ctorKind `>`
}];
// Printing and parsing also available in CIRDialect.cpp
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Printing and parsing also available in CIRDialect.cpp

Attribute should either use default printer/parser or custom, only in latter case it is available in CIRDialect.cpp which obvious from the context.

Comment on lines 1399 to 1402
let builders =
[AttrBuilderWithInferredContext<(ins "mlir::Type":$type), [{
return $_get(type.getContext(), type);
}]>];
Copy link
Collaborator

Choose a reason for hiding this comment

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

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

Comment on lines 1379 to 1383
let builders =
[AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
CArg<"CtorKind", "cir::CtorKind::Custom">:$ctorKind), [{
return $_get(type.getContext(), type, ctorKind);
}]>];
Copy link
Collaborator

Choose a reason for hiding this comment

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants