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
Using
-fPIC
implies that you intend your functions to be used in a library. As neither function is labeledstatic
, 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
or wrap it in an unnamed
namespace
.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.
If you add the
always_inline
attribute toadd
, you can see the compiler error when it tries to inline:The
add(int, int)
symbol might end up being something else (e.g., another library that was inLD_PRELOAD
with anint add(int, int)
is loaded first, youradd_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 orstatic
).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.