为什么汇编代码在加总和之前将值从 %edx 复制到 %rcx?
Why does the assembly code copy the value from %edx to %rcx before adding to the sum?
使用 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 [=10=], %edx
movl [=10=], %eax
jmp .L2
.L3:
movslq %edx, %rcx
addl (%rdi,%rcx,4), %eax
addl , %edx
.L2:
cmpl %esi, %edx
jl .L3
rep ret
正如4386427之前的回答所指出的,您不能在一个有效地址中混用32位和64位寄存器。 CPU 不支持。所以 addl (%rdi,%edx,4), %eax
将不可编码。
要使用i
作为有效地址的索引部分,我们需要在64位寄存器中。由于 i
是带符号的 int
类型,编译器用 movsx
对其进行符号扩展。它使用一个单独的寄存器 %rcx
以便 %edx
可以继续保存变量 i
的值,使调试器更容易检查这个值(例如 print i
在 gdb 中)。
事实证明,我们可以证明 i
在这个函数中总是非负的。最初的movl [=20=], %edx
also zeros out the high half of %rdx
,从那时起它将保持为零,所以实际上%rdx
确实总是包含变量i
的正确64位值。因此我们可以使用 addl (%rdi, %rdx, 4), %eax
代替,并省略 movsx
。不过,编译器可能没有在这种优化级别上进行推论。
(也可以在具有地址大小覆盖前缀的有效地址中使用所有 32 位寄存器,因此 addl (%edi, %edx, 4), %eax
是一条可编码指令,但它不会工作,因为它会截断%rdi
中指针 arr
的高 32 位。因此,地址大小覆盖在 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 [=10=], %edx
movl [=10=], %eax
jmp .L2
.L3:
movslq %edx, %rcx
addl (%rdi,%rcx,4), %eax
addl , %edx
.L2:
cmpl %esi, %edx
jl .L3
rep ret
正如4386427之前的回答所指出的,您不能在一个有效地址中混用32位和64位寄存器。 CPU 不支持。所以 addl (%rdi,%edx,4), %eax
将不可编码。
要使用i
作为有效地址的索引部分,我们需要在64位寄存器中。由于 i
是带符号的 int
类型,编译器用 movsx
对其进行符号扩展。它使用一个单独的寄存器 %rcx
以便 %edx
可以继续保存变量 i
的值,使调试器更容易检查这个值(例如 print i
在 gdb 中)。
事实证明,我们可以证明 i
在这个函数中总是非负的。最初的movl [=20=], %edx
also zeros out the high half of %rdx
,从那时起它将保持为零,所以实际上%rdx
确实总是包含变量i
的正确64位值。因此我们可以使用 addl (%rdi, %rdx, 4), %eax
代替,并省略 movsx
。不过,编译器可能没有在这种优化级别上进行推论。
(也可以在具有地址大小覆盖前缀的有效地址中使用所有 32 位寄存器,因此 addl (%edi, %edx, 4), %eax
是一条可编码指令,但它不会工作,因为它会截断%rdi
中指针 arr
的高 32 位。因此,地址大小覆盖在 64 位代码中几乎没有用。)