-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[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
00526ca
f2f2496
69378a5
7d9eb14
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,180 @@ | ||
| // 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
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. I think the If not, I think we need some comments explaining the |
||
|
|
||
| #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(); | ||
|
|
||
| // 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(); | ||
|
|
||
| // This will be instantiated implicitly but won't be exported. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_exported(); | ||
|
|
||
| // This won't be instantiated. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated(); | ||
| }; | ||
|
|
||
| template <class T> void C<T>::to_be_exported() {} | ||
| template <class T> void C<T>::to_be_exported_explicitly() {} | ||
| template <class T> void C<T>::not_to_be_exported() {} | ||
| template <class T> void C<T>::not_to_be_instantiated() {} | ||
|
|
||
| // 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(); | ||
| }; | ||
|
|
||
| template <class T> void D<T>::to_be_exported_iff_no_explicit_instantiation() {} | ||
|
|
||
| // Interaction with VTables. | ||
| template <class T> | ||
| struct E { | ||
| // This will be instanciated by the explicit template instantiation definition. | ||
| virtual void to_be_exported(); | ||
|
|
||
| // 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(); | ||
|
|
||
| // This too, but will be exported by the member attribute. | ||
| EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) virtual void to_be_exported_explicitly(); | ||
| }; | ||
|
|
||
| template <class T> void E<T>::to_be_exported() {} | ||
| template <class T> void E<T>::to_be_instantiated() {} | ||
| template <class T> void E<T>::to_be_exported_explicitly() {} | ||
|
|
||
| // 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{{.*}} ptr @"??4?$C@H@@QEAAAEAU0@$$QEAU0@@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{{.*}} ptr @_ZN1CIiEaSEOS0_ | ||
| // 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" | ||
| // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$D@H@@QEAAAEAU0@$$QEAU0@@Z" | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1DIiEaSERKS0_ | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1DIiEaSEOS0_ | ||
| 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 @"??4?$E@H@@QEAAAEAU0@$$QEAU0@@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{{.*}} ptr @"??0?$E@H@@QEAA@$$QEAU0@@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{{.*}} ptr @_ZN1EIiEaSEOS0_ | ||
| // 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 @_ZN1EIiEC2EOS0_ | ||
| // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1EIiEC1EOS0_ | ||
| // 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" | ||
| // MSC: define weak_odr dso_local dllexport{{.*}} ptr @"??4?$D@I@@QEAAAEAU0@$$QEAU0@@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.
As a style issue, I think it would be easier to read if we put this in a variable, like:
Also, I'm not sure that it matters whether the method with the attribute is virtual or not. I think it would be enough to check that any method has the attribute -- because then the motivation about "suppress the vtable; it will live with the explicit instantiation definition" may not hold.
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 if we could break this out into a separate PR as well, since it seems somewhat separate from the other issue.
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.
Sure.
I don't understand why non-virtual functions matter here. If no virtual methods are excluded, then no special handling is needed to enforce emitting the definition. The vtable itself is emitted by another procedure anyway (I still can't find the procedure, though).
No, this should be a part of the PR because this is the cause of reverting #65961.