X86汇编中数组访问的解释
Explanation of array accessing in X86 assembly
我有以下 C 函数:
int sum_arr(int b[], int size){
int counter = size-1;
int res = 0;
while(counter >= 0){
res = res + b[counter];
counter = counter - 1;
}
return res;
}
我从中生成了以下汇编代码:
gcc -Og -S file.c
产生了以下汇编代码(我只包含了感兴趣的部分):
sum_arr:
.LFB41:
.cfi_startproc
subl , %esi
movl [=12=], %eax
jmp .L2
.L3:
movslq %esi, %rdx
addl (%rdi,%rdx,4), %eax
subl , %esi
.L2:
testl %esi, %esi
jns .L3
rep ret
.cfi_endproc
我在使用 .L3 时遇到了一些问题。我的理解是它通过将 int 计数器从 32 位寄存器 %esi
移动到 64 位寄存器 %rdx
开始。然后我不明白下面一行:
addl (%rdi,%rdx,4), %eax
特别是 (%rdi,%rdx,4)
部分,它被添加到 %eax
寄存器中的值。
在最后一行,它将计数器减 1。
有人可以帮我解决这个问题吗?
这种形式更容易理解:
sum_arr:
sub esi, 1
js .L4
movsx rsi, esi
mov eax, 0
.L3:
add eax, DWORD PTR [rdi+rsi*4]
sub rsi, 1
test esi, esi
jns .L3
ret
.L4:
mov eax, 0
ret
两点说明:
你的整数很可能溢出所以你应该使用 long long 作为临时 & return 值。也可以缩短
long long sum_arr(const int *b, size_t size){
long long res = 0;
while(size--){
res = res + *b++;
}
return res;
}
.L3:
movslq %esi, %rdx /* sign extend counter<%esi> to 64bit %rdx */
addl (%rdi,%rdx,4), %eax /* res<%eax> += b<%rdi>[counter<%rdx>]; */
subl , %esi /* counter<%esi> -= 1 */
.L2:
testl %esi, %esi /* do counter<%esi> & counter<%esi> */
jns .L3 /* if result is no 0, jump to L3 */
基本上addl (%rdi,%rdx,4), %eax
就是你访问数组(%rdi
)的地方,索引为counter(%rdx
),然后将元素的值添加到res(%eax
), 4
只是内存访问的计数器 (%rdx
) 的乘积,因为 int
数组中的每个地址占用系统内存中的 4 个字节。
这行基本上是说 res += MEMORY[addrssOf(b) + counter*4]
顺便说一句,我相信你想检查 int counter = size-1;
行之前的 size > 0
,而且正如 P__J__
在他的回答中提到的那样,你的 res
可能会溢出您求和的数组中每个元素的类型相同。
我有以下 C 函数:
int sum_arr(int b[], int size){
int counter = size-1;
int res = 0;
while(counter >= 0){
res = res + b[counter];
counter = counter - 1;
}
return res;
}
我从中生成了以下汇编代码:
gcc -Og -S file.c
产生了以下汇编代码(我只包含了感兴趣的部分):
sum_arr:
.LFB41:
.cfi_startproc
subl , %esi
movl [=12=], %eax
jmp .L2
.L3:
movslq %esi, %rdx
addl (%rdi,%rdx,4), %eax
subl , %esi
.L2:
testl %esi, %esi
jns .L3
rep ret
.cfi_endproc
我在使用 .L3 时遇到了一些问题。我的理解是它通过将 int 计数器从 32 位寄存器 %esi
移动到 64 位寄存器 %rdx
开始。然后我不明白下面一行:
addl (%rdi,%rdx,4), %eax
特别是 (%rdi,%rdx,4)
部分,它被添加到 %eax
寄存器中的值。
在最后一行,它将计数器减 1。
有人可以帮我解决这个问题吗?
这种形式更容易理解:
sum_arr:
sub esi, 1
js .L4
movsx rsi, esi
mov eax, 0
.L3:
add eax, DWORD PTR [rdi+rsi*4]
sub rsi, 1
test esi, esi
jns .L3
ret
.L4:
mov eax, 0
ret
两点说明: 你的整数很可能溢出所以你应该使用 long long 作为临时 & return 值。也可以缩短
long long sum_arr(const int *b, size_t size){
long long res = 0;
while(size--){
res = res + *b++;
}
return res;
}
.L3:
movslq %esi, %rdx /* sign extend counter<%esi> to 64bit %rdx */
addl (%rdi,%rdx,4), %eax /* res<%eax> += b<%rdi>[counter<%rdx>]; */
subl , %esi /* counter<%esi> -= 1 */
.L2:
testl %esi, %esi /* do counter<%esi> & counter<%esi> */
jns .L3 /* if result is no 0, jump to L3 */
基本上addl (%rdi,%rdx,4), %eax
就是你访问数组(%rdi
)的地方,索引为counter(%rdx
),然后将元素的值添加到res(%eax
), 4
只是内存访问的计数器 (%rdx
) 的乘积,因为 int
数组中的每个地址占用系统内存中的 4 个字节。
这行基本上是说 res += MEMORY[addrssOf(b) + counter*4]
顺便说一句,我相信你想检查 int counter = size-1;
行之前的 size > 0
,而且正如 P__J__
在他的回答中提到的那样,你的 res
可能会溢出您求和的数组中每个元素的类型相同。