-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add support for calling C++ constructors #30630
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
Conversation
3b902c2
to
f5903f7
Compare
Summary: This is needed in Swift for C++ interop -- see here for the corresponding Swift change: swiftlang/swift#30630 As part of this change, I've had to make some changes to the interface of CGCXXABI to return the additional parameters separately rather than adding them directly to a `CallArgList`. Reviewers: rjmccall Reviewed By: rjmccall Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D79942
Summary: This is needed in Swift for C++ interop -- see here for the corresponding Swift change: swiftlang/swift#30630 As part of this change, I've had to make some changes to the interface of CGCXXABI to return the additional parameters separately rather than adding them directly to a `CallArgList`. Reviewers: rjmccall Reviewed By: rjmccall Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D79942
@swift-ci Please test |
@swift-ci Please test Windows |
This is now ready for review again -- sorry for the delay. See also the discussion here: |
@rjmccall Not sure if you want to review this also? |
Build failed |
// Note `this` return type and implicit "most derived" argument. | ||
// MICROSOFT_X64: call %struct.HasVirtualBase* @"??0HasVirtualBase@@QEAA@UArgType@@@Z"(%struct.HasVirtualBase* %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 1) | ||
return HasVirtualBase(ArgType()) | ||
} |
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.
Could you add some more tests, for cases that don't involve virtual base classes?
For example, you mentioned that constructors on some Itanium ABI variants on ARM64 iOS return 'this', so they would also need a thunk.
Also would be nice to have some tests for cases that don't need thunks.
Feel free to add some of them here if you are not using standard library types -- or in a separate file if you need standard library types.
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.
Could you add some more tests, for cases that don't involve virtual base classes?
Done.
For example, you mentioned that constructors on some Itanium ABI variants on ARM64 iOS return 'this', so they would also need a thunk.
Some of the existing test cases already demonstrate this -- see the comments on the ITANIUM_ARM
and MICROSOFT_X64
tests, which point out the this
return type.
Also would be nice to have some tests for cases that don't need thunks.
This was already the case for the existing ITANIUM_X64
test. The newly added ITANIUM_X64
test also does not need a thunk.
// attributes of the constructor that was generated by Clang, which doesn't | ||
// insert these attributes. | ||
// | ||
// - The `this` parameter should _not_ carry a `nocapture` attribute (unlike |
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 the type is loadable in Swift, nocapture
is permitted, I think.
In this example, the function returns the result indirectly, and the result is marked nocapture
. Since we construct a C++ object in that memory, the constructor must also be nocapture
.
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.
Hm... good point.
Since there is no way in general to guarantee that the C++ constructor will be nocapture
, I think the conclusion here is that when we're returning C++ objects indirectly, we should always do so without nocapture
?
(We could, in principle, add the nocapture
back if we can prove to ourselves that the pointer can't be captured. But I think Swift currently isn't set up to do that -- I assume the place that adds the nocapture
is in SignatureExpansion
, and that only sees the type of the function, not its definition.)
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.
Well, we can probably check whether the type is trivially copyable. If it's trivially copyable, it can't be captured, because Swift is absolutely not going to promise not to move it around.
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 I see what you're saying. Swift can certainly also move non-trivially-copyable C++ types around as it pleases, but then the move constructor will notice this happening and can adjust the pointer that was previously captured by the constructor. But a trivially copyable type has no way of doing this, and so we can assume that the constructor doesn't capture this
. Is this what you're getting at?
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.
FWIW, I've checked, and Clang doesn't add nocapture
to the this
parameter of a constructor, even if the type is trivially copyable. I'm not sure why this is, but if Clang isn't adding the nocapture
, I don't think we should be adding it either.
There's a wider discussion of what we should be doing WRT to nocapture
for Swift functions that return C++ types (see https://bugs.swift.org/browse/SR-12845), but that's outside the scope of this PR.
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test |
Build failed |
Build failed |
I've tried to reproduce the LLDB test failures locally, but they pass, even with ASAN enabled. Restarting testing in case this was just an infrastructure failure (though I'm not exactly sure how it could be). |
It looks like (on Windows only?) it's possible for a function template to have a |
Yeah, that didn't work. I'll try my other idea. |
…orting the function name. Sometimes, on windows, we get a function template wrapping the constructor decl. In this case, look through the function template to find the constructor decl.
b0dbeff
to
545b44e
Compare
@swift-ci please test Windows platform. |
1 similar comment
@swift-ci please test Windows platform. |
@rjmccall thank you again for the help reviewing. I really appreciate your insights. Any other comments? Hoping to land this in the next few days. |
Unless anyone has an objection, I'm going to land this patch on Wednesday. |
nice! |
Oh, yes, that's right. I don't think that's Windows only — any constructor template should have that characteristic. |
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.
LGTM. Thanks for seeing this through! Very impressive work.
Actually, thinking about it, that won't work because even if it does correctly merge the branches before benchmarking, the benchmark will be shown as "added" so, we won't be able to get any real data from it. |
Performance: -O
Code size: -OPerformance: -Osize
Code size: -OsizePerformance: -Onone
Code size: -swiftlibsHow to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview
|
@swift-ci please test and merge. |
1 similar comment
@swift-ci please test and merge. |
Big congrats to both Martin and Zoe, impressive work! |
This test was dissabled until swiftlang#30630 (support calling C++ constructors) landed. That patch landed a while ago, so this test should be enabled.
Summary: This is needed in Swift for C++ interop -- see here for the corresponding Swift change: swiftlang/swift#30630 As part of this change, I've had to make some changes to the interface of CGCXXABI to return the additional parameters separately rather than adding them directly to a `CallArgList`. Reviewers: rjmccall Reviewed By: rjmccall Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D79942
A problem that we have to deal with when calling C++ constructors is that the ABI of a C++ constructor depends not only on the signature of the constructor but on the class on which it is defined. For example, it can be necessary to pass additional “implicit” arguments to constructors of virtual base classes.
It is therefore fundamentally not possible for SignatureExpansion (which doesn’t have access to the
CXXConstructorDecl
) to reliably produce the correct LLVM signature from only the type of the constructor. The approach we take is to try to get most of the way in SignatureExpansion by marking the SIL function return type with the@out
attribute, representing the fact that C++ constructors take athis
points to the object to be initialized.This produces an “assumed” constructor ABI in SignatureExpansion that assumes there are no implicit arguments and that the return type of the constructor ABI is void (and indeed there is no way to represent anything else in the SIL type). If this assumed ABI doesn’t match the actual ABI, we insert a thunk in IRGen.
The thunk is marked
alwaysinline
, so it doesn’t incur any runtime overhead, but some compile-time overhead is required for LLVM to inline the thunk. If this turns out to be excessive, we can introduce what is essentially a peephole optimization for a full apply of afunction_ref
that refers to a C++ constructor and directly emit the constructor call in this case (along with any required implicit arguments) instead of calling the thunk. I’ve chosen not to tackle that in this PR though as it’s already large enough.On some ABIs (e.g. Itanium x64), we get lucky and the ABI for a complete constructor call always matches the assumed ABI.
In addition to the core C++ constructor functionality, this PR adds some a couple of small required features:
void
typeSignatureExpansion::expandExternalSignatureTypes()