-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[llvm] get cl::opt instantiations working with MSVC DLL build #147810
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?
Conversation
5a21933
to
eb5a734
Compare
@llvm/pr-subscribers-llvm-support Author: Andrew Rogers (andrurogerz) ChangesPurposeThis patch is one in a series of code-mods that annotate LLVM’s public interface for export. This patch annotates the BackgroundThis effort is tracked in #109483. Additional context is provided in this discourse, and documentation for Annotating the OverviewThere are two specific issues that appear when exporting the
ValidationWindows with MSVC Full diff: https://github.com/llvm/llvm-project/pull/147810.diff 2 Files Affected:
diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h
index adaa75cc6c348..ab3ab9b52ac12 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -1518,11 +1518,20 @@ class opt
[](const typename ParserClass::parser_data_type &) {};
};
-extern template class opt<unsigned>;
-extern template class opt<int>;
-extern template class opt<std::string>;
-extern template class opt<char>;
-extern template class opt<bool>;
+#if !defined(LLVM_ENABLE_LLVM_EXPORT_ANNOTATIONS) || \
+ !(defined(_MSC_VER) && !defined(__clang__))
+// Only instantiate opt<std::string> when not building a Windows DLL with MSVC.
+// When exporting opt<std::string>, MSVC cl implicitly exports symbols for
+// std::basic_string through transitive inheritance via std::string. These
+// symbols may appear in other TUs with different linkage, leading to duplicate
+// symbol conflicts.
+extern template class LLVM_TEMPLATE_ABI opt<std::string>;
+#endif
+
+extern template class LLVM_TEMPLATE_ABI opt<unsigned>;
+extern template class LLVM_TEMPLATE_ABI opt<int>;
+extern template class LLVM_TEMPLATE_ABI opt<char>;
+extern template class LLVM_TEMPLATE_ABI opt<bool>;
//===----------------------------------------------------------------------===//
// Default storage class definition: external storage. This implementation
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index d5c3cba13e030..c9541289aa3bd 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -68,11 +68,21 @@ template class LLVM_EXPORT_TEMPLATE basic_parser<float>;
template class LLVM_EXPORT_TEMPLATE basic_parser<std::string>;
template class LLVM_EXPORT_TEMPLATE basic_parser<char>;
-template class opt<unsigned>;
-template class opt<int>;
-template class opt<std::string>;
-template class opt<char>;
-template class opt<bool>;
+#if !defined(LLVM_ENABLE_LLVM_EXPORT_ANNOTATIONS) || \
+ !(defined(_MSC_VER) && !defined(__clang__))
+// Only instantiate opt<std::string> when not building a Windows DLL with MSVC.
+// When exporting opt<std::string>, MSVC cl implicitly exports symbols for
+// std::basic_string through transitive inheritance via std::string. These
+// symbols may appear in other TUs with different linkage, leading to duplicate
+// symbol conflicts.
+template class LLVM_EXPORT_TEMPLATE opt<std::string>;
+#endif
+
+template class LLVM_EXPORT_TEMPLATE opt<bool>;
+template class LLVM_EXPORT_TEMPLATE opt<char>;
+template class LLVM_EXPORT_TEMPLATE opt<int>;
+template class LLVM_EXPORT_TEMPLATE opt<unsigned>;
+
} // namespace cl
} // namespace llvm
@@ -95,6 +105,15 @@ void parser<float>::anchor() {}
void parser<std::string>::anchor() {}
void parser<char>::anchor() {}
+// These anchor functions instantiate opt<T> and reference its virtual
+// destructor to ensure MSVC exports the corresponding vtable and typeinfo when
+// building a Windows DLL. Without an explicit reference, MSVC may omit the
+// instantiation at link time even if it is marked DLL-export.
+void opt_bool_anchor() { opt<bool> anchor{""}; }
+void opt_char_anchor() { opt<char> anchor{""}; }
+void opt_int_anchor() { opt<int> anchor{""}; }
+void opt_unsigned_anchor() { opt<unsigned> anchor{""}; }
+
//===----------------------------------------------------------------------===//
const static size_t DefaultPad = 2;
|
@compnerd @vgvassilev this one is a bit weird. I did some digging, and MSVC definitely has some quirks around exporting template instances. I will do another pass through the other exported template instances to see if there are any similar situations that didn't fail to link. |
Purpose
This patch is one in a series of code-mods that annotate LLVM’s public interface for export. This patch annotates the
llvm::cl::opt
explicit template instantiations for export withLLVM_TEMPLATE_ABI
andLLVM_EXPORT_TEMPLATE
. This annotation currently has no meaningful impact on the LLVM build; however, it is a prerequisite to support an LLVM Windows DLL (shared library) build.Background
This effort is tracked in #109483. Additional context is provided in this discourse, and documentation for
LLVM_ABI
and related annotations is found in the LLVM repo here.Annotating the
llvm::cl::opt
template instances for DLL export was not straight-forward like other explicit template instances that have already been annotated. Annotating them as documented here results in link errors when building a Windows DLL using MSVC.Overview
There are two specific issues that appear when exporting the
llvm::cl::opt
templates and compiling a Windows DLL with MSVC:opt<std::string>
. This is because MSVC exports all ancestor classes when exporting an instantiated template class. Since one ofopt
's ancestor classes is its type argument (viaopt_storage
), it is an ancestor ofstd::string
. Therefore, if we exportopt<std::string>
from the LLVM DLL, MSVC forcesstd::basic_string
to also be exported. This leads to duplicate symbol errors and generally seems like a bad idea. Compiling withclang-cl
does not exhibit this behavior.opt
template instances other thanopt<bool>
get optimized out because they are not referenced in the TU (opt<bool>
actually is). It is unclear exactly why MSVC optimizes these template instances away, butclang-cl
does not. Adding explicit references to the instantiatedopt
template classes' vtables via implicit virtual destructor forces MSVC to export them.Validation
Windows with MSVC
Windows with Clang