请解释为什么寄存器不被压入堆栈



我正在做乔纳森·巴特利特(Jonathan Bartlett)所著的《从头开始编程》(Programming from the ground up)一书中的许多任务之一。

我正在执行第72页的任务:

阶乘函数可以非递归地编写。这样做。

下面的程序工作正常。我很难理解EBX,ECXEAX寄存器在堆栈上的位置。

为什么下面的代码不使用PUSHLPOPL指令将寄存器放在堆栈上?

压入寄存器和它们神秘地存在于堆栈中的某个地方有什么区别?

.section .data
.section .text
.globl _start

_start:
pushl $4        #pushing argument
call factorial  #calling function
addl $4, %esp       #move stack pointer back
movl %eax, %ebx     #place result into ebx for exit status
movl $1, %eax       #exit
int $0x80

#finds the factorial of a value
#INPUT: First and only argument is the number to find the factorial for
#NOTES: number must be greater than one Variables:
# %ebx – holds the argument value
# %ecx – holds the index we are multiplying the argument by
# -4(%ebp) – holds the current result
# %eax is used for temporary storage
factorial:
pushl %ebp      #save old base pointer
movl %esp, %ebp     #make stack pointer the base pointer
subl $4, %esp       #get room for local storage
movl 8(%ebp), %ebx  #put first argument in %eax
movl %ebx, %ecx     #set index to the value of argument
decl %ecx       #subtract index by 1 as starting point
movl %ebx, -4(%ebp)     #store current result
factorial_loop_start:
movl -4(%ebp), %eax     #move current result into %eax
imull %ecx, %eax    #multiple current result by index
movl %eax, -4(%ebp)     #move new result into storage
decl %ecx       #decrease index by 1
cmpl $1, %ecx       #if index is 1 jump to end of loop
je end_factorial
jmp factorial_loop_start    #if index is not one restart loop

end_factorial:
#movl -4(%ebp), %eax    #return value goes in %eax
movl %ebp, %esp     #restore stack pointer
popl %ebp       #restore base pointer
ret

#    To run, from the command line (I'm using an x86-64 build so you may need to assemble differently)
#               as --32 factorial.s -o factorial.o
#           ld -melf_i386 factorial.o -o factorial
#           ./factorial
#           echo $?

asm堆栈只是内存,就像任何其他区域一样;一旦你预留了空间,你可以用mov和寻址模式随机访问它,ESP作为基本寄存器(或者EBP,如果你把它设置为一个帧指针)。

事实上,通常这样做更方便:只有对本地变量的后进先出访问将是可怕的!

在这种情况下,它只是一个错过的优化:sub $4, %esp/…/movl %ebx, -4(%ebp)可以合并成push %ebx。(与C/c++编译器可以使用push pop指令创建局部变量,而不是只增加esp一次中所描述的错过的优化相同?)

这段低效代码的作者大概是故意这样做的,以遵循标准约定/模式来完成事情(例如,编译器在调试模式下)。

OTOH,这段代码中的一些东西很傻,比如je end_factorial只跳过了jmp factorial_loop_start。这应该是jne factorial_loop_start或失败。并且每次通过循环加载/存储产品都是零点;把它记在登记簿上就行了。这就是寄存器的作用!只有实际的编译器在调试模式下才会出现这种"愚蠢"的情况,但是编译器不会犯这种愚蠢的jeoverjmp的错误,所以这是两种情况中最糟糕的。:/