skip to Main Content

I have this foo.cpp:

int add(int a, int b) {
    return a + b;
}

int add_wrapper(int a, int b) {
    return add(a,b);
}

Compiling like so

g++ -c -S -fPIC -O4 -finline-functions  foo.cpp -o foo.s

Gives the (demangled) assembly:

    .globl  add(int, int)
    .type   add(int, int), @function
add(int, int):
.LFB0:
    .cfi_startproc
    endbr64
    leal    (%rdi,%rsi), %eax
    ret
    .cfi_endproc
.LFE0:
    .size   add(int, int), .-add(int, int)
    .p2align 4
    .globl  add_wrapper(int, int)
    .type   add_wrapper(int, int), @function
add_wrapper(int, int):
.LFB1:
    .cfi_startproc
    endbr64
    jmp add(int, int)@PLT
    .cfi_endproc
.LFE1:
    .size   add_wrapper(int, int), .-add_wrapper(int, int)
    .ident  "GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0"

Note that add() is not inlined.

However, without -fPIC, I get

    .globl  add(int, int)
    .type   add(int, int), @function
add(int, int):
.LFB0:
    .cfi_startproc
    endbr64
    leal    (%rdi,%rsi), %eax
    ret
    .cfi_endproc
.LFE0:
    .size   add(int, int), .-add(int, int)
    .p2align 4
    .globl  add_wrapper(int, int)
    .type   add_wrapper(int, int), @function
add_wrapper(int, int):
.LFB3:
    .cfi_startproc
    endbr64
    leal    (%rdi,%rsi), %eax
    ret
    .cfi_endproc
.LFE3:
    .size   add_wrapper(int, int), .-add_wrapper(int, int)

With add() inlined.

If I add inline to the declaration of add() and compile with -fPIC, I get

    .globl  add_wrapper(int, int)
    .type   add_wrapper(int, int), @function
add_wrapper(int, int):
.LFB1:
    .cfi_startproc
    endbr64
    leal    (%rdi,%rsi), %eax
    ret
    .cfi_endproc
.LFE1:
    .size   add_wrapper(int, int), .-add_wrapper(int, int)
    .ident  "GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0"

With add() omitted entirely.

So it seems that inlining is not disallowed by -fPIC because the compiler will inline if I label the function inline, but I don’t understand why the compiler is unwilling to inline automatically with ‘-fPIC’.

Does anyone know why this is?

Is there a flag that will convince g++ to inline with -fPIC but without labeling functions as inline?

I’m using g++ 11.2, but Compiler Explorer shows that the behavior is consistent across versions: https://godbolt.org/z/Y14edz968.

2

Answers


  1. Using -fPIC implies that you intend your functions to be used in a library. As neither function is labeled static, the compiler cannot assume anything and must make it available to link.

    The function may still be used inline elsewhere in the compiled object code, but again, it cannot be removed from actual existence.

    If you wish to remove a function from external availability, either label it with static

    static int add( ... ) { ... }
    

    or wrap it in an unnamed namespace.

    namespace
    {
      int add(...) { ... }
    }
    

    At this point it can be inlined (either implicitly or explicitly) because you have specifically marked it as unavailable to external code, and consequently it might be optimized out of actual existence.

    Login or Signup to reply.
  2. If you add the always_inline attribute to add, you can see the compiler error when it tries to inline:

    warning: 'always_inline' function might not be inlinable [-Wattributes]
        1 | [[gnu::always_inline]] int add(int a, int b) {
          |                            ^~~
    
    In function 'int add_wrapper(int, int)':
    error: inlining failed in call to 'always_inline' 'int add(int, int)': function body can be overwritten at link time
    note: called from here
        6 |     return add(a,b);
          |            ~~~^~~~~
    

    The add(int, int) symbol might end up being something else (e.g., another library that was in LD_PRELOAD with an int add(int, int) is loaded first, your add_wrapper should call that one).

    You can fix this by making add invisible ([[gnu::visibility("hidden")]]) or not giving it external linkage (with an anonymous namespace or static).

    Or you can make g++ assume this will never happen with the switch -fno-semantic-interposition (which is the default on clang).

    See New option in GCC 5.3: -fno-semantic-interposition for more information about semantic interposition.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search