[LLVMdev] Feedback required on proper dllexport/import implementation

Nico Rieck nico.rieck at gmail.com
Tue Apr 23 13:06:52 PDT 2013


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



More information about the llvm-dev mailing list