https://github.com/kikairoya updated https://github.com/llvm/llvm-project/pull/168171
>From 3234c2685a86561a3df695f6a8e4b6c55c1a7f5c Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Sat, 15 Nov 2025 11:25:23 +0900 Subject: [PATCH 1/6] pretest --- ...t_instantiation.exclude_from_dllexport.cpp | 64 +++++++++++++++++++ ...t_instantiation.exclude_from_dllimport.cpp | 50 +++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp create mode 100644 clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp new file mode 100644 index 0000000000000..6b1bd83f6d69b --- /dev/null +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_ +// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ +// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ + +// Test that __declspec(dllexport) doesn't instantiate entities marked with +// the exclude_from_explicit_instantiation attribute unless marked as dllexport explicitly. + +#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; + + // 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 as an exported function unintentionally. + 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 {} + +// MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any +// MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any +// MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any +// MSC: $"?not_to_be_instantiated@?$C@H@@QEAAXXZ" = comdat any +// GNU: $_ZN1CIiE14to_be_exportedEv = comdat any +// GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any +// GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any +// GNU: $_ZN1CIiE22not_to_be_instantiatedEv = comdat any + +// MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$C@H@@QEAAXXZ" +// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1CIiE14to_be_exportedEv +template struct __declspec(dllexport) C<int>; + +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 +}; + +// 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 weak_odr dso_local dllexport void @"?not_to_be_exported@?$C@H@@QEAAXXZ" +// GNU: define weak_odr dso_local dllexport void @_ZN1CIiE18not_to_be_exportedEv + +// MSC: define weak_odr dso_local dllexport void @"?not_to_be_instantiated@?$C@H@@QEAAXXZ" +// GNU: define weak_odr dso_local dllexport void @_ZN1CIiE22not_to_be_instantiatedEv diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp new file mode 100644 index 0000000000000..adc420f37bbc6 --- /dev/null +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_ +// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ +// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ + +// Test that __declspec(dllimport) doesn't instantiate entities marked with +// the exclude_from_explicit_instantiation attribute unless marked as dllimport explicitly. + +#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation)) + +template <class T> +struct C { + // This will be instantiated explicitly as an imported function because it + // inherits dllimport from the class instantiation. + void to_be_imported() noexcept; + + // Per-member dllimport in explicitly dllimport-ed template instantiation is not allowed. + //EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) void to_be_imported_explicitly() noexcept; + + // This will be instantiated implicitly as an imported function unintentionally. + EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_imported() noexcept; + + // This won't be instantiated. + EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_instantiated() noexcept; +}; + +template <class T> void C<T>::to_be_imported() noexcept {} +template <class T> void C<T>::not_to_be_imported() noexcept {} +template <class T> void C<T>::not_to_be_instantiated() noexcept {} + +extern template struct __declspec(dllimport) C<int>; + +void use() { + C<int> c; + + // MSC: call void @"?to_be_imported@?$C@H@@QEAAXXZ" + // GNU: call void @_ZN1CIiE14to_be_importedEv + c.to_be_imported(); + + //c.to_be_imported_explicitly(); + + // MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ" + // GNU: call void @_ZN1CIiE18not_to_be_importedEv + c.not_to_be_imported(); // implicitly instantiated here +}; + +// MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ" +// GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv + +// MSC: declare dllimport void @"?not_to_be_imported@?$C@H@@QEAAXXZ" +// GNU: declare dllimport void @_ZN1CIiE18not_to_be_importedEv >From 1e558daf82c6750b14b6ea273175d29279cce450 Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Sat, 15 Nov 2025 11:25:24 +0900 Subject: [PATCH 2/6] [Clang] Apply exclude_from_explicit_instantiation to dllimport/dllexport Attaching `__declspec(dllexport/dllimport)` to explicit instantiation declaration made its whole member instantiated even if they were `__attribute__((__exclude_from_explicit_instantation__))`. Such members should not be instantiated nor be exported to avoid symbol leakage or duplication. --- clang/lib/Sema/SemaDeclCXX.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index aa36a79142e52..fe732a1328fab 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6551,6 +6551,8 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { return; } + TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind(); + if (Context.getTargetInfo().shouldDLLImportComdatSymbols() && !ClassAttr->isInherited()) { // Diagnose dll attributes on members of class with dll attribute. @@ -6561,6 +6563,11 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { if (!MemberAttr || MemberAttr->isInherited() || Member->isInvalidDecl()) continue; + if ((TSK == TSK_ExplicitInstantiationDeclaration || + TSK == TSK_ExplicitInstantiationDefinition) && + Member->hasAttr<ExcludeFromExplicitInstantiationAttr>()) + continue; + Diag(MemberAttr->getLocation(), diag::err_attribute_dll_member_of_dll_class) << MemberAttr << ClassAttr; @@ -6583,8 +6590,6 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { !ClassExported && cast<DLLImportAttr>(ClassAttr)->wasPropagatedToBaseTemplate(); - TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind(); - // Ignore explicit dllexport on explicit class template instantiation // declarations, except in MinGW mode. if (ClassExported && !ClassAttr->isInherited() && @@ -6601,6 +6606,11 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { // seem to be true in practice? for (Decl *Member : Class->decls()) { + if ((TSK == TSK_ExplicitInstantiationDeclaration || + TSK == TSK_ExplicitInstantiationDefinition) && + Member->hasAttr<ExcludeFromExplicitInstantiationAttr>()) + continue; + VarDecl *VD = dyn_cast<VarDecl>(Member); CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member); >From d7e93e8c139252f102bd78fda4a3ab252bf93ab2 Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Sat, 15 Nov 2025 11:25:24 +0900 Subject: [PATCH 3/6] update test --- ...t_instantiation.exclude_from_dllexport.cpp | 11 +++------- ...t_instantiation.exclude_from_dllimport.cpp | 20 +++++++++++++------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp index 6b1bd83f6d69b..0a94bd4b1f6b6 100644 --- a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp @@ -17,7 +17,7 @@ struct C { // marked as dllexport explicitly. EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllexport) void to_be_exported_explicitly() noexcept; - // This will be instantiated implicitly as an exported function unintentionally. + // 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. @@ -32,11 +32,9 @@ template <class T> void C<T>::not_to_be_instantiated() noexcept {} // MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any // MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any // MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any -// MSC: $"?not_to_be_instantiated@?$C@H@@QEAAXXZ" = comdat any // GNU: $_ZN1CIiE14to_be_exportedEv = comdat any // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any -// GNU: $_ZN1CIiE22not_to_be_instantiatedEv = comdat any // MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported@?$C@H@@QEAAXXZ" // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1CIiE14to_be_exportedEv @@ -57,8 +55,5 @@ void use() { // 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 weak_odr dso_local dllexport void @"?not_to_be_exported@?$C@H@@QEAAXXZ" -// GNU: define weak_odr dso_local dllexport void @_ZN1CIiE18not_to_be_exportedEv - -// MSC: define weak_odr dso_local dllexport void @"?not_to_be_instantiated@?$C@H@@QEAAXXZ" -// GNU: define weak_odr dso_local dllexport void @_ZN1CIiE22not_to_be_instantiatedEv +// 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 diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp index adc420f37bbc6..b070259c2f048 100644 --- a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp @@ -13,10 +13,11 @@ struct C { // inherits dllimport from the class instantiation. void to_be_imported() noexcept; - // Per-member dllimport in explicitly dllimport-ed template instantiation is not allowed. - //EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) void to_be_imported_explicitly() noexcept; + // This will be instantiated implicitly as an imported function because it is + // marked as dllimport explicitly. + EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) void to_be_imported_explicitly() noexcept; - // This will be instantiated implicitly as an imported function unintentionally. + // This will be instantiated implicitly but won't be imported. EXCLUDE_FROM_EXPLICIT_INSTANTIATION void not_to_be_imported() noexcept; // This won't be instantiated. @@ -27,6 +28,8 @@ template <class T> void C<T>::to_be_imported() noexcept {} template <class T> void C<T>::not_to_be_imported() noexcept {} template <class T> void C<T>::not_to_be_instantiated() noexcept {} +// MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any +// GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any extern template struct __declspec(dllimport) C<int>; void use() { @@ -36,7 +39,9 @@ void use() { // GNU: call void @_ZN1CIiE14to_be_importedEv c.to_be_imported(); - //c.to_be_imported_explicitly(); + // MSC: call void @"?to_be_imported_explicitly@?$C@H@@QEAAXXZ" + // GNU: call void @_ZN1CIiE25to_be_imported_explicitlyEv + c.to_be_imported_explicitly(); // implicitly instantiated here // MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ" // GNU: call void @_ZN1CIiE18not_to_be_importedEv @@ -46,5 +51,8 @@ void use() { // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ" // GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv -// MSC: declare dllimport void @"?not_to_be_imported@?$C@H@@QEAAXXZ" -// GNU: declare dllimport void @_ZN1CIiE18not_to_be_importedEv +// MSC: declare dllimport void @"?to_be_imported_explicitly@?$C@H@@QEAAXXZ" +// GNU: declare dllimport void @_ZN1CIiE25to_be_imported_explicitlyEv + +// MSC: define linkonce_odr dso_local void @"?not_to_be_imported@?$C@H@@QEAAXXZ" +// GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv >From 94d7cf7933a73410548dc787baf6d9eac5b0d60f Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Sun, 30 Nov 2025 10:03:12 +0900 Subject: [PATCH 4/6] add --implicit-check-not=dll{{in|ex}}port --- ...it_instantiation.exclude_from_dllexport.cpp | 18 ++++++++++++++---- ...it_instantiation.exclude_from_dllimport.cpp | 16 ++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp index 0a94bd4b1f6b6..a2d8e37acb626 100644 --- a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp @@ -1,10 +1,18 @@ -// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_ -// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ -// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ +// 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 + #define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation)) template <class T> @@ -36,7 +44,9 @@ template <class T> void C<T>::not_to_be_instantiated() noexcept {} // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any +// 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>; @@ -50,7 +60,7 @@ void use() { // 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 -}; +} // 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 diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp index b070259c2f048..fa44f3aaa6e8a 100644 --- a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp @@ -1,10 +1,18 @@ -// RUN: %clang_cc1 -triple x86_64-win32 -fms-extensions -emit-llvm -o - %s | FileCheck %s --check-prefixes=MSC --implicit-check-not=to_be_ -// RUN: %clang_cc1 -triple x86_64-mingw -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ -// RUN: %clang_cc1 -triple x86_64-cygwin -emit-llvm -o - %s | FileCheck %s --check-prefixes=GNU --implicit-check-not=to_be_ +// 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=dllimport +// 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=dllimport +// 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=dllimport // Test that __declspec(dllimport) doesn't instantiate entities marked with // the exclude_from_explicit_instantiation attribute unless marked as dllimport explicitly. +// MSC: ModuleID = {{.*}}exclude_from_dllimport.cpp +// MSC: source_filename = {{.*}}exclude_from_dllimport.cpp +// GNU: ModuleID = {{.*}}exclude_from_dllimport.cpp +// GNU: source_filename = {{.*}}exclude_from_dllimport.cpp + #define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation)) template <class T> @@ -46,7 +54,7 @@ void use() { // MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ" // GNU: call void @_ZN1CIiE18not_to_be_importedEv c.not_to_be_imported(); // implicitly instantiated here -}; +} // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ" // GNU: declare dllimport void @_ZN1CIiE14to_be_importedEv >From bdbeeac2ccefd653641ab115edf58408d8c12d91 Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Mon, 1 Dec 2025 21:15:38 +0900 Subject: [PATCH 5/6] add tests for the class-level attributes --- ...t_instantiation.exclude_from_dllexport.cpp | 32 +++++++++++++++++ ...t_instantiation.exclude_from_dllimport.cpp | 34 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp index a2d8e37acb626..eba5f24f2d5ae 100644 --- a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp @@ -37,12 +37,29 @@ 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 should be exported by the class-level attribute. + void to_be_exported() noexcept; + + // This also should be exported by the class-level attribute but currently not. + EXCLUDE_FROM_EXPLICIT_INSTANTIATION void also_to_be_exported() noexcept; +}; + +template <class T> void D<T>::to_be_exported() noexcept {} +template <class T> void D<T>::also_to_be_exported() noexcept {} + // MSC: $"?to_be_exported@?$C@H@@QEAAXXZ" = comdat any +// MSC: $"?to_be_exported@?$D@H@@QEAAXXZ" = comdat any // MSC: $"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" = comdat any // MSC: $"?not_to_be_exported@?$C@H@@QEAAXXZ" = comdat any +// MSC: $"?also_to_be_exported@?$D@H@@QEAAXXZ" = comdat any // GNU: $_ZN1CIiE14to_be_exportedEv = comdat any +// GNU: $_ZN1DIiE14to_be_exportedEv = comdat any // GNU: $_ZN1CIiE25to_be_exported_explicitlyEv = comdat any // GNU: $_ZN1CIiE18not_to_be_exportedEv = comdat any +// GNU: $_ZN1DIiE19also_to_be_exportedEv = comdat any // 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" @@ -50,6 +67,12 @@ template <class T> void C<T>::not_to_be_instantiated() noexcept {} // 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{{.*}} void @"?to_be_exported@?$D@H@@QEAAXXZ" +// GNU: define weak_odr dso_local dllexport{{.*}} ptr @_ZN1DIiEaSERKS0_ +// GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1DIiE14to_be_exportedEv +template struct D<int>; + void use() { C<int> c; @@ -60,6 +83,12 @@ void use() { // 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> d; + + // MSC: call void @"?also_to_be_exported@?$D@H@@QEAAXXZ" + // GNU: call void @_ZN1DIiE19also_to_be_exportedEv + d.also_to_be_exported(); // implicitly instantiated here } // MSC: define weak_odr dso_local dllexport{{.*}} void @"?to_be_exported_explicitly@?$C@H@@QEAAXXZ" @@ -67,3 +96,6 @@ void use() { // 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 @"?also_to_be_exported@?$D@H@@QEAAXXZ" +// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_exportedEv diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp index fa44f3aaa6e8a..f263ff070d852 100644 --- a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp @@ -36,10 +36,28 @@ template <class T> void C<T>::to_be_imported() noexcept {} template <class T> void C<T>::not_to_be_imported() 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(dllimport) D { + // This will be imported by the class-level attribute. + void to_be_imported() noexcept; + + // This also should be imported by the class-level attribute but currently not. + EXCLUDE_FROM_EXPLICIT_INSTANTIATION void also_to_be_imported() noexcept; +}; + +template <class T> void D<T>::to_be_imported() noexcept {} +template <class T> void D<T>::also_to_be_imported() noexcept {} + // MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any +// MSC: $"?also_to_be_imported@?$D@H@@QEAAXXZ" = comdat any // GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any +// GNU: $_ZN1DIiE19also_to_be_importedEv = comdat any + extern template struct __declspec(dllimport) C<int>; +extern template struct D<int>; + void use() { C<int> c; @@ -54,6 +72,16 @@ void use() { // MSC: call void @"?not_to_be_imported@?$C@H@@QEAAXXZ" // GNU: call void @_ZN1CIiE18not_to_be_importedEv c.not_to_be_imported(); // implicitly instantiated here + + D<int> d; + + // MSC: call void @"?to_be_imported@?$D@H@@QEAAXXZ" + // GNU: call void @_ZN1DIiE14to_be_importedEv + d.to_be_imported(); // implicitly instantiated here + + // MSC: call void @"?also_to_be_imported@?$D@H@@QEAAXXZ" + // GNU: call void @_ZN1DIiE19also_to_be_importedEv + d.also_to_be_imported(); // implicitly instantiated here } // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ" @@ -64,3 +92,9 @@ void use() { // MSC: define linkonce_odr dso_local void @"?not_to_be_imported@?$C@H@@QEAAXXZ" // GNU: define linkonce_odr dso_local void @_ZN1CIiE18not_to_be_importedEv + +// MSC: declare dllimport void @"?to_be_imported@?$D@H@@QEAAXXZ" +// GNU: declare dllimport void @_ZN1DIiE14to_be_importedEv + +// MSC: define linkonce_odr dso_local void @"?also_to_be_imported@?$D@H@@QEAAXXZ" +// GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_importedEv >From bf8df954888217b07b5c5f99f0b98cb5d58e0e6d Mon Sep 17 00:00:00 2001 From: kikairoya <[email protected]> Date: Sun, 30 Nov 2025 07:37:12 +0900 Subject: [PATCH 6/6] add tests for virtual member functions --- ...t_instantiation.exclude_from_dllexport.cpp | 64 +++++++++++++++++ ...t_instantiation.exclude_from_dllimport.cpp | 69 +++++++++++++++++++ 2 files changed, 133 insertions(+) diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp index eba5f24f2d5ae..4bff5a828f34c 100644 --- a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllexport.cpp @@ -50,16 +50,54 @@ struct __declspec(dllexport) D { template <class T> void D<T>::to_be_exported() noexcept {} template <class T> void D<T>::also_to_be_exported() 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@?$D@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: $"?also_to_be_exported@?$D@H@@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: $_ZN1DIiE14to_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: $_ZN1DIiE19also_to_be_exportedEv = 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" @@ -73,6 +111,22 @@ template struct __declspec(dllexport) C<int>; // GNU: define weak_odr dso_local dllexport{{.*}} void @_ZN1DIiE14to_be_exportedEv template struct D<int>; +// 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>; + void use() { C<int> c; @@ -99,3 +153,13 @@ void use() { // MSC: define linkonce_odr dso_local void @"?also_to_be_exported@?$D@H@@QEAAXXZ" // GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_exportedEv + +// 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 diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp index f263ff070d852..6cb169bb5a4d8 100644 --- a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp +++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.exclude_from_dllimport.cpp @@ -49,15 +49,59 @@ struct __declspec(dllimport) D { template <class T> void D<T>::to_be_imported() noexcept {} template <class T> void D<T>::also_to_be_imported() noexcept {} +// Interaction with VTables. +template <class T> +struct E { + // For the MSVC ABI: this constructor causes implicit instantiation of + // the VTable, which should trigger instantiating all virtual member + // functions regardless `exclude_from_explicit_instantiation` but currently not. + // For the Itanium ABI: Emitting the VTable is suppressed by implicit + // instantiation declaration so virtual member functions won't be instantiated. + EXCLUDE_FROM_EXPLICIT_INSTANTIATION explicit E(int); + + // This constructor doesn't trigger the instantiation of the VTable. + // In this case, declaration of virtual member functions are absent too. + explicit E(long); + + // The body of this shouldn't be emitted since instantiation is suppressed + // by the explicit instantiation declaration. + virtual void to_be_imported() noexcept; + + // The body of this should be emitted if the VTable is instantiated, even if + // the instantiation of this class template is declared with dllimport. + EXCLUDE_FROM_EXPLICIT_INSTANTIATION virtual void to_be_instantiated() noexcept; + + // The body of this shouldn't be emitted since that comes from an external DLL. + EXCLUDE_FROM_EXPLICIT_INSTANTIATION __declspec(dllimport) virtual void to_be_imported_explicitly() noexcept; + +}; + +template <class T> E<T>::E(int) {} +template <class T> E<T>::E(long) {} +template <class T> void E<T>::to_be_imported() noexcept {} +template <class T> void E<T>::to_be_instantiated() noexcept {} + // MSC: $"?not_to_be_imported@?$C@H@@QEAAXXZ" = comdat any // MSC: $"?also_to_be_imported@?$D@H@@QEAAXXZ" = comdat any // GNU: $_ZN1CIiE18not_to_be_importedEv = comdat any // GNU: $_ZN1DIiE19also_to_be_importedEv = comdat any +// GNU: @_ZTV1EIiE = external dllimport unnamed_addr +// GNU: @_ZTV1EIjE = external unnamed_addr + +// MSC: @0 = private unnamed_addr constant {{.*}}, comdat($"??_S?$E@H@@6B@") +// MSC: @1 = private unnamed_addr constant {{.*}}, comdat($"??_7?$E@I@@6B@") +// MSC: @"??_S?$E@H@@6B@" = +// MSC: @"??_7?$E@I@@6B@" = extern template struct __declspec(dllimport) C<int>; extern template struct D<int>; +extern template struct __declspec(dllimport) E<int>; // $E@H, 1EIiE +extern template struct E<unsigned>; // $E@I, 1EIjE +extern template struct __declspec(dllimport) E<long int>; // $E@J, 1EIlE +extern template struct E<unsigned long int>; // $E@K, 1EImE + void use() { C<int> c; @@ -82,6 +126,14 @@ void use() { // MSC: call void @"?also_to_be_imported@?$D@H@@QEAAXXZ" // GNU: call void @_ZN1DIiE19also_to_be_importedEv d.also_to_be_imported(); // implicitly instantiated here + + E<int> ei{1}; + + E<unsigned> ej{1}; + + E<long int> el{1L}; + + E<unsigned long int> eu{1L}; } // MSC: declare dllimport void @"?to_be_imported@?$C@H@@QEAAXXZ" @@ -98,3 +150,20 @@ void use() { // MSC: define linkonce_odr dso_local void @"?also_to_be_imported@?$D@H@@QEAAXXZ" // GNU: define linkonce_odr dso_local void @_ZN1DIiE19also_to_be_importedEv + +// MSC: declare dllimport noundef ptr @"??0?$E@J@@QEAA@J@Z" +// MSC: declare dso_local noundef ptr @"??0?$E@K@@QEAA@J@Z" +// GNU: define linkonce_odr dso_local void @_ZN1EIiEC1Ei +// GNU: define linkonce_odr dso_local void @_ZN1EIjEC1Ei +// GNU: declare dllimport void @_ZN1EIlEC1El +// GNU: declare dso_local void @_ZN1EImEC1El +// GNU: define linkonce_odr dso_local void @_ZN1EIiEC2Ei +// GNU: define linkonce_odr dso_local void @_ZN1EIjEC2Ei + +// MSC: declare dllimport void @"?to_be_imported@?$E@H@@UEAAXXZ" +// MSC: declare dso_local void @"?to_be_instantiated@?$E@H@@UEAAXXZ" +// MSC: declare dllimport void @"?to_be_imported_explicitly@?$E@H@@UEAAXXZ" + +// MSC: declare dso_local void @"?to_be_imported@?$E@I@@UEAAXXZ" +// MSC: declare dso_local void @"?to_be_instantiated@?$E@I@@UEAAXXZ" +// MSC: declare dllimport void @"?to_be_imported_explicitly@?$E@I@@UEAAXXZ" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
