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)
但是,使用这样的局部变量会破坏拥有全局变量的全部意义。如果你从来不写它,你还不如定义一个常量。
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)
但是,使用这样的局部变量会破坏拥有全局变量的全部意义。如果你从来不写它,你还不如定义一个常量。