[LLVMdev] Feedback required on proper dllexport/import implementation

Nico Rieck
Tue, 23 Apr 2013 20:06:52 -0000

On 23.04.2013 19:10, Gao, Yunzhong wrote:
> I missed the discussion when I implemented dllexport/dllimport for our
local tree. I
> essentially implemented your approach#1. I was trying to avoid the various
> external_linkage + some_attribute approaches because it seems that
external_linkage
> would imply the external linkage without the dllimport/dllexport semantics,
and there
> may be existing compiler codes that rely on this semantics. If I used
external_linkage
> + some attribute, then any place in existing codes that uses
hasExternalLInkage() will
> have to be modified to check both the linkage type as well as the attribute
list, which
> seems a bit fragile to maintain; somebody somewhere will forget to check
the attributes
> when writing their new optimization or garbage collection passes.

What problems do you expect with another approach? I have a local
prototype that removes dllimport/export as linkage and uses function
attributes and an extension to globals. I decorate dllexported functions
with the Used attribute and it seems to work fine. This allows such
functions to have linkonce_odr and weak_odr linkage and not be discarded.

> I think dllimport inline functions are more closely related to the
available_externally
> linkage than linkonce_odr; no COMDAT is involved and no .linkonce discard
directive
> is needed. I would name it dllimport_inline or something similar.

Hm, I don't think so, because dllimported inline functions must never be
instantiated, or else you get duplicated symbols. They are only allowed
to be expanded. At the moment I solve this by checking for definitions
during codegen (this means they survived the inliner) and dropping their
body.

> While it makes sense to mimic MSVC behavior and MSDN doc for C++ codes, it
is
> not very clear to me what should be the correct semantics when a C99 inline
function
> carries a dllimport/dllexport declspec. [...]
> An alternative is to generate an error message whenever a C99 inline
function is used
> with a dllimport/dllexport declspec.

I agree. This should be rejected.

> In a similar manner, I wonder what should be the correct semantics of:
> inline __declspec(dllexport) int foo() { return 100; }
>
> I guess I could let dllexport imply "extern", so that an
out-of-line definition always gets
> generated, but is that the expected behavior?

Yes, such a function is always instanciated and exported.

> For the following test case, GCC ignores the dllimport attribute with a
warning (similar
> to clang's current behavior)
>
> /* test1.c */
> inline __declspec(dllimport) void foo();
> inline void foo();
> inline void foo() { }
> void foo_user() { foo(); }
> /* end of file */

Generally, MSVC allows a dllimport definition if the first declaration
is specified as inline. This test case should import foo() (or expand it
inline).

> But for the following two cases, GCC produces both an out-of-line
definition and also a
> reference to an dllimported symbol foo with no diagnostics. It seems wrong,
unless
> there is some problem with my build process.
>
> /* test2.c */
> inline void foo();
> __declspec(dllimport) void foo();
> inline void foo() { }
> void foo_user() { foo(); }
> /* end of file */

Having both seems wrong, as it leads to linker errors. MSVC rejects this
code because it does not allow redeclaring a function and adding dllimport.

> /* test3.c */
> inline void foo();
> __declspec(dllimport) void foo();
> __declspec(dllimport) void foo() { } // clang complains that this is a
redeclaration without
>                                                          //
"dllimport" attribute", which seems wrong...
> void foo_user() { foo(); }
> /* end of file */

Same as above.

> What are your thoughts?

I have one issue in mind where I wonder if it's beneficial to be less
strict than MSVC. As stated before, MSVC requires the inline keyword
together with dllimport. This extends to member functions and prevents
out-of-line definitions with inline:

   struct X1 {
     inline void foo();
     void bar();
   };
   void X::foo() {} // OK
   inline void X::bar() {} // Error

Same goes for class and function templates. I stumbled on this in libc++
where internal class templates are externed.


-Nico