为什么汇编代码要先把%edx 的值复制到%rcx 再加和?

使用 x86-64 gcc -Og -std=gnu99 -xc 编译。

.L3的第二行(addl (%rdi,%rcx,4),%eax)中,为什么不直接使用寄存器%edx来加和呢?

addl (%rdi,%edx,%eax

int sum_arr(int arr[],int nelems) {
  int sum = 0;
  for (int i = 0; i < nelems; i++) {
    sum += arr[i];
  }
  return sum;
}

sum_arr:
        movl    $0,%edx
        movl    $0,%eax
        jmp     .L2
.L3:
        movslq  %edx,%rcx
        addl    (%rdi,%eax
        addl    $1,%edx
.L2:
        cmpl    %esi,%edx
        jl      .L3
        rep ret
justcools 回答:为什么汇编代码要先把%edx 的值复制到%rcx 再加和?

正如 4386427 之前的回答所指出的,您不能在有效地址中混合使用 32 位和 64 位寄存器。 CPU不支持。所以 addl (%rdi,%edx,4),%eax 将无法编码。

要使用 i 作为有效地址的索引部分,我们需要在 64 位寄存器中使用它。由于 i 是带符号的 int 类型,编译器用 movsx 对其进行符号扩展。并且它使用一个单独的寄存器 %rcx 以便 %edx 可以继续保存变量 i 的值,使调试器更容易检查这个值(例如 print i在 gdb 中)。

事实证明,我们可以证明 i 在这个函数中总是非负的。初始 movl $0,%edx also zeros out the high half of %rdx,从那时起它将保持为零,因此实际上 %rdx 确实始终包含变量 i 的正确 64 位值。因此,我们可以使用 addl (%rdi,%rdx,%eax 代替,并省略 movsx。不过,编译器可能没有在这种优化级别上进行推断。

(也可以使用地址大小覆盖前缀的有效地址中的所有 32 位寄存器,因此 addl (%edi,%eax 是一个可编码指令,但它不会工作,因为它会截断高 32 arr 中指针 %rdi 的位。因此,地址大小覆盖在 64 位代码中几乎没有用处。)

本文链接:https://www.f2er.com/2464.html

大家都在问