背景
使用 gcc 7.2 我发现当循环迭代 999 次时编译器输出会发生变化。
特别是这个程序(link to compiler explorer using gcc 7.2):
int f()
{
int i=0x7fffffff-998;
while (i+1>i)
i++;
return i;
}
编译(使用 -O3 -fwrapv)为:
f():
mov eax, 2147483647
ret
但是,如果我将 998 更改为 999,它会编译为:
f():
xor eax, eax
movdqa xmm0, XMMWORD PTR .LC0[rip]
movdqa xmm2, XMMWORD PTR .LC1[rip]
jmp .L2
.L3:
movdqa xmm0, xmm1
.L2:
movdqa xmm1, xmm0
add eax, 1
cmp eax, 250
paddd xmm1, xmm2
jne .L3
pshufd xmm0, xmm0, 255
movd eax, xmm0
ret
.LC0:
.long 2147482648
.long 2147482649
.long 2147482650
.long 2147482651
.LC1:
.long 4
.long 4
.long 4
.long 4
问题
为什么输出会改变,是否有一个开关来控制行为改变的阈值?
注意事项
由于有符号溢出是未定义的行为,默认情况下编译器会将此程序变成无限循环。这就是编译期间需要 -fwrapv 选项的原因。
最佳答案
这基本上是 GCC 源代码中任意常量的结果。
GCC 有一个内部参数,用于控制在优化过程中循环暂时展开的次数:
/* The maximum number of iterations of a loop the brute force algorithm
for analysis of # of iterations of the loop tries to evaluate. */
DEFPARAM(PARAM_MAX_ITERATIONS_TO_TRACK,
"max-iterations-to-track",
"Bound on the number of iterations the brute force #"
" of iterations analysis algorithm evaluates.",
1000, 0, 0)
如果 GCC 没有特殊的逻辑来执行某种代数转换以获得迭代计数,这将用于分析循环。
如果您将此参数更改为不同的值,则从结果到另一个的切换将以不同的魔法值发生。有了你原来的 998 值(value),我得到这个:
$ gcc -O3 -fwrapv -S -o- --param max-iterations-to-track=997 t.c | grep jl
jl .L3
$ gcc -O3 -fwrapv -S -o- --param max-iterations-to-track=998 t.c | grep jl
jl .L3
$ gcc -O3 -fwrapv -S -o- --param max-iterations-to-track=999 t.c | grep jl
$ gcc -O3 -fwrapv -S -o- --param max-iterations-to-track=1000 t.c | grep jl
这些参数是一个内部实现细节,可以随时改变含义,或者完全消失。
(我使用的编译器版本,基于GCC 6.3,没有使用那些未优化情况下的 vector 指令,而是一个有条件的jl
跳转的序列,截断点略微不同,大概是由于其他优化。)
关于c - gcc 中关于迭代 999 次的循环有什么特别之处?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54952960/