-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[clang] Fix loss of dllexport
for exported template specialization
#93302
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
[clang] Fix loss of dllexport
for exported template specialization
#93302
Conversation
In commit 0a20f54, "Better codegen support for DLL attributes being dropped after the first declaration (PR20792)", code was added to enable "dropping" of DLL attributes. The specific issue and example given was related to `dllimport` and this was the test case that was added in that commit. However, the code also included the "dropping" of `dllexport` but no test case for this was added. This "dropping" of `dllexport` can cause a `dllexport` template specialization to incorrectly lose its `dllexport` attribute as shown by the test case in this patch. As there appears to be no need for the "dropping" of `dllexport`, remove this code to fix this issue.
@llvm/pr-subscribers-clang-codegen Author: Andrew Ng (nga888) ChangesIn commit 0a20f54, "Better codegen support for DLL attributes being dropped after the first declaration (PR20792)", code was added to enable "dropping" of DLL attributes. The specific issue and example given was related to However, the code also included the "dropping" of As there appears to be no need for the "dropping" of Full diff: https://github.com/llvm/llvm-project/pull/93302.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e4774a587707a..ae8104fe1c4cb 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4554,8 +4554,11 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
Entry->setLinkage(llvm::Function::ExternalLinkage);
}
- // Handle dropped DLL attributes.
- if (D && !D->hasAttr<DLLImportAttr>() && !D->hasAttr<DLLExportAttr>() &&
+ // Handle dropped dllimport.
+ if (D &&
+ (Entry->getDLLStorageClass() ==
+ llvm::GlobalVariable::DLLImportStorageClass) &&
+ !D->hasAttr<DLLImportAttr>() &&
!shouldMapVisibilityToDLLExport(cast_or_null<NamedDecl>(D))) {
Entry->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
setDSOLocal(Entry);
@@ -4849,9 +4852,12 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, llvm::Type *Ty,
Entry->setLinkage(llvm::Function::ExternalLinkage);
}
- // Handle dropped DLL attributes.
- if (D && !D->hasAttr<DLLImportAttr>() && !D->hasAttr<DLLExportAttr>() &&
- !shouldMapVisibilityToDLLExport(D))
+ // Handle dropped dllimport.
+ if (D &&
+ (Entry->getDLLStorageClass() ==
+ llvm::GlobalVariable::DLLImportStorageClass) &&
+ !D->hasAttr<DLLImportAttr>() &&
+ !shouldMapVisibilityToDLLExport(cast_or_null<NamedDecl>(D)))
Entry->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
if (LangOpts.OpenMP && !LangOpts.OpenMPSimd && D)
diff --git a/clang/test/CodeGenCXX/windows-instantiate-dllexport-template-specialization.cpp b/clang/test/CodeGenCXX/windows-instantiate-dllexport-template-specialization.cpp
new file mode 100644
index 0000000000000..97f341ba1f909
--- /dev/null
+++ b/clang/test/CodeGenCXX/windows-instantiate-dllexport-template-specialization.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -triple i686-windows -fdeclspec -emit-llvm %s -o - | FileCheck %s -check-prefix CHECK-MS
+// RUN: %clang_cc1 -triple i686-windows-itanium -fdeclspec -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-scei-ps4 -fdeclspec -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-sie-ps5 -fdeclspec -emit-llvm %s -o - | FileCheck %s
+
+struct s {
+ template <bool b = true> static bool f();
+};
+
+template <typename T> bool template_using_f(T) { return s::f(); }
+
+bool use_template_using_f() { return template_using_f(0); }
+
+template<>
+bool __declspec(dllexport) s::f<true>() { return true; }
+
+// CHECK-MS: dllexport {{.*}} @"??$f@$00@s@@SA_NXZ"
+// CHECK: dllexport {{.*}} @_ZN1s1fILb1EEEbv
|
@llvm/pr-subscribers-clang Author: Andrew Ng (nga888) ChangesIn commit 0a20f54, "Better codegen support for DLL attributes being dropped after the first declaration (PR20792)", code was added to enable "dropping" of DLL attributes. The specific issue and example given was related to However, the code also included the "dropping" of As there appears to be no need for the "dropping" of Full diff: https://github.com/llvm/llvm-project/pull/93302.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e4774a587707a..ae8104fe1c4cb 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4554,8 +4554,11 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
Entry->setLinkage(llvm::Function::ExternalLinkage);
}
- // Handle dropped DLL attributes.
- if (D && !D->hasAttr<DLLImportAttr>() && !D->hasAttr<DLLExportAttr>() &&
+ // Handle dropped dllimport.
+ if (D &&
+ (Entry->getDLLStorageClass() ==
+ llvm::GlobalVariable::DLLImportStorageClass) &&
+ !D->hasAttr<DLLImportAttr>() &&
!shouldMapVisibilityToDLLExport(cast_or_null<NamedDecl>(D))) {
Entry->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
setDSOLocal(Entry);
@@ -4849,9 +4852,12 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, llvm::Type *Ty,
Entry->setLinkage(llvm::Function::ExternalLinkage);
}
- // Handle dropped DLL attributes.
- if (D && !D->hasAttr<DLLImportAttr>() && !D->hasAttr<DLLExportAttr>() &&
- !shouldMapVisibilityToDLLExport(D))
+ // Handle dropped dllimport.
+ if (D &&
+ (Entry->getDLLStorageClass() ==
+ llvm::GlobalVariable::DLLImportStorageClass) &&
+ !D->hasAttr<DLLImportAttr>() &&
+ !shouldMapVisibilityToDLLExport(cast_or_null<NamedDecl>(D)))
Entry->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
if (LangOpts.OpenMP && !LangOpts.OpenMPSimd && D)
diff --git a/clang/test/CodeGenCXX/windows-instantiate-dllexport-template-specialization.cpp b/clang/test/CodeGenCXX/windows-instantiate-dllexport-template-specialization.cpp
new file mode 100644
index 0000000000000..97f341ba1f909
--- /dev/null
+++ b/clang/test/CodeGenCXX/windows-instantiate-dllexport-template-specialization.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -triple i686-windows -fdeclspec -emit-llvm %s -o - | FileCheck %s -check-prefix CHECK-MS
+// RUN: %clang_cc1 -triple i686-windows-itanium -fdeclspec -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-scei-ps4 -fdeclspec -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-sie-ps5 -fdeclspec -emit-llvm %s -o - | FileCheck %s
+
+struct s {
+ template <bool b = true> static bool f();
+};
+
+template <typename T> bool template_using_f(T) { return s::f(); }
+
+bool use_template_using_f() { return template_using_f(0); }
+
+template<>
+bool __declspec(dllexport) s::f<true>() { return true; }
+
+// CHECK-MS: dllexport {{.*}} @"??$f@$00@s@@SA_NXZ"
+// CHECK: dllexport {{.*}} @_ZN1s1fILb1EEEbv
|
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.
Does the test exercise both modified paths? I'm guessing it only exercises GetOrCreateLLVMFunction, but I'm not a codegen expert.
clang/lib/CodeGen/CodeGenModule.cpp
Outdated
(Entry->getDLLStorageClass() == | ||
llvm::GlobalVariable::DLLImportStorageClass) && | ||
!D->hasAttr<DLLImportAttr>() && | ||
!shouldMapVisibilityToDLLExport(cast_or_null<NamedDecl>(D))) |
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.
!shouldMapVisibilityToDLLExport(cast_or_null<NamedDecl>(D))) | |
!shouldMapVisibilityToDLLExport(cast<NamedDecl>(D))) |
D is non-null 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.
This part of the code isn't directly related to the change but I assume I can go ahead and change it?
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.
Sorry, the above comment should have been in reply to the instance above. This cast shouldn't be needed at all. I suspect a copy & paste error on my behalf!
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've fixed up both instances of the call to shouldMapVisibilityToDLLExport()
.
clang/lib/CodeGen/CodeGenModule.cpp
Outdated
if (D && | ||
(Entry->getDLLStorageClass() == | ||
llvm::GlobalVariable::DLLImportStorageClass) && | ||
!D->hasAttr<DLLImportAttr>() && | ||
!shouldMapVisibilityToDLLExport(cast_or_null<NamedDecl>(D))) { |
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.
!shouldMapVisibilityToDLLExport(cast_or_null<NamedDecl>(D))) { | |
!shouldMapVisibilityToDLLExport(cast<NamedDecl>(D))) { |
D is non-null here.
Only 1 path is "exercised" but I believe neither path of the removed code was exercised previously and no other tests are affected by the code removal. |
✅ With the latest revision this PR passed the C/C++ code formatter. |
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.
In commit 0a20f54, "Better codegen support for DLL attributes being dropped after the first declaration (PR20792)", code was added to enable "dropping" of DLL attributes.
Wow, how is that 10 years ago already?
I'm not sure this is the right place for the fix though. Note that even though my patch added handling for dropping dllexport, we still do export f
in this case:
__declspec(dllexport) int f(int x);
int user(int x) {
return f(x);
}
int f(int x) { return 1; }
Because f
is still dllexport at the AST level, as it should. That's handled by the code which deals with redeclarations.
I suspect that's where we should fix your case also: we should figure out why s::f<true>()
isn't(?) marked dllexport in the AST.
In your example, the "early" declaration of
It is the "late" specialization of the template that has I understand that removing the code that drops One existing scenario where this patch's test case does result in the expected export is with the option |
I still think fixing the AST would be better. That would also fix the "dllimport version" of your test case:
Here, |
I think the difference in this case, is that this behaviour is unlikely to cause a "real" issue. The import of a definition/specialization is already a bit of an anomaly, so dropping |
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.
The import of a definition/specialization is already a bit of an anomaly, so dropping dllimport is probably reasonable.
Well, you're exporting a specialization. How is it going to get imported?
Perhaps me leaving the definition in there was unnecessary. How about just:
template<>
bool __declspec(dllimport) s::f<true>();
(https://godbolt.org/z/nqE7zPWYP)
Anyway, my point is that your patch is only working around half the bug. It seems to me that the proper fix has to be to get the attributes right in the AST.
The "importing" side is just:
This test case was derived from code that is part of UnrealEngine 5.4 which builds fine with MSVC, i.e. no undefined symbol for
So in this test case for exported template specializations, there isn't really another "half" to the issue. |
I looked at this some more. Here's a slightly reduced version of your test case:
What happens (when building with
I think the "surprise" is that the latter call to
If we're concerned about the extra function call in a common codepath, we could only do this if there is a dll attribute on the |
Thanks for your time and the update. Yes, this is what I also discovered whilst debugging this issue. I also came to the same conclusion as you but when I tried using It was after this failure that I then started to question the whole removing of |
Ok, I have confirmed that the The code changes in this patch have been tested across @zmodem, how would you like to proceed? If the removal of the dropping of |
Great!
Was this about I think we should proceed with switching to |
OK, so this change in behaviour is desirable and matches MinGW? The odd thing is that this behaviour doesn't change on the
I think it might be easier and simpler to go with a new PR. |
@zmodem, forgot to ask, and not directly linked to this patch as such, but shouldn't there be a test case added for the dropping of |
I could have sworn it failed... but I was also using main from a few weeks ago. I think the test doesn't fail anymore because sized delete was enabled in 130e93c. Maybe we should specify -fno-sized-deallocation or update the test somehow.
I can't think of a test case that would hit it. |
Just for clarification, you want to pursue the |
Yes. |
Closing this PR in favour of PR #94664. |
In commit 0a20f54, "Better codegen support for DLL attributes being dropped after the first declaration (PR20792)", code was added to enable "dropping" of DLL attributes. The specific issue and example given was related to
dllimport
and this was the test case that was added in that commit.However, the code also included the "dropping" of
dllexport
but no test case for this was added. This "dropping" ofdllexport
can cause adllexport
template specialization to incorrectly lose itsdllexport
attribute as shown by the test case in this patch.As there appears to be no need for the "dropping" of
dllexport
, remove this code to fix this issue.