GCC/Clang 没有优化静态全局变量

GCC/Clang not optimising static global variable

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

编译代码时

static int test = 0;

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

int main() {
  return abc();
}

带有标志 -Os(以生成更短且更易读的程序集)和 -fwhole-program-flto 使用 GCC 版本 11.2 我希望这会被优化为 return 1,或以下程序集:

main:
        mov     eax, 1
        ret

这实际上是 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

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

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

相关: 但是那里给出的答案提到了外部函数和线程,这里都不适用

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

我能想到的两个原因是:

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

  • 这种优化的用例很少见。全局变量最常用于表示一些共享的全局状态。优化它是没有意义的。与大多数程序的好处相比,在 compiler/linker 中实现此类功能的工作量很大。

加法
显然,如果您 read-only 访问它,GCC 会优化掉该变量。如果编译如下:

static int test = 0;

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

int main() {
  return abc();
}

如果您将变量一次读入局部变量而永远不会写入它,它会被优化为:

main:
    mov     eax, 1
    ret

(见here for a demo
但是,使用这样的局部变量会破坏拥有全局变量的全部意义。如果你从来不写它,你还不如定义一个常量。