Skip to content

Commit 0489222

Browse files
authored
[llvm] get cl::opt instantiations working with MSVC DLL build (#147810)
## 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 with `LLVM_TEMPLATE_ABI` and `LLVM_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](https://discourse.llvm.org/t/psa-annotating-llvm-public-interface/85307), and documentation for `LLVM_ABI` and related annotations is found in the LLVM repo [here](https://github.com/llvm/llvm-project/blob/main/llvm/docs/InterfaceExportAnnotations.rst). 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](https://github.com/llvm/llvm-project/blob/main/llvm/docs/InterfaceExportAnnotations.rst#templates) 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: 1. We cannot export `opt<std::string>`. This is because MSVC exports all ancestor classes when exporting an instantiated template class. Since one of `opt`'s ancestor classes is its type argument (via `opt_storage`), it is an ancestor of `std::string`. Therefore, if we export `opt<std::string>` from the LLVM DLL, MSVC forces `std::basic_string` to also be exported. This leads to duplicate symbol errors and generally seems like a bad idea. Compiling with `clang-cl` does not exhibit this behavior. 2. The `opt` template instances other than `opt<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, but `clang-cl` does not. Adding explicit references to the instantiated `opt` template classes' vtables via implicit virtual destructor forces MSVC to export them. ## Validation Windows with MSVC Windows with Clang
1 parent 967626b commit 0489222

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

llvm/include/llvm/Support/CommandLine.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,11 +1518,18 @@ class opt
15181518
[](const typename ParserClass::parser_data_type &) {};
15191519
};
15201520

1521-
extern template class opt<unsigned>;
1522-
extern template class opt<int>;
1523-
extern template class opt<std::string>;
1524-
extern template class opt<char>;
1525-
extern template class opt<bool>;
1521+
#if !(defined(LLVM_ENABLE_LLVM_EXPORT_ANNOTATIONS) && defined(_MSC_VER))
1522+
// Only instantiate opt<std::string> when not building a Windows DLL. When
1523+
// exporting opt<std::string>, MSVC implicitly exports symbols for
1524+
// std::basic_string through transitive inheritance via std::string. These
1525+
// symbols may appear in clients, leading to duplicate symbol conflicts.
1526+
extern template class LLVM_TEMPLATE_ABI opt<std::string>;
1527+
#endif
1528+
1529+
extern template class LLVM_TEMPLATE_ABI opt<unsigned>;
1530+
extern template class LLVM_TEMPLATE_ABI opt<int>;
1531+
extern template class LLVM_TEMPLATE_ABI opt<char>;
1532+
extern template class LLVM_TEMPLATE_ABI opt<bool>;
15261533

15271534
//===----------------------------------------------------------------------===//
15281535
// Default storage class definition: external storage. This implementation

llvm/lib/Support/CommandLine.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,19 @@ template class LLVM_EXPORT_TEMPLATE basic_parser<float>;
6868
template class LLVM_EXPORT_TEMPLATE basic_parser<std::string>;
6969
template class LLVM_EXPORT_TEMPLATE basic_parser<char>;
7070

71-
template class opt<unsigned>;
72-
template class opt<int>;
73-
template class opt<std::string>;
74-
template class opt<char>;
75-
template class opt<bool>;
71+
#if !(defined(LLVM_ENABLE_LLVM_EXPORT_ANNOTATIONS) && defined(_MSC_VER))
72+
// Only instantiate opt<std::string> when not building a Windows DLL. When
73+
// exporting opt<std::string>, MSVC implicitly exports symbols for
74+
// std::basic_string through transitive inheritance via std::string. These
75+
// symbols may appear in clients, leading to duplicate symbol conflicts.
76+
template class LLVM_EXPORT_TEMPLATE opt<std::string>;
77+
#endif
78+
79+
template class LLVM_EXPORT_TEMPLATE opt<bool>;
80+
template class LLVM_EXPORT_TEMPLATE opt<char>;
81+
template class LLVM_EXPORT_TEMPLATE opt<int>;
82+
template class LLVM_EXPORT_TEMPLATE opt<unsigned>;
83+
7684
} // namespace cl
7785
} // namespace llvm
7886

@@ -95,6 +103,15 @@ void parser<float>::anchor() {}
95103
void parser<std::string>::anchor() {}
96104
void parser<char>::anchor() {}
97105

106+
// These anchor functions instantiate opt<T> and reference its virtual
107+
// destructor to ensure MSVC exports the corresponding vtable and typeinfo when
108+
// building a Windows DLL. Without an explicit reference, MSVC may omit the
109+
// instantiation at link time even if it is marked DLL-export.
110+
void opt_bool_anchor() { opt<bool> anchor{""}; }
111+
void opt_char_anchor() { opt<char> anchor{""}; }
112+
void opt_int_anchor() { opt<int> anchor{""}; }
113+
void opt_unsigned_anchor() { opt<unsigned> anchor{""}; }
114+
98115
//===----------------------------------------------------------------------===//
99116

100117
const static size_t DefaultPad = 2;

0 commit comments

Comments
 (0)