Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

dllexport / inline issue

2013-03-29
2013-06-06
  • Geoff Worboys
    Geoff Worboys
    2013-03-29

    I have a problem with class function inlines when trying to use dllexport (migrating code to a shared library):  When using __attribute__((dllexport))  or __declspec(dllexport)  on a class, the class inline functions will not inline inside the compiled library when using -O2.  So all those little accessor functions have become function calls.  If the inline function is defined inside the class declaration then you get no warning of this, but if (as I usually do) you have the class function definition outside the class declaration then you get warnings if you use -Winline.

    I suspect the problem is with gcc - but given that it's about dllexport (ie Windows), I really wasn't sure where to turn.  I first saw the problem with mingw / gcc v4.7.2 and  I just now downloaded the rubenvb personal build with gcc v4.8.0 release and replicated the issue with that.  (I wanted to see if v4.8 may have resolved the problem already.)

    Full details can be found on the stackoverflow forum here, or I am happy to post a sample somewhere. … Or, you can tell me to go and talk to gcc.gnu.org if you think thats where I should be.

     
  • Jonathan Yong
    Jonathan Yong
    2013-03-29

    dllexport and inline at the same time does not make sense, it is either inlined, or not. What you want to do is to export those non-inlined only.

    You can do this by marking individual methods for export. I realize this is a bit of work, but you will need to do it this way to separate them.

     
  • Geoff Worboys
    Geoff Worboys
    2013-03-29

    If you believe all inline requests will always be honoured then it wouldn't make sense (and compilers would simply never export them), but that doesn't happen.  So compilers/linkers are built to deal with having both inline code definitions and possibly multiple addressable instances of inlined functions.  And even if all that was wrong/different, there would still be no reason why the compiler couldn't inline code inside the library itself - and that is the problem I am seeing.

    Note that gcc has -fvisibility-inlines-hidden option to prevent export of inlines and reduce the size of the DSO, otherwise they will be exported (not that this option solves my particular problem, I've tried it) - so gcc is obviously expecting to deal with inlines in dynamic objects.  This page describes how MSVC handles it, I presume gcc does something similar (over on Linux where export is automatic and doesn't need the dllexport attribute that is causing all my problems).

    But, having got that off my chest ;), your suggested work-around is appreciated.  It's very messy, but it does seem to work.  (I had already tried the reverse - various ways of hiding inlines from the export, but they don't fix the problem inside the library.)  I would have to implement your suggestion via macros so that the code can return or a relatively normal state for compilation by MSVC.  So I'm still hoping for a better option.  (As would be anyone looking to create shared libraries under Windows.)

     
  • Jonathan Yong
    Jonathan Yong
    2013-03-29

    Please drop any discussion comparing it to Linux, it does not work that way, elf visibility is not the same as dllexport.

    Think of it this way, if it was inlinable, why do you need to export it from the DLL? The definition is also already there next to the declaration, if inline fails, GCC just creates another copy.

    What are you trying to do by making a method both inline and dllexport?

     
  • rubenvb
    rubenvb
    2013-03-29

    Let's set some things straight:

    1. the inline keyword is not for inlining, it's for annotating a definition in a header file to not break the One Definition Rule. It is also an implementation defined suggestion to effectively "inline" calls to that function. Implementation defined equals "I don't count on it doing anything" in that regard, at least for me. GCC can decide what function calls to inline on its own.

    2. GCC seems incapable of inlining a function call to a dllexported function even if the definition is visible. This is IMHO a bug. There is no reason a function with an inline (not the keyword, but the intended use of the keyword) definition that is also dllexported should not be inlineable by the optimizer. In fact, dllexport should have no influence on optimizing calls to that function.

    The only thing the keyword does (following MSDN's described behavior) is make sure you can LoadLibrary the DLL and call the function contained in the DLL. Again, this is seperate from the missed optimization.

    Concluding: GCC is treating dllexport inline functions in a bad way: it is not considering their calls for inlining. Instead it should do:
    - create a dllexport definition somewhere for LoadLibrary to work (which it already does).
    - treat is as a normal inline function at call sites (which it apparantly, if I understand the OP correctly, does not currently do).

    Just my 2 cents

     
  • Geoff Worboys
    Geoff Worboys
    2013-03-29

    It's not that I'm trying to do both, I would be quite happy to have the inlines not exported, I just didn't realise the only way to do with gcc would be to mark each exported member explicitly.  I would also suggest that I am far from the only one not to realise this.

    For example.  Take a look at the small amount of boost source code that gets compiled into libraries.  For example take a look at boost/regex/v4/filter.hpp and the number of inline accessor methods in there.  None of those will be inlined in the boost dll created by current versions of mingw/mingw64.  I doubt it this is expected.

     
  • Geoff Worboys
    Geoff Worboys
    2013-03-29

    rubenvb, are you suggesting that I should take the problem to gcc.gnu.org?  (As I said earlier, I wasn't sure where to take it.)

     
  • rubenvb
    rubenvb
    2013-03-29

    First I'd make a case about a missed optimization oppurtunity by creating a simple test case with two or three identical classes but different inline/dllexport things, and then look at the assembly for each case. Pretty much what you have on SO, but without the clunky defines. Just make class A, B and C, which makes it easier for everyone to talk about every single case. I might just take a look myself this evening or tomorrow, although I've been saying that a lot just to find out I was lying.

     
  • Geoff Worboys
    Geoff Worboys
    2013-03-29

    When you said "first", I wasn't sure if you meant post them here … but here they are anyway.

    test.hpp

        class __declspec(dllexport) A {
        public:
            int fa() { return m; }
            int ga();
        private:
            int m{0};
        };
        class __declspec(dllexport) B {
        public:
            int fb();
            int gb();
        private:
            int m{0};
        };
        inline int B::fb() { return m; }
        class C {
        public:
            int fc() { return m; }
            __declspec(dllexport) int gc();
        private:
            int m{0};
        };
    

    test.cpp

        #include "test.hpp"
        int A::ga() { return (fa() + 1); }
        int B::gb() { return (fb() + 1); }
        int C::gc() { return (fc() + 1); }
    

    Compiled with: -std=c++11 -O2 -S

    ga():

        subq    $40, %rsp
        .seh_stackalloc 40
        .seh_endprologue
        call    _ZN1A2faEv
        addl    $1, %eax
        addq    $40, %rsp
        ret
    

    gb():

        subq    $40, %rsp
        .seh_stackalloc 40
        .seh_endprologue
        call    _ZN1B2fbEv
        addl    $1, %eax
        addq    $40, %rsp
        ret
    

    gc():

        .seh_endprologue
        movl    (%rcx), %eax
        addl    $1, %eax
        ret
    

    with -Winline on the command line we get:
    warning: function 'int B::fb()' can never be inlined because it uses attributes conflicting with inlining
    warning: inlining failed in call to 'int B::fb()': function not inlinable  (called from B::gb())

    Note that neither fa() nor fc() give any warning about not inlining.

    PS. I hope the above comes out clearly - I haven't done much inside sourceforge before and there's no preview to check (nor edit to fix, AFAICT).