[英]gcc -O2 optimize to wrong behavior, is it a bug?
我已经编写了一个非常简单的程序来测试gcc -O2选项,它将生成错误的行为。 这是gcc的错误吗?
我的C程序:
#include <stdio.h>
int main(int argc, char **argv)
{
unsigned long i = 0;
while (1) {
if (++i > 0x1fffffffUL) {
printf("hello\n");
i = 0;
}
}
}
当我用-O1编译它时,一切都正确,我在每个循环中加1。 但是,当我使用-O2进行编译时,将被忽略,在每个循环中都会输出“ hello”。 为什么-O2会导致这种错误行为? 是gcc的明显错误吗? 我的gcc版本是cygwin的4.8.3,我尝试了一个新版本,它也有同样的问题。
用-O1编译,汇编代码为:
Disassembly of section .text:
00000000 <_main>:
#include <stdio.h>
int main(int argc, char **argv)
{
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 e4 f0 and $0xfffffff0,%esp
6: 83 ec 10 sub $0x10,%esp
9: e8 00 00 00 00 call e <_main+0xe>
e: b8 00 00 00 20 mov $0x20000000,%eax
unsigned long i = 0;
while (1) {
if (++i > 0x1fffffffUL) {
13: 83 e8 01 sub $0x1,%eax
16: 75 fb jne 13 <_main+0x13>
printf("hello\n");
18: c7 04 24 00 00 00 00 movl $0x0,(%esp)
1f: e8 00 00 00 00 call 24 <_main+0x24>
24: eb e8 jmp e <_main+0xe>
26: 90 nop
27: 90 nop
用-O2编译,汇编代码为:
Disassembly of section .text.startup:
00000000 <_main>:
#include <stdio.h>
int main(int argc, char **argv)
{
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 e4 f0 and $0xfffffff0,%esp
6: 83 ec 10 sub $0x10,%esp
9: e8 00 00 00 00 call e <_main+0xe>
e: 66 90 xchg %ax,%ax
10: c7 04 24 00 00 00 00 movl $0x0,(%esp)
17: e8 00 00 00 00 call 1c <_main+0x1c>
1c: eb f2 jmp 10 <_main+0x10>
1e: 90 nop
1f: 90 nop
此优化是正确的。
在-O1
,编译器实质上将您的函数重写为:
for (;;) {
for (int i = 0; i < 0x20000000; i++) {
}
printf("hello\n");
}
在-O2
,正在优化空循环。
对我来说听起来不像是个虫子。 当程序的可观察行为是在无限循环中输出“ hello \\ n”时,为什么编译器为什么要生成会浪费时间的代码。 您不需要增加无用的变量。
它会注意到您在循环中没有执行任何其他操作,因此可以优化它们。 您仍然会得到相同的输出。 这就是优化的全部要点:只要生成相同的输出,它就会消除内部无用的内容。
编辑:作为中提到的这篇文章 ,如果你想保持一个空的循环被优化掉,可以包括asm("");
是一个空的汇编命令,位于while循环的主体内部。 这使GCC无法对其进行优化。
您会期望什么行为? 基本上, i++ > 0x1FFFFFFFUL
条件将是无数次的真实值,并且每次满足时它将打印“ hello”。
外部可观察到的行为是无限次打印“ hello”。 编译器仅产生这种优化就太聪明了。
如果您希望实际循环一个空循环,则可能不应该告诉编译器进行困难的优化,或者最好确保循环的每次运行都具有外部可观察到的行为。 根据定义,这可以通过volatile
声明i
来完成:
#include <stdio.h>
int main(int argc, char **argv)
{
static volatile unsigned long i = 0;
while (1) {
if (++i > 0x1fffffffUL) {
printf("hello\n");
i = 0;
}
}
}
volatile
声明基本上意味着编译器必须生成实际执行对该变量的每次写入的代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.