gcc 对于采用const结构的函数,编译器不优化函数体吗?

b1payxdu  于 2022-11-13  发布在  其他
关注(0)|答案(3)|浏览(201)

下面是一段代码:

#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

k97glaaz

k97glaaz1#

编译器 * 确实 * 消除了main内联函数中的else路径。您混淆了全局函数,该函数无论如何都不会被调用,最终将被链接器丢弃。
如果使用-fwhole-program标志让编译器知道没有其他文件要链接,则会丢弃未使用的段:
See online(https://godbolt.org/z/dovas8ePe)

此外,您可以使用staticinline关键字来实现类似的功能。

llycmphe

llycmphe2#

编译器无法优化else路径,因为目标文件可能会链接到任何其他代码。如果函数是静态的,或者您使用整个程序优化,则情况会有所不同。

6uxekuva

6uxekuva3#

调用该函数的唯一代码路径来自main
GCC不能知道这一点,除非你用-fwhole-program或者-flto(链接时优化)告诉它。否则它必须假设另一个编译单元中的某个静态构造函数可以调用它。(可能包括在一个共享库中,但你链接的另一个.cpp可以做到这一点。)例如:

// another .cpp
typedef struct {  bool some_var; } model_t;
void bla(const model_t *m);              // declare the things from the other .cpp

int foo() {
    model_t model = {false};
    bla(&model);
    return 1;
}
int some_global = foo();  // C++ only: non-constant static initializer.

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将其设为预设值)。

相关问题