为什么x86架构使用两个堆栈寄存器(esp; ebp)?

lll*_*lll 7 x86 assembly x86-64

我的问题很简单,为什么x86架构使用两个堆栈寄存器(esp ; ebp)?

堆栈帧的长度已在编译期间确定,然后我认为我们可以只使用一个寄存器(例如esp)来访问堆栈,并维护以前在ebp堆栈中(或在其他内存中的寄存器中)的基址地区,但这将导致更多的性能损失)

可能吗?

pax*_*blo 10

这一切都取决于调用约定当然,但它通常是这样的.

堆栈指针是能够根据任何你需要推到,或从栈中弹出掉在任何给定的时间点任意移动.这可以在函数内的任何时间发生,因为您需要临时在堆栈上保存一些数据.

所述指针通常被设置为对于任何给定堆栈深度相同的值,并且用于访问(在另一侧)传递的参数(在一侧上)和局部变量.它还用于在退出函数时快速移动堆栈指针.

它以这种方式完成的原因是简化代码,这样您就不必根据可能改变的堆栈指针引用堆栈内容.使用基指针可以大大减轻代码生成的任务(您无需知道任何给定时间的堆栈指针是什么,只需使用基本指针,在指定函数期间保持不变).

没有它,想要推送局部变量的两个副本以调用下一个函数的代码将如下所示:

    mov    eax, [esp+16]    ; get var1
    push   eax              ; push it
    mov    eax, [esp+20]    ; get var1 again
    push   eax
    call   _somethingElse
Run Code Online (Sandbox Code Playgroud)

抛开你不会eax在这种情况下重新加载的事实,我想要做的是,移动堆栈指针中的项目的相对位置可能不必要地使问题复杂化.

例如,这是一个在程序集中编码的函数,它遵循一个通用的调用约定:

_doSomething:
    push   ebp              ; stack current base pointer
    mov    ebp, esp         ; save previous stack pointer
    sub    esp, 48          ; incl 48 bytes local storage

    ; do whatever you want here, including changing
    ;  esp, as long as it ends where it started.

    mov    esp, ebp         ; restore previous stack pointer
    pop    ebp              ; and previous base pointer

    ret                     ; then return

_callIt:
    mov    eax, 7
    push   eax              ; push parameter for function
    call   _doSomething
    add    esp, 4           ; get rid of pushed value
    :
Run Code Online (Sandbox Code Playgroud)

如果按照代码,可以看到,ebp内部函数体是具有固定基准点[ebp](的内容ebp作为返回地址)[ebp+4]被的推值7,并且[ebp-N]是用于本地存储_doSomething,其中N从变化148.

无论在函数体内推送或弹出多少项,都是如此.

  • 一个好的编译器可以轻松跟踪堆栈指针的指定位置,并调整帧的偏移量以访问它所需的内容.即使存在"内联汇编程序",编译器通常也会看到转换指令; 如果有一个"mov <reg>,<var>",编译器将用"mov <reg>,k [esp]"替换它,并且它知道k的正确值.所以这真的不是问题.OP询问x86设计者是否坚持使用*两个*堆栈指针; 他们没有.硬件指令(ESP)有一个支持,EBP仅按惯例. (5认同)
  • @computereasy:是的,它可以但是,如果你要在函数调用期间_changing_堆栈指针,代码生成会复杂得多 - 本地和参数访问需要知道`esp`是如何变化的在功能期间. (3认同)
  • @computereasy:不是它的_value,_ no,但它可能知道_from_堆栈指针的相对_offset_.但它还有一件事需要跟踪,也许,也许,只是也许,你有内联汇编搞砸了编译器的假设:-) (3认同)
  • @computereasy因为它意味着有特殊的指令修改`esp` _specifically_(`push` /`pop` /`call` /`ret`),一般来说x86假定`esp`来保存堆栈指针.其他寄存器不存在等价物; 也就是说,没有"推"改变了'ebp`.`ebp`只是_chosen_(好吧,很久以前,或多或少设计)来保持基指针,但原则上任何其他寄存器(`esi`,`edi`,...)都可以存储基数指针.在`esp`中的堆栈指针不是这样,因为堆栈指令改变_only_`esp`(不能指定另一个寄存器). (3认同)
  • @computereasy:Iwillnotexist Idonotexist非常清楚地说.x86 CPU在各种特殊指令中使用ESP.EBP IIRC仅用于在ENTER和LEAVE指令中使用,并且没有编译器再使用它们.该约定由编译器选择; 它可能会也可能不会使用EBP作为帧指针.(我认为MS和GCC C编译器都有"省略帧指针"选项). (2认同)