From 96a7860f7917a3568b63e25813ff2079d5a39e93 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Mon, 12 Nov 2018 08:38:10 +0000 Subject: [PATCH] clang-cl: Add documentation for /Zc:dllexportInlines- Differential revision: https://reviews.llvm.org/D54319 llvm-svn: 346639 --- clang/docs/UsersManual.rst | 76 +++++++++++++++++++ clang/include/clang/Driver/CLCompatOptions.td | 6 +- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 87a6d3e0025d..a7f2f8145e87 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2947,6 +2947,8 @@ Execute ``clang-cl /?`` to see a list of supported options: /Yc Generate a pch file for all code up to and including /Yu Load a pch file and use it instead of all code up to and including /Z7 Enable CodeView debug information in object files + /Zc:dllexportInlines- Don't dllexport/import inline member functions of dllexport/import classes + /Zc:dllexportInlines dllexport/import inline member functions of dllexport/import classes (default) /Zc:sizedDealloc- Disable C++14 sized global deallocation functions /Zc:sizedDealloc Enable C++14 sized global deallocation functions /Zc:strictStrings Treat string literals as const @@ -3096,6 +3098,80 @@ driver. Regardless of where they appear in the command line, the ``/clang:`` arguments are treated as if they were passed at the end of the clang-cl command line. +The /Zc:dllexportInlines- Option +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This causes the class-level `dllexport` and `dllimport` attributes not to be +applied to inline member functions, as they otherwise would. For example, in +the code below `S::foo()` would normally be defined and exported by the DLL, +but when using the ``/Zc:dllexportInlines-`` flag it is not: + +.. code-block:: c + + struct __declspec(dllexport) S { + void foo() {} + } + +This has the benefit that the compiler doesn't need to emit a definition of +`S::foo()` in every translation unit where the declaration is included, as it +would otherwise do to ensure there's a definition in the DLL even if it's not +used there. If the declaration occurs in a header file that's widely used, this +can save significant compilation time and output size. It also reduces the +number of functions exported by the DLL similarly to what +``-fvisibility-inlines-hidden`` does for shared objects on ELF and Mach-O. +Since the function declaration comes with an inline definition, users of the +library can use that definition directly instead of importing it from the DLL. + +Note that the Microsoft Visual C++ compiler does not support this option, and +if code in a DLL is compiled with ``/Zc:dllexportInlines-``, the code using the +DLL must be compiled in the same way so that it doesn't attempt to dllimport +the inline member functions. The reverse scenario should generally work though: +a DLL compiled without this flag (such as a system library compiled with Visual +C++) can be referenced from code compiled using the flag, meaning that the +referencing code will use the inline definitions instead of importing them from +the DLL. + +Also note that like when using ``-fvisibility-inlines-hidden``, the address of +`S::foo()` will be different inside and outside the DLL, breaking the C/C++ +standard requirement that functions have a unique address. + +The flag does not apply to explicit class template instantiation definitions or +declarations, as those are typically used to explicitly provide a single +definition in a DLL, (dllexported instantiation definition) or to signal that +the definition is available elsewhere (dllimport instantiation declaration). It +also doesn't apply to inline members with static local variables, to ensure +that the same instance of the variable is used inside and outside the DLL. + +Using this flag can cause problems when inline functions that would otherwise +be dllexported refer to internal symbols of a DLL. For example: + +.. code-block:: c + + void internal(); + + struct __declspec(dllimport) S { + void foo() { internal(); } + } + +Normally, references to `S::foo()` would use the definition in the DLL from +which it was exported, and which presumably also has the definition of +`internal()`. However, when using ``/Zc:dllexportInlines-``, the inline +definition of `S::foo()` is used directly, resulting in a link error since +`internal()` is not available. Even worse, if there is an inline definition of +`internal()` containing a static local variable, we will now refer to a +different instance of that variable than in the DLL: + +.. code-block:: c + + inline int internal() { static int x; return x++; } + + struct __declspec(dllimport) S { + int foo() { return internal(); } + } + +This could lead to very subtle bugs. Using ``-fvisibility-inlines-hidden`` can +lead to the same issue. + The /fallback Option ^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Driver/CLCompatOptions.td b/clang/include/clang/Driver/CLCompatOptions.td index 6d4382658fcd..f4a4488c72ac 100644 --- a/clang/include/clang/Driver/CLCompatOptions.td +++ b/clang/include/clang/Driver/CLCompatOptions.td @@ -335,8 +335,10 @@ def _SLASH_Yu : CLJoined<"Yu">, MetaVarName<"">; def _SLASH_Y_ : CLFlag<"Y-">, HelpText<"Disable precompiled headers, overrides /Yc and /Yu">; -def _SLASH_Zc_dllexportInlines : CLFlag<"Zc:dllexportInlines">; -def _SLASH_Zc_dllexportInlines_ : CLFlag<"Zc:dllexportInlines-">; +def _SLASH_Zc_dllexportInlines : CLFlag<"Zc:dllexportInlines">, + HelpText<"dllexport/import inline member functions of dllexport/import classes (default)">; +def _SLASH_Zc_dllexportInlines_ : CLFlag<"Zc:dllexportInlines-">, + HelpText<"Don't dllexport/import inline member functions of dllexport/import classes">; def _SLASH_Fp : CLJoined<"Fp">, HelpText<"Set pch filename (with /Yc and /Yu)">, MetaVarName<"">;