GCC/Clang 未优化静态全局变量

Hea*_*ell 2 c c++ gcc clang compiler-optimization

GCC 似乎无法跟踪和优化在 C/C++ 中读/写全局变量的程序,即使它们是static,这应该允许它保证其他编译单元不会更改该变量。

编译代码时

static int test = 0;

int abc() {
  test++;
  if (test > 100) \
    return 123;
  --test;
  return 1;
}

int main() {
  return abc();
}
Run Code Online (Sandbox Code Playgroud)

使用标志-Os(以生成更短且更具可读性的程序集)和-fwhole-program/或-flto使用 GCC 版本 11.2 我希望将其优化为return 1或以下程序集:

main:
        mov     eax, 1
        ret
Run Code Online (Sandbox Code Playgroud)

这实际上是test局部变量所产生的结果。但是,会生成以下内容:

main:
        mov     eax, DWORD PTR test[rip]
        mov     r8d, 1
        inc     eax
        cmp     eax, 100
        jle     .L1
        mov     DWORD PTR test[rip], eax
        mov     r8d, 123
.L1:
        mov     eax, r8d
        ret
Run Code Online (Sandbox Code Playgroud)

示例: https: //godbolt.org/z/xzrPjanjd

GGC 和 Clang 以及我尝试过的所有其他编译器都会发生这种情况。我希望现代编译器能够跟踪程序的流程并删除检查。是否有一些我没有考虑到的东西可能会允许程序外部的东西影响变量,或者这只是尚未在任何编译器中实现?

相关:为什么 gcc 没有优化全局变量?但那里给出的答案提到了外部函数和线程,这两者都不适用于此

Jak*_*ark 5

我认为您对大多数编译器的要求有点过高。虽然编译器可能被允许根据标准中的as-if 规则优化静态变量,但它显然在许多编译器中没有实现,就像您为 GCC 和 Clang 所说的那样。

我能想到的两个原因是:

  • 在您的示例中,显然链接时间优化决定内联函数abc,但没有优化掉test变量。为此,test需要对变量的读/写语义进行分析。以通用方式做到这一点非常复杂。在您提供的简单情况下可能是可能的,但任何更复杂的情况都会非常困难。

  • 此类优化的用例很少见。全局变量最常用于表示某些共享的全局状态。我没有任何意义去优化它。与大多数程序的好处相比,在编译器/链接器中实现此类功能的工作量将很大。

加法
显然,如果您只读访问该变量,GCC 会优化该变量。如果您编译以下内容:

static int test = 0;

int abc() {
  int test_ = test;
  test_++;
  if (test_ > 100) \
    return 123;
  --test_;
  return 1;
}

int main() {
  return abc();
}
Run Code Online (Sandbox Code Playgroud)

如果您将变量读入局部变量一次并且从不写入它,则它会被优化为:

main:
    mov     eax, 1
    ret
Run Code Online (Sandbox Code Playgroud)

(请参阅此处的演示
但是,使用这样的局部变量会破坏全局变量的全部意义。如果你从不写它,你不妨定义一个常量。

  • 还有其他涉及“静态”的优化是在编译时完成的,而不是在链接时完成的。作为最简单的示例,从未使用过的“静态”函数或变量将在编译时被删除,甚至永远不会进入链接器。因此,如果要完成此优化,我希望它由编译器而不是链接器完成。 (2认同)