-
Notifications
You must be signed in to change notification settings - Fork 153
[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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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?
@@ -1300,6 +1300,39 @@ def GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor", | |||
"A function with this attribute excutes before module unloading" | |||
>; | |||
|
|||
class CIR_CXXCtorDtor<string name, string attrMnemonic, string sum, string desc> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe rename this to CIR_CXXSpecialMember
? As we build on top of this I figure we'll want to look at operators and other things, so maybe relax on the name for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed here, I see the value in recognizing things like push_back
and emplace_back
for other opts, and wonder if we should be doing that soon in a followup.
let summary = sum; | ||
let description = desc; | ||
|
||
let parameters = (ins "mlir::StringAttr":$name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you plan to do anything with the string name? Seems like it would be more profitable long term to store a Type
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 Some kind of reference to the struct/class definition seems much more useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, storing the Type
is much better, I have updated this.
clang/test/CIR/CodeGen/String.cpp
Outdated
@@ -64,10 +64,10 @@ void test() { | |||
// CHECK-NEXT: cir.store{{.*}} %arg1, %1 : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>> | |||
// CHECK-NEXT: %2 = cir.load{{.*}} %0 : !cir.ptr<!cir.ptr<!rec_String>>, !cir.ptr<!rec_String> | |||
// CHECK-NEXT: %3 = cir.load{{.*}} %1 : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i> | |||
// CHECK-NEXT: cir.call @_ZN6StringC2EPKc(%2, %3) : (!cir.ptr<!rec_String>, !cir.ptr<!s8i>) -> () | |||
// CHECK-NEXT: cir.call @_ZN6StringC2EPKc(%2, %3) {cxx_ctor = #cir.cxx_ctor<"class String">} : (!cir.ptr<!rec_String>, !cir.ptr<!s8i>) -> () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of adding the attribute to the default attribute dict, perhaps have it proper in FuncOp
and print something like ctor<type>
? @xlauko since you have been cleaning up a lot of attribute things, any input here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder here too if this should be a property of the target function instead (that is, do this NOT on the call, but on the emission of _ZN6StringC2EPKc
, then check it via indirection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, proper in FuncOp
should be the way!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated. The attributes are now attached directly to the FuncOp
clang/lib/CIR/CodeGen/CIRGenCall.cpp
Outdated
@@ -424,7 +424,9 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &CallInfo, | |||
const CallArgList &CallArgs, | |||
cir::CIRCallOpInterface *callOrTryCall, | |||
bool IsMustTail, mlir::Location loc, | |||
std::optional<const clang::CallExpr *> E) { | |||
std::optional<const clang::CallExpr *> E, | |||
std::optional<cir::CXXCtorAttr> cxxCtor, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of adding two optionals, perhaps pass in a ArrayRef<Attribute>
and loop over it while calling callLikeOp->setAttr(element)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think this suggestion is a better idea. i was originally thinking we should do something where we capture the 'kind' better, but I think the suggestoin of just 'additionalAttrsToAdd' or something is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have gotten rid of this now, since the attributes are now attached to the FuncOp
(@andykaylor @erichkeane @dkolsen-pgi in case you have any extra thoughts here) |
There was a problem hiding this 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?
let summary = sum; | ||
let description = desc; | ||
|
||
let parameters = (ins "mlir::StringAttr":$name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 Some kind of reference to the struct/class definition seems much more useful.
let summary = sum; | ||
let description = desc; | ||
|
||
let parameters = (ins "mlir::StringAttr":$name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be value in also storing the ctor/dtor kind (i.e. base vs. complete) or other traits (copy/move/default/etc.)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think maybe we can add traits incrementally, as required. For now, I have added copy & default because I use them in LifetimeCheck
@@ -1300,6 +1300,39 @@ def GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor", | |||
"A function with this attribute excutes before module unloading" | |||
>; | |||
|
|||
class CIR_CXXCtorDtor<string name, string attrMnemonic, string sum, string desc> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed here, I see the value in recognizing things like push_back
and emplace_back
for other opts, and wonder if we should be doing that soon in a followup.
clang/lib/CIR/CodeGen/CIRGenCall.cpp
Outdated
@@ -424,7 +424,9 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &CallInfo, | |||
const CallArgList &CallArgs, | |||
cir::CIRCallOpInterface *callOrTryCall, | |||
bool IsMustTail, mlir::Location loc, | |||
std::optional<const clang::CallExpr *> E) { | |||
std::optional<const clang::CallExpr *> E, | |||
std::optional<cir::CXXCtorAttr> cxxCtor, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think this suggestion is a better idea. i was originally thinking we should do something where we capture the 'kind' better, but I think the suggestoin of just 'additionalAttrsToAdd' or something is better.
|
||
auto cxxDtor = cir::CXXDtorAttr::get( | ||
&getMLIRContext(), | ||
getContext().getRecordType(DtorDecl->getParent()).getAsString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO instead of counting on clang's to-string mechanism, I wonder if we could store a better way of identifying the kind. This is I believe going to include the template arguments along the way, which are going to be confusing.
IF what we are really looking for is something like "this is a std::
collection", I wonder if we should do the work to only do this in the case of DtorDecl->isInStdNamespace
, and perhaps slightly more work to see if the base 'name' is correct (that is, only if DtorDecl->getParent()->getName()
-is-in a certain list).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we store the type instead, we can easily do this check if needed in an analysis (like this helper in
bool isStdArrayType(mlir::Type t) { |
static bool isIteratorInStdContainter(mlir::Type t) { |
clang/test/CIR/CodeGen/String.cpp
Outdated
@@ -64,10 +64,10 @@ void test() { | |||
// CHECK-NEXT: cir.store{{.*}} %arg1, %1 : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>> | |||
// CHECK-NEXT: %2 = cir.load{{.*}} %0 : !cir.ptr<!cir.ptr<!rec_String>>, !cir.ptr<!rec_String> | |||
// CHECK-NEXT: %3 = cir.load{{.*}} %1 : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i> | |||
// CHECK-NEXT: cir.call @_ZN6StringC2EPKc(%2, %3) : (!cir.ptr<!rec_String>, !cir.ptr<!s8i>) -> () | |||
// CHECK-NEXT: cir.call @_ZN6StringC2EPKc(%2, %3) {cxx_ctor = #cir.cxx_ctor<"class String">} : (!cir.ptr<!rec_String>, !cir.ptr<!s8i>) -> () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder here too if this should be a property of the target function instead (that is, do this NOT on the call, but on the emission of _ZN6StringC2EPKc
, then check it via indirection.
68ff6ef
to
8c7235c
Compare
β With the latest revision this PR passed the C/C++ code formatter. |
A few updates:
|
77c8851
to
d4c51f3
Compare
There was a problem hiding this 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.
else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && | ||
fd->hasAttr<CUDAGlobalAttr>()) | ||
} else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && | ||
fd->hasAttr<CUDAGlobalAttr>()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fd->hasAttr<CUDAGlobalAttr>()) | |
fd->hasAttr<CUDAGlobalAttr>()) { |
Our coding standard is that if anything in the 'if/else-if/else' list requires curleys, they all do, so just add them to this branch (and perhaps the 'else' at the end as a driveby).
@@ -37,4 +37,4 @@ B::B() { | |||
// CHECK: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B> | |||
// CHECK: cir.return | |||
// CHECK: } | |||
// CHECK: cir.func private dso_local @_ZN1BC1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BC2Ev) | |||
// CHECK: cir.func private dso_local @_ZN1BC1Ev(!cir.ptr<!rec_B>) ctor<!rec_B, default_ctor> alias(@_ZN1BC2Ev) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps use default
instead of default_ctor
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise for copy
"Functions with this attribute are CXX constructors"> { | ||
let parameters = (ins "mlir::Type":$type, | ||
"bool":$is_default_constructor, | ||
"bool":$is_copy_constructor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like you want to use an enum kind here since those states cannot be true at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, both cannot be true, but I think introducing an enum kind here makes it a bit complicated when introducing extra traits (in the future, if necessary).
`<` | ||
$type `,` $is_default_constructor `,` $is_copy_constructor | ||
`>` | ||
}]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a let description
and explain the other params.
OptionalAttr<AnyASTFunctionDeclAttr>:$ast | ||
OptionalAttr<AnyASTFunctionDeclAttr>:$ast, | ||
OptionalAttr<CXXCtorAttr>:$cxx_ctor, | ||
OptionalAttr<CXXDtorAttr>:$cxx_dtor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A function cannot be ctor and dtor at the same time, perhaps both should inherit from a CIR_CXXSpecialMember instead such that we could have OptionalAttr<CXXSpecialMember>:$cxx_specialmember,
which could be either?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, correct me if I am wrong, but I do not think we can use OptionalAttr<CXXSpecialMember>
, since CIR_CXXSpecialMember
is a class definition:
class CIR_CXXSpecialMember<string name, string attrMnemonic, string sum>
: CIR_Attr<name, attrMnemonic> {...}
AttrBuilderWithInferredContext<(ins "mlir::Type":$type), [{ | ||
return $_get(type.getContext(), type); | ||
}]>]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need a CIR to CIR test for parsing/printing
There was a problem hiding this 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
ab65415
to
c1f8a62
Compare
c1f8a62
to
f6aaa4d
Compare
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 modifyingIdiomRecognizer
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.