为什么-fPIC会阻碍内联

  • 本文关键字:-fPIC c++ g++ inline
  • 更新时间 :
  • 英文 :


我有这个foo.cpp:

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

像这样编译

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

给出(未成角度的(程序集:

.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"

请注意,add()不是内联的。

然而,如果没有-fPIC,我会得到

.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)

带有内联的add()

如果我将inline添加到add()的声明中,并使用-fPIC进行编译,我将获得

.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"

完全省略add()

因此,-fPIC似乎并不禁止内联,因为如果我将函数标记为inline,编译器就会内联,但我不明白为什么编译器不愿意使用"-fPIC"自动内联。

有人知道为什么会这样吗?

是否有一个标志可以说服g++-fPIC内联,但不将函数标记为inline

我使用的是g++11.2,但编译器资源管理器显示,不同版本的行为是一致的:https://godbolt.org/z/Y14edz968.

使用-fPIC意味着您打算在库中使用函数。由于这两个函数都没有标记为static,编译器不能假设任何内容,必须使其可用于链接。

该函数仍然可以在编译的对象代码的其他地方内联使用,但同样,它不能从实际存在中删除。

如果您希望从外部可用性中删除某个功能,请将其标记为static

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

或者将其包裹在未命名的CCD_ 17中。

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

在这一点上,它可以被内联(隐式或显式(,因为您已经明确地将它标记为对外部代码不可用,因此它可能会在实际存在的情况下被优化。

如果将always_inline属性添加到add,当它尝试内联时,您可以看到编译器错误:

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);
|            ~~~^~~~~

add(int, int)符号可能会变成其他符号(例如,首先加载LD_PRELOAD中具有int add(int, int)的另一个库,您的add_wrapper应该调用该库(。

可以通过使add不可见([[gnu::visibility("hidden")]](或不给它外部链接(使用匿名命名空间或static(来解决此问题。

或者,您可以让g++假设开关-fno-semantic-interposition(这是clang的默认值(永远不会发生这种情况。

有关语义插入的更多信息,请参阅GCC 5.3中的新选项:-fno语义插入。

最新更新