下面是一段代码:
#include <stdio.h>
typedef struct {
bool some_var;
} model_t;
const model_t model = {
true
};
void bla(const model_t *m) {
if (m->some_var) {
printf("Some var is true!\n");
}
else {
printf("Some var is false!\n");
}
}
int main() {
bla(&model);
}
我想编译器已经有了删除bla()
函数中的else
子句所需的所有信息。调用该函数的唯一代码路径来自main,并且它接受const model_t
,所以它应该能够判断出该代码路径没有被使用。然而:
在GCC 12.2中,我们看到第二部分是链接的。
如果I inline
,则函数会消失:
我在这里遗漏了什么?有没有什么方法可以让编译器做一些更聪明的工作?这在C和C++中都会发生,-O3
和-Os
。
3条答案
按热度按时间k97glaaz1#
编译器 * 确实 * 消除了
main
内联函数中的else路径。您混淆了全局函数,该函数无论如何都不会被调用,最终将被链接器丢弃。如果使用-fwhole-program标志让编译器知道没有其他文件要链接,则会丢弃未使用的段:
See online(https://godbolt.org/z/dovas8ePe)
此外,您可以使用
static
或inline
关键字来实现类似的功能。llycmphe2#
编译器无法优化else路径,因为目标文件可能会链接到任何其他代码。如果函数是静态的,或者您使用整个程序优化,则情况会有所不同。
6uxekuva3#
调用该函数的唯一代码路径来自main
GCC不能知道这一点,除非你用
-fwhole-program
或者-flto
(链接时优化)告诉它。否则它必须假设另一个编译单元中的某个静态构造函数可以调用它。(可能包括在一个共享库中,但你链接的另一个.cpp
可以做到这一点。)例如:Godbolt上的示例,这些行与main位于同一个编译单元中,显示它同时输出
Some var is false!
,然后输出Some var is true!
,而没有更改main
的代码。ISO C没有简单的方法来执行init代码,但是GNU C(特别是GCC)有方法让代码在启动时运行,而不是由
main
调用。对于
-fwhole-program
,适当的优化应该是根本不为它发出定义,因为它已经内联到main
的调用点中。就像inline
(在C++中,一个承诺,另一个编译单元中的任何其他调用者都可以看到它自己的函数定义)或static
(该编译单元私有)。在
main
内部,它已经优化掉了常量传播后的分支。没有任何东西调用函数的独立定义。函数的独立定义并不知道
m
的唯一可能值是&model
,如果你把它放在函数中,那么它就可以像你期望的那样优化。只有
-fPIC
会强制编译器考虑symbol-interposition的可能性,因此const model_t model
的定义不是在(动态)链接。但是您正在编译可执行文件的代码,而不是库。(您可以停用全局变量的符号插入,方法是将其设为“隐藏”可见性,__attribute__((visibility("hidden")))
,或使用-fvisibility=hidden
将其设为预设值)。