-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[Clang] Apply exclude_from_explicit_instantiation to dllimport/dllexport #168171
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?
Changes from all commits
3234c26
1e558da
d7e93e8
94d7cf7
bdbeeac
bf8df95
4ca9b9a
5a16b11
d40c741
73b9cf4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| // RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | \ | ||
| // RUN: FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_ --implicit-check-not=dllexport | ||
| // RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -o - %s | \ | ||
| // RUN: FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ --implicit-check-not=dllexport | ||
| // RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -o - %s | \ | ||
| // RUN: FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ --implicit-check-not=dllexport | ||
|
|
||
| // Test that __declspec(dllexport) doesn't instantiate entities marked with | ||
| // the exclude_from_explicit_instantiation attribute unless marked as dllexport explicitly. | ||
|
|
||
| // MSC: ModuleID = {{.*}}exclude_from_dllexport.cpp | ||
| // MSC: source_filename = {{.*}}exclude_from_dllexport.cpp | ||
| // GNU: ModuleID = {{.*}}exclude_from_dllexport.cpp | ||
| // GNU: source_filename = {{.*}}exclude_from_dllexport.cpp | ||
|
Comment on lines
+11
to
+14
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These checks seem redundant. (Same in the other file.)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just to silence |
||
|
|
||
| #define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation)) | ||
|
|
||
| template <class T> | ||
| struct C { | ||
| // This will be instantiated explicitly as an exported function because it | ||
| // inherits dllexport from the class instantiation. | ||
| void to_be_exported() noexcept; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is there
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought a test I referenced did that but I can't remember. I'll remove them. |
||
|
|
||
| // This will be instantiated implicitly as an exported function because it is | ||
| // marked as dllexport explicitly. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) void to_be_exported_explicitly() noexcept; | ||
|
|
||
| // This will be instantiated implicitly but won't be exported. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_exported() noexcept; | ||
|
|
||
| // This won't be instantiated. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated() noexcept; | ||
| }; | ||
|
|
||
| template <class T> void C<T>::to_be_exported() noexcept {} | ||
| template <class T> void C<T>::to_be_exported_explicitly() noexcept {} | ||
| template <class T> void C<T>::not_to_be_exported() noexcept {} | ||
| template <class T> void C<T>::not_to_be_instantiated() noexcept {} | ||
|
|
||
| // Attach the attribute to class template declaration instead of instantiation declaration. | ||
| template <class T> | ||
| struct __declspec(dllexport) D { | ||
| // This will be exported if and only if no explicit instantiations are provided. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION void to_be_exported_iff_no_explicit_instantiation() noexcept; | ||
| }; | ||
|
|
||
| template <class T> void D<T>::to_be_exported_iff_no_explicit_instantiation() noexcept {} | ||
|
|
||
| // Interaction with VTables. | ||
| template <class T> | ||
| struct E { | ||
| // This will be instanciated by the explicit template instantiation definition. | ||
| virtual void to_be_exported() noexcept; | ||
|
|
||
| // This will be instantiated by the VTable definition, regardless of | ||
| // `exclude_from_explicit_instantiation`. | ||
| // The dllexport attribute won't be inherited. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION virtual void to_be_instantiated() noexcept; | ||
|
|
||
| // This too, but will be exported by the member attribute. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) virtual void to_be_exported_explicitly() noexcept; | ||
| }; | ||
|
|
||
| template <class T> void E<T>::to_be_exported() noexcept {} | ||
| template <class T> void E<T>::to_be_instantiated() noexcept {} | ||
| template <class T> void E<T>::to_be_exported_explicitly() noexcept {} | ||
|
|
||
| // MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_exported@?$E@H@@UEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_exported@?$E@I@@UEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any | ||
| // MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_exported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_exported_iff_no_explicit_instantiation@?$D@I@@QEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_instantiated@?$E@H@@UEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_exported_explicitly@?$E@H@@UEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_instantiated@?$E@I@@UEAAXXZ" = comdat any | ||
| // MSC: $"?to_be_exported_explicitly@?$E@I@@UEAAXXZ" = comdat any | ||
| // GNU: $_ZN1CIiE14to_be_exportedEv = comdat any | ||
| // GNU: $_ZN1EIiE14to_be_exportedEv = comdat any | ||
| // GNU: $_ZN1EIjE14to_be_exportedEv = comdat any | ||
| // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any | ||
| // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any | ||
| // GNU: $_ZN1DIiE44to_be_exported_iff_no_explicit_instantiationEv = comdat any | ||
| // GNU: $_ZN1DIjE44to_be_exported_iff_no_explicit_instantiationEv = comdat any | ||
| // GNU: $_ZN1EIiE18to_be_instantiatedEv = comdat any | ||
| // GNU: $_ZN1EIiE25to_be_exported_explicitlyEv = comdat any | ||
| // GNU: $_ZN1EIjE18to_be_instantiatedEv = comdat any | ||
| // GNU: $_ZN1EIjE25to_be_exported_explicitlyEv = comdat any | ||
|
|
||
| // MSC: @0 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E@H@@6B@") | ||
| // MSC: @1 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E@I@@6B@") | ||
| // MSC: @"??_7?$E@H@@6B@" = dllexport unnamed_addr | ||
| // MSC: @"??_7?$E@I@@6B@" = unnamed_addr | ||
| // GNU:@_ZTV1EIiE = weak_odr dso_local dllexport unnamed_addr constant {{.*}}, comdat | ||
| // GNU:@_ZTV1EIjE = weak_odr dso_local unnamed_addr constant {{.*}}, comdat | ||
|
|
||
| // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$C@H@@QEAAAEAU0@AEBU0@@Z" | ||
| // MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$C@H@@QEAAXXZ" | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1CIiEaSERKS0_ | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1CIiE14to_be_exportedEv | ||
| template struct __declspec(dllexport) C<int>; | ||
|
|
||
| // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$D@H@@QEAAAEAU0@AEBU0@@Z" | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1DIiEaSERKS0_ | ||
| template struct D<int>; // No dllexport here. | ||
| // Don't provide explicit instantiation for D<unsigned>. | ||
|
|
||
|
|
||
| // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$E@H@@QEAAAEAU0@AEBU0@@Z" | ||
| // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E@H@@QEAA@XZ" | ||
| // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??0?$E@H@@QEAA@AEBU0@@Z" | ||
| // MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$E@H@@UEAAXXZ" | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1EIiEaSERKS0_ | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC2Ev | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC1Ev | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC2ERKS0_ | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC1ERKS0_ | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiE14to_be_exportedEv | ||
| template struct __declspec(dllexport) E<int>; | ||
|
|
||
| // MSC: define weak_odr dso_local{{.*}} void @"?to_be_exported@?$E@I@@UEAAXXZ" | ||
| // GNU: define weak_odr dso_local{{.*}} void @_ZN1EIjE14to_be_exportedEv | ||
| template struct E<unsigned int>; | ||
|
|
||
| // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$D@I@@QEAAAEAU0@AEBU0@@Z" | ||
|
|
||
| void use() { | ||
| C<int> c; | ||
|
|
||
| // MSC: call void @"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" | ||
| // GNU: call void @_ZN1CIiE25to_be_exported_explicitlyEv | ||
| c.to_be_exported_explicitly(); // implicitly instantiated here | ||
|
|
||
| // MSC: call void @"?not_to_be_exported@?$C@H@@QEAAXXZ" | ||
| // GNU: call void @_ZN1CIiE18not_to_be_exportedEv | ||
| c.not_to_be_exported(); // implicitly instantiated here | ||
|
|
||
| D<int> di; | ||
|
|
||
| // MSC: call void @"?to_be_exported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ" | ||
| // GNU: call void @_ZN1DIiE44to_be_exported_iff_no_explicit_instantiationEv | ||
| di.to_be_exported_iff_no_explicit_instantiation(); // implicitly instantiated here | ||
|
|
||
| D<unsigned> dj; | ||
|
|
||
| // MSC: call void @"?to_be_exported_iff_no_explicit_instantiation@?$D@I@@QEAAXXZ" | ||
| // GNU: call void @_ZN1DIjE44to_be_exported_iff_no_explicit_instantiationEv | ||
| dj.to_be_exported_iff_no_explicit_instantiation(); // implicitly instantiated here | ||
| } | ||
|
|
||
| // MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1CIiE25to_be_exported_explicitlyEv | ||
|
|
||
| // MSC: define linkonce_odr dso_local void @"?not_to_be_exported@?$C@H@@QEAAXXZ" | ||
| // GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_exportedEv | ||
|
|
||
| // MSC: define linkonce_odr dso_local void @"?to_be_exported_iff_no_explicit_instantiation@?$D@H@@QEAAXXZ" | ||
| // MSC: define weak_odr dso_local dllexport void @"?to_be_exported_iff_no_explicit_instantiation@?$D@I@@QEAAXXZ" | ||
| // GNU: define linkonce_odr dso_local void @_ZN1DIiE44to_be_exported_iff_no_explicit_instantiationEv | ||
| // GNU: define weak_odr dso_local dllexport void @_ZN1DIjE44to_be_exported_iff_no_explicit_instantiationEv | ||
|
|
||
| // MSC: define linkonce_odr dso_local void @"?to_be_instantiated@?$E@H@@UEAAXXZ" | ||
| // MSC: define weak_odr dso_local dllexport void @"?to_be_exported_explicitly@?$E@H@@UEAAXXZ" | ||
| // GNU: define linkonce_odr dso_local void @_ZN1EIiE18to_be_instantiatedEv | ||
| // GNU: define weak_odr dso_local dllexport void @_ZN1EIiE25to_be_exported_explicitlyEv | ||
|
|
||
| // MSC: define linkonce_odr dso_local void @"?to_be_instantiated@?$E@I@@UEAAXXZ" | ||
| // MSC: define weak_odr dso_local dllexport void @"?to_be_exported_explicitly@?$E@I@@UEAAXXZ" | ||
| // GNU: define linkonce_odr dso_local void @_ZN1EIjE18to_be_instantiatedEv | ||
| // GNU: define weak_odr dso_local dllexport void @_ZN1EIjE25to_be_exported_explicitlyEv | ||
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.
Hmm. I guess I'm confused by the contradiction of setting "DefineVTable = false" and then "ensure ... is accessible from the vtable".
The problem seems to be that some parts of the code tries to suppress vtable emission, while a different part emits it.
Would it work if we don't do the
DefineVTable = falsepart, i.e. just changed the theif (IsExplicitInstantiationDeclaration)to something likeif (IsExplicitInstantiationDeclaration && !HasExcludeFromExplicitInstantiationAttr)?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 wasn't sure if
Consumer.HandleVTable(Class);below should be called in this case; that was not called previously. However, since the VTable is actually emitted, it might need to be called.Surprisingly, setting
DefineVTable = trueregardlessIsExplicitInstantiationDeclarationdoesn't break any tests.The current construction was introduced in 88d292c, but the commit doesn't mention MSVC ABI.
How do I check "correct" behavior?