Skip to content

Commit

Permalink
clang-cl: Add documentation for /Zc:dllexportInlines-
Browse files Browse the repository at this point in the history
Differential revision: https://reviews.llvm.org/D54319

llvm-svn: 346639
  • Loading branch information
zmodem committed Nov 12, 2018
1 parent 8443f88 commit 96a7860
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 2 deletions.
76 changes: 76 additions & 0 deletions clang/docs/UsersManual.rst
Expand Up @@ -2947,6 +2947,8 @@ Execute ``clang-cl /?`` to see a list of supported options:
/Yc<filename> Generate a pch file for all code up to and including <filename>
/Yu<filename> Load a pch file and use it instead of all code up to and including <filename>
/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
Expand Down Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^

Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Driver/CLCompatOptions.td
Expand Up @@ -335,8 +335,10 @@ def _SLASH_Yu : CLJoined<"Yu">,
MetaVarName<"<filename>">;
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<"<filename>">;

Expand Down

0 comments on commit 96a7860

Please sign in to comment.