为什么for循环有一个超出预期的额外指令?

Yal*_*ang 6 optimization x86 assembly gcc gcc4.9

我写了很多矢量化循环,所以有一个常见的习语

volatile int dummy[1<<10];
for (int64_t i = 0; i + 16 <= argc; i+= 16)   // process all elements with whole vector
{
  int x = dummy[i];
}
// handle remainder (hopefully with SIMD too)
Run Code Online (Sandbox Code Playgroud)

但是生成的机器代码比我想要的多1个指令(使用gcc 4.9)

.L3:
        leaq    -16(%rax), %rdx
        addq    $16, %rax
        cmpq    %rcx, %rax
        movl    -120(%rsp,%rdx,4), %edx
        jbe     .L3
Run Code Online (Sandbox Code Playgroud)

如果我将代码更改为for (int64_t i = 0; i <= argc - 16; i+= 16),则"额外"指令消失了:

.L2:
        movl    -120(%rsp,%rax,4), %ecx
        addq    $16, %rax
        cmpq    %rdx, %rax
        jbe     .L2
Run Code Online (Sandbox Code Playgroud)

但为什么差异呢?我想也许这是由于循环不变量,但是太模糊了.然后我注意到在5指令的情况下,增量在加载之前完成,由于x86的破坏性2操作数指令,这将需要额外的mov.所以另一种解释可能是它为一条额外的指令交易指令并行性.

虽然看起来几乎没有任何性能差异,但有人可以解释这个谜(最好是谁知道编译器转换)?

理想情况下,我想保持i + 16 <=大小形式,因为它具有更直观的含义(向量的最后一个元素不会越界)

sup*_*cat 8

如果argc低于-2147483632,并且i低于2147483632,则表达式i+16 <= argc将需要产生算术正确的结果,而表达式则i<argc-16不会.在该拐角情况下给出算术正确结果的需要阻止编译器优化前一表达式以匹配后者.