【发布时间】:2020-05-12 12:04:20
【问题描述】:
C11 标准似乎暗示不应优化具有常量控制表达式的迭代语句。我听取了this answer 的建议,其中特别引用了标准草案中的第 6.8.5 节:
控制表达式不是常量表达式的迭代语句......可能会被实现假定终止。
在那个答案中,它提到像 while(1) ; 这样的循环不应进行优化。
那么...为什么 Clang/LLVM 会优化下面的循环(使用 cc -O2 -std=c11 test.c -o test 编译)?
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
die();
printf("unreachable\n");
}
在我的机器上,这会打印出begin,然后在一条非法指令上崩溃(ud2 陷阱放置在die() 之后)。 On godbolt,我们可以看到调用puts之后什么都没有产生。
让 Clang 在-O2 下输出无限循环是一项非常困难的任务——而我可以反复测试volatile 变量,这涉及到我不想要的内存读取。如果我这样做:
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
volatile int x = 1;
if(x)
die();
printf("unreachable\n");
}
...Clang 打印 begin 后跟 unreachable,就好像无限循环不存在一样。
如何让 Clang 在优化开启的情况下输出正确的、无内存访问的无限循环?
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
-
没有不涉及副作用的便携式解决方案。如果您不想要内存访问,您最好的希望是注册 volatile unsigned char;但是寄存器在 C++17 中消失了。
-
也许这不在问题的范围内,但我很好奇你为什么要这样做。当然还有其他方法可以完成你的真正任务。或者这只是学术性质的?
-
@Cruncher:任何特定尝试运行程序的效果可能是有用的,基本上是无用的,或者比无用更糟糕。导致程序陷入无限循环的执行可能是无用的,但仍然比编译器可能替代的其他行为更可取。
-
@Cruncher:因为代码可能在没有
exit()概念的独立上下文中运行,并且因为代码可能发现了无法保证继续执行的效果的情况不会比没用更糟。跳到自我循环是处理这种情况的一种非常糟糕的方法,但它可能仍然是处理糟糕情况的最佳方法。
标签: c clang language-lawyer compiler-optimization