Sha*_*han 1 x86 assembly callstack cpu-architecture masm32
我是MASM的新手.我对这些指针寄存器感到困惑.如果你们帮帮我,我真的很感激.
谢谢
使用[ebp + disp8]比编码寻址模式短一个字节[esp+disp8],因为使用ESP作为基址寄存器需要一个SIB字节.请参阅rbp不允许作为SIB基础?详情.(该问题标题询问[ebp]必须编码为的事实[ebp+0].)
第一次[esp + disp8]在推送或弹出之后使用call,或者在之后,将需要在Intel CPU上使用堆栈同步uop.(Sandybridge微体系结构中的堆栈引擎是什么?).当然,mov ebp, esp首先创建堆栈帧也会触发堆栈同步uop:在无序核心(不仅仅是寻址模式)中对ESP的任何显式引用都会导致堆栈同步uop(如果堆栈引擎可能)有一个无序后端不知道的偏移量.
传统的堆栈框架设置ebp创建了一个堆栈框架的链接列表(每个保存的EBP指向父项保存的EBP,位于返回地址的正下方),便于分析和有时调试,如果您的代码没有备用元数据让您的调试器展开堆栈以显示堆栈回溯.
但是尽管使用ESP有这些缺点,但使用EBP作为帧指针通常不会更好(性能),因为它会占用堆栈的8个GP寄存器中的额外一个,而实际上你可以使用6而不是7用于堆栈以外的东西. 现代编译器默认-fomit-frame-pointer为启用优化时.
编译器很容易跟踪ESP相对于存储内容的sub esp,28移动量,因为他们知道堆栈指针的移动量.即使在push函数arg之后,它们仍然知道在函数中较早存储在堆栈中的任何东西的正确ESP相对偏移量.
人类也可以这样做,但是当你修改函数以保留一些额外的空间而忘记将ESP的所有偏移更新到你的本地和堆栈args(如果有的话)时,很容易出错.(通常不值得手工编写大型函数,这些函数不能将大部分变量保存在寄存器中.将它留给编译器,只花时间在asm中编写热循环,如果有的话.)
例外情况是你的函数分配了一个可变数量的堆栈空间(比如C alloca或C99可变长度数组int arr[n]) ; 在这种情况下,编译器将使用EBP制作传统的堆栈帧.或者在手写的asm中,如果你push在循环中使用调用堆栈作为Stack数据结构.
例如,x86 MSVC 19.14编译此C
int foo() {
volatile int i = 0; // force it to be stored to memory
return i;
}
Run Code Online (Sandbox Code Playgroud)
进入这个MASM asm.(在Godbolt编译器资源管理器中自己查看)
;;; MSVC -O2
_i$ = -4 ; size = 4
int foo(void) PROC ; foo, COMDAT
push ecx
mov DWORD PTR _i$[esp+4], 0 ; note this is actually [esp+0] ; _i$ = -4
mov eax, DWORD PTR _i$[esp+4]
pop ecx
ret 0
int foo(void) ENDP ; foo
Run Code Online (Sandbox Code Playgroud)
请注意,它为ia push而不是sub esp, 4因为保存代码大小而保留空间,并且通常具有相同的性能.它与前端的uop数相同,没有额外的堆栈同步uops,因为push它在任何显式引用之前esp,并且pop在最后一个之后.
(如果它保留了超过4个字节,我认为它只会使用普通sub esp, 8或其他.)
这里有明显错过的优化; push 0将存储它实际想要的值,而不是ECX中的任何垃圾.(C/C++编译器可以使用push pop指令创建局部变量,而不是仅仅增加esp一次?).并且pop eax将清理栈和负载i的返回值.
与禁用优化的情况相比. 请注意,_i$ = -4它与"堆栈帧"的偏移量相同,但优化的代码esp+4在此使用时用作基础ebp.这主要是MSVC内部的一个有趣的事实,如果没有优化掉帧指针的创建,它似乎会考虑EBP的位置.选择一个参考点是有道理的,并且排除它的框架指针启用选项是显而易见的选择.
;;; MSVC -O0
_i$ = -4 ; size = 4
int foo(void) PROC ; foo
push ebp
mov ebp, esp ; make a stack frame
push ecx
mov DWORD PTR _i$[ebp], 0
mov eax, DWORD PTR _i$[ebp]
mov esp, ebp
pop ebp
ret 0
int foo(void) ENDP ; foo
Run Code Online (Sandbox Code Playgroud)
有趣的是,它仍然使用push/pop来保留4个字节的堆栈空间.这次它确实在Intel CPU上引起了一个额外的堆栈同步uop,因为push ecx之后mov ebp,esp重新弄脏堆栈引擎mov esp, ebp.但那非常微不足道.
| 归档时间: |
|
| 查看次数: |
149 次 |
| 最近记录: |