Rya*_*own 1 c x86 assembly stdcall
我一直在阅读有关汇编函数的内容,我对是否使用enter和exit或只是使用call/return指令进行快速执行感到困惑.一路快,另一路小吗?例如,在没有内联函数的情况下,在汇编中执行此操作的最快(stdcall)方法是什么:
static Int32 Add(Int32 a, Int32 b) {
return a + b;
}
int main() {
Int32 i = Add(1, 3);
}
Run Code Online (Sandbox Code Playgroud)
使用call/ ret,未做堆栈帧或者与enter/ leave或push&pop rbp / mov rbp, rsp.gcc(默认情况下-fomit-frame-pointer)只在堆栈中进行可变大小分配的函数中生成堆栈帧. 这可能会使调试稍微困难一些,因为gcc在编译时通常会发出堆栈展开信息-fomit-frame-pointer,但是你手写的asm不会有.通常,只在asm中编写叶子函数才有意义,或者至少在不调用许多其他函数的函数中编写叶子函数.
堆栈帧意味着您不必跟踪堆栈指针自从函数入口访问堆栈中的内容(例如本地的函数args和溢出槽)以来已经更改了多少.Windows和Linux/Unix 64位ABI都通过寄存器中的前几个args,并且通常有足够的regs,您不必将任何变量溢出到堆栈中.在大多数情况下,堆栈帧是浪费指令.在32位代码,其ebp可(6〜7 GP暂存器去,不计算堆栈指针),使比14将15场有较大的差异,你还是要push/popRBP,如果你不使用它,但是,因为在这两个ABI都是被调用者保存的寄存器,不允许函数被破坏.
如果你正在优化x86-64 asm,你应该阅读Agner Fog的指南,并查看x86标签wiki 中的一些其他链接.
您的函数的最佳实现可能是:
align 16
global Add
Add:
lea eax, [rdi + rsi]
ret
; the high 32 of either reg doesn't affect the low32 of the result
; so we don't need to zero-extend or use a 32bit address-size prefix
; like lea eax, [edi, esi]
; even if we're called with non-zeroed upper32 in rdi/rsi.
align 16
global main
main:
mov edi, 1 ; 1st arg in SysV ABI
mov esi, 3 ; 2nd arg in SysV ABI
call Add
; return value in eax in all ABIs
ret
align 16
OPmain: ; This is what you get if you don't return anything from main to use the result of Add
xor eax, eax
ret
Run Code Online (Sandbox Code Playgroud)
这实际上是gcc发出的Add(),但它仍然将main转换为空函数,或者转换为return 4if return i. 即使结果是编译时常量,clang 3.7-fno-inline-functions仍然尊重.它通过尾部调用优化jmp来实现我的主意Add.
请注意,Windows 64位ABI对函数args使用不同的寄存器.请参阅x86标记wiki中的链接或Agner Fog的ABI指南. 汇编程序宏可能有助于在asm中编写函数,这些函数使用正确的寄存器作为其args,具体取决于您所针对的平台.
| 归档时间: |
|
| 查看次数: |
121 次 |
| 最近记录: |