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

Jan*_*oom 17 c c++ gcc compiler-optimization

我有以下代码:

#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);
}
Run Code Online (Sandbox Code Playgroud)

我想编译器拥有消除函数else中的子句所需的所有信息bla()。调用该函数的唯一代码路径来自 main,并且它接受const model_t,因此它应该能够确定该代码路径未被使用。然而:

不带内联

在 GCC 12.2 中,我们看到第二部分被链接进来。

如果我的inline功能这个消失了:

带内联

我在这里缺少什么?有什么方法可以让编译器做一些更智能的工作吗?在 C 和 C++ 中,使用-O3和都会发生这种情况-Os

Ayx*_*xan 33

编译器确实消除了 中内联函数中的 else 路径main。您混淆了无论如何都不会调用的全局函数,并且最终将被链接器丢弃。

如果使用-fwhole-program标志让编译器知道不会链接其他文件,则未使用的段将被丢弃:

[在线查看]

在此输入图像描述

此外,您还可以使用staticinline关键字来实现类似的效果。


MrT*_*Tux 27

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

  • 如果函数被声明为“static”,它也不会被发出(与“inline”相同)。无论如何,所有调用站点都是内联的。问题中发出的函数从未使用过。 (8认同)

Pet*_*des 8

调用该函数的唯一代码路径来自 main

GCC 无法知道这一点,除非你用-fwhole-programor Maybe -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.
Run Code Online (Sandbox Code Playgroud)

Godbolt 上的示例,其中这些行与 main 位于同一编译单元中,表明它输出Some var is false!和 then Some var is true!,而无需更改 的代码main

ISO C 没有简单的方法来执行 init 代码,但 GNU C(特别是 GCC)有方法让代码在启动时运行,而不是由main. 这甚至适用于共享库。


对于-fwhole-program,适当的优化就是根本不发出它的定义,因为它已经内联到 中的调用站点中main。就像inline(在 C++ 中,承诺另一个编译单元中的任何其他调用者都可以看到它自己的函数定义) 或static(此编译单元私有)。

在内部main,它在不断传播后优化掉了分支。如果您运行该程序,则不会实际执行任何分支;没有任何东西调用函数的独立定义。


函数的独立定义不知道 的唯一可能值m&model。如果您将其放入函数中,那么它可以像您期望的那样进行优化。

只会-fPIC迫使编译器考虑符号插入的可能性,因此 的定义const model_t model不是(动态)链接后生效的定义。但是您正在为可执行文件而不是库编译代码。(您可以通过赋予全局变量“隐藏”可见性来禁用符号插入,__attribute__((visibility("hidden")))或使用-fvisibility=hidden将其设置为默认值)。