为什么汇编代码在添加到总和之前将值从 %edx 复制到 %rcx?

Jes*_*n56 3 c arrays assembly gcc x86-64

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

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

addl (%rdi,%edx,4), %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,%rcx,4), %eax
        addl    $1, %edx
.L2:
        cmpl    %esi, %edx
        jl      .L3
        rep ret
Run Code Online (Sandbox Code Playgroud)

Nat*_*dge 5

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

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

事实证明,我们可以证明i在这个函数中总是非负的。初始值movl $0, %edx 也将 的高半部分%rdx清零,从那时起它将保持为零,因此实际上%rdx始终包含变量 的正确 64 位值i。因此,我们可以addl (%rdi, %rdx, 4), %eax改用,并省略movsx. 不过,编译器可能不会在这种优化级别上进行推断。

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