当使用C++的-O3编译选项编译代码时,为什么我的编译器会将这个for循环优化成无限循环?

4
我正在尝试理解优化过程是如何导致下列代码在使用 -O3 优化标志编译时产生无限循环的。顺带一提,我明白问题的真正原因在于该非 void 函数缺少返回,而我在嵌入式系统上实现这段代码的过程中遇到了这种有趣的行为,此时我还没有添加返回语句,因为那时我没有使用返回值。
我的问题更多地涉及优化过程,以及它是如何在其他情况下提高性能的/‘优化’逻辑的样子。额外提供信息,当我在 Ubuntu 使用 C++ 编译器(c++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0)和 Xilinx Vitis 2020.2 提供的 aarch64-linux-gnu-g++ 编译器时(分别在它们各自的平台上运行),都能看到这种行为。
最小可重现示例(目前我已创建):
#include <iostream>

int broken_for_loop(){
    for (int i = 0; i < 10000; i+= 1000){
        std::cout << i << std::endl;
    }
}

int main(int argc, char const *argv[]){
    broken_for_loop();
}

当使用c++ ./broken_loop_test.cpp -o test_local -O3或ARM相应的编译命令进行编译时,循环的输出是无限的,并且我已经运行了直到32位整数溢出。没有优化时,它的工作方式符合我的期望。如果在for循环后简单地return 0,则在进行优化时也可以正常工作。

我天真的怀疑是因为循环外没有返回,所以编译器希望我从循环内返回或中断,因此删除了测试循环条件的检查/分支,但我想知道可以查看什么内容以获取更多关于这个特定主题(以及优化的一般信息,自上次参加编译器设计课程以来,已经过了一段时间)的信息,而我对汇编语言不够自信,无法确定问题所在。

任何帮助都将不胜感激,谢谢!

由于需要此部分,我会注意到我尝试声明volatile i,并使用不同类型的整数,以及将常量值转换和在循环中执行更多/更少的操作。所有情况都没有返回语句会导致相同的行为。


3
如果你的函数中漏掉了返回某些整数值,会导致代码出现未定义行为(UB)... - Jarod42
6
你打破语言规则,语言规则反过来就会束缚你 ;) - NathanOliver
2
没有有效的return语句,代码就是不完整的,所以(大概)所有的赌注都是无效的。 - Adrian Mole
2
没有return语句,g++和clang都会删除循环边界检查,并且根本不会从函数中返回。一个不无道理的推断可能是:“这个函数永远不会返回,所以循环永远不会终止,因此没有必要检查循环条件”。 - molbdnilo
下次编译时,请允许您的gcc编译器告诉您一些警告。即使是开源代码编辑器...例如Qt Creator也会警告实际问题。 - Öö Tiib
显示剩余4条评论
1个回答

6
未定义行为导致时间旅行
你的代码是一个很好的例子。在循环后面有未定义行为(必须返回值的函数没有返回值)。由于编译器可以假设 UB 从不会发生,它会认为循环永远不会结束,并相应地编译它。

你能否指引我查找有关代码优化方面的资料?我知道,正如之前所说,UB/缺少返回是根本问题,但我的问题更多的是想了解在我的情况下哪个优化过程导致UB成为问题,并且当遵循语言规则时它会出现在哪里。我早已解决了这个问题,现在只是想满足好奇心。 - Douglas B
1
UB总是有问题,因为编译器总是被允许做任何事情。你是否想知道为什么当不进行优化时,“任何事情”恰好与“按我预期工作”相符合?因为你那一次运气好而已。警察没有注意到,编译器也没有注意到循环结束时所有执行路径都会导致UB,因为检测这种情况的逻辑只有在开启优化时才会启用。 - n. m.
1
@DouglasB: -O0 编译每个 C 语句时都会单独处理,没有常量传播。使用调试器进行单步调试,退出循环是一个可见的效果,而 gcc -O0 提供了一致的调试功能。为什么 clang 在 -O0 下生成低效的汇编代码(对于这个简单的浮点数求和)? 然而,在调试器中,没有真正的方法可以在没有 UB 的情况下从这个函数中退出,因为它没有任何返回语句,你也无法通过 jset i = -1234 来设置值。 - Peter Cordes
1
@DouglasB:gcc -O0 可能只是对处理这种编译时可见 UB 实例的默认方式有所不同,因为它会发出一个 ret 指令,而不是让执行掉入下一个函数(即不发出任何 ret 指令,例如 int foo() {},它编译成字面上零条指令,甚至没有 ret,带有优化)。https://godbolt.org/z/Kh64fqo3v - Peter Cordes
程序员有办法避免执行无必要指令。 - supercat
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接