Search code examples
gccmoduledinformation-hiding

D / DLang : Inhibiting code generation of module-private inlined functions


I have a D module which I hope contains public and private parts. I have tried using the keywords private and static before function definitions. I have a function that I wish to make externally-callable / public and ideally I would like it to be inlined at the call-site. This function calls other module-internal functions that are intended to be private, i.e. not externally callable. Calls to these are successfully inlined within the module and a lot of the cruft is disposed of by CTFE plus known-constant propagation. However the GDC compiler also generates copies of these internal routines, even though they have been inlined where needed and they are not supposed to be externally callable. I'm compiling with -O3 -frelease. What should I be doing - should I expect this even if I use static and/or private?

I have also taken a brief look at this thread concerning GCC hoping for insight.

As I mentioned earlier, I've tried both using private and static on these internal functions, but I can't seem to suppress the code generation. I could understand this if a debugger needed to have copies of these routines to set breakpoints in. I need to stress that this could perhaps be sorted out somehow at link-time, for all I know. I haven't tried linking the program, I'm just looking at the generated code in the Matt Godbolt D Compiler Explorer using GDC. Everything can be made into templates with a zero-length list of template parameters (e.g. auto my_fn()( in arg_t x ) ), tried that, it doesn't help but does no harm.

A couple of other things to try: I could try and make a static class with private parts, as a way of implementing a package, Ada-style. (Needs to be single-instance strictly.) I've never done any C++, only massive amounts of asm and C professionally. So that would be a learning curve.

The only other thing I can think of is to use nested function definitions, Pascal/Ada-style, move the internal routines to be inside the body of their callers. But that has a whole lot of disadvantages.

Rough example

module junk;

auto my_public_fn() {  return my_private_fn();  }

private
static // 'static' and/or 'private', tried both
auto my_private_fn() { xxx ; return whatever; }

Solution

  • I just had a short discussion with Iain about this and implementing this is not as simple as it seems.

    First of all static has many meanings in D, but the C meaning of translation unit local function is not one of them ;-)

    So marking these functions as private seems intuitive. After all, if you can't access a function from outside of the translation unit and you never leak an address to the function why not remove it? It could be either completely unused or inlined into all callers in this case.

    Now here's the catch: We can't know for sure if a function is unused:

    private void fooPrivate() {}
    
    /*template*/ void fooPublic()()
    {
        fooPrivate();
    }
    

    When compiling the file GDC knows nothing about the fooPublic template (as templates can only be fully analyzed when instantiated), so fooPrivate appears to be unused. When later using fooPublic in a different file GDC will rely on fooPrivate being already emitted in the original source - after all it's not a template so it's not being emitted into the new module.

    There might be workarounds but this whole problem seems nontrivial. We could also introduce a custom gcc.attribute attribute for this. It would cause the same problems with templates, but as it's a specific annotation for one usecase (unlike private) we could rely on the user to do the right thing.