热门问题
时间线
聊天
视角
調用約定
来自维基百科,自由的百科全书
Remove ads
在計算機科學中,調用約定(calling convention)是一種定義子過程從調用處接受參數以及返回結果的方法的約定。不同調用約定的區別在於:
- 參數和返回值放置的位置(在寄存器中;在調用棧中;兩者混合)
- 參數傳遞的順序(或者單個參數不同部分的順序)
- 調用前設置和調用後清理的工作,在調用者和被調用者之間如何分配
- 被調用者可以直接使用哪一個寄存器有時也包括在內。(否則的話被當成ABI的細節)
- 哪一個寄存器被當作volatile的或者非volatile的,並且如果是volatile的,不需要被調用者恢復
架構舉例
32-位版本的x86架構使用了很多不同的調用約定。由於有着少量的架構寄存器,以及在歷史上聚焦於簡單性和更小代碼大小,很多x86調用約定傳遞實際參數於堆棧之上。返回值(或到它的指針)被返回在一個寄存器中。一些約定對前幾個實際參數使用寄存器,這可以增進性能,特別是對於被頻繁調用的短小而簡單的葉子例程(就是不調用其他例程的例程)。
例子調用:
push EAX ; 传递一些寄存器结果
push dword [EBP+20] ; 传递一些内存变量(FASM/TASM语法)
push 3 ; 传递一些常量
call calc ; 返回的结果将在EAX中
典型的被調用者結構如下,其中某些或全部(ret除外)指令在簡單過程中總是可以被優化掉的:
calc:
push EBP ; 保存旧的帧指针
mov EBP,ESP ; 得到新的帧指针
sub ESP,localsize ; 为局部变量预留空间
.
. ; 进行计算,留下结果于EAX
.
mov ESP,EBP ; 释放局部变量的空间
pop EBP ; 复原旧的帧指针
ret paramsize ; 释放参数空间并返回
一些約定留下分配的參數空間,使用平常的ret而非ret imm16。在這種情況下,調用者可以於此例子中add esp,12,或者用其他方式處置對ESP的變更。
Remove ads
64-位版本的x86架構,叫做x86-64、x64、AMD64或Intel 64,有兩個公用的調用序列[1]。一個調用序列是Microsoft定義的x64調用約定,用於Windows;另一個調用序列規定於System V AMD64 ABI,用於類Unix系統,並在改動後用於OpenVMS。由於x86-64比32-位x86有更多的通用寄存器,兩個約定都傳遞一些實際參數於寄存器之中。
標準32-位ARM調用約定分配16個通用寄存器為:
- r15:程序計數器(依據指令集規定)。
- r14:鏈接寄存器。BL指令,用在子例程調用中,存儲返回地址於此寄存器。
- r13:堆棧寄存器。PUSH和POP指令在「Thumb」運行模態下只使用這個寄存器。
- r12:過程調用內部(Intra-Procedure-call)暫存(scratch)寄存器。
- r4 至 r11:局部變量。
- r0 至 r3:傳給子例程的實際參數值和從子例程返回的結果值。
如果返回值的類型對於放入r0至r3太大,或者其大小在編譯時間不能靜態的確定,那麼調用者必須在運行時間為這個值分配空間,並傳遞到這個空間的一個指針於r0,
子例程必須保持(preserve)r4至r11和棧指針的內容,這可以通過在函數前言中將其保存至堆棧,接着使用它們為暫存空間,接着在函數結語中從堆棧復原它們。特別是,調用其他子例程的子例程,在調用這些其他子例程之前,「必須」將鏈接寄存器r14中的返回地址保存到堆棧。但是在返回之時,這樣的子例程不需要將這個值返回到r14,它們只需要將這個值裝載進入程序計數器r15。
ARM調用約定強制使用完全降序的堆棧。此外,棧指針必須總是4-字節對齊,並且在具有公開接口的函數調用上必須總是8-字節對齊[2]。
調用約定導致「典型」的ARM子例程會:
- 在前言中,將r4至r11壓入到堆棧,並將在r14中的返回地址壓入堆棧,這可以通過單一的STM指令完成;
- 複製任何要傳遞的實際參數(在r0至r3中)到局部暫存寄存器(r4至r11);
- 分配其他局部變量到剩餘的局部暫存器(r4至r11);
- 進行計算並按需要使用BL調用其他子例程,假定r0至r3、r12和r14會被保持;
- 將結果放入r0;
- 在結語中,將r4至r11從堆棧取出,並取出返回地址放入程序計數器r15,這可以通過單一的LDM指令完成。
Remove ads
64-位ARM(AArch64)調用約定分配31個通用寄存器為[3]:
- x31 (SP):棧指針或零寄存器,依賴上下文。
- x30 (LR):過程鏈接寄存器,用於從子例程返回。
- x29 (FP):幀指針。
- x19 至 x28:被調用者保存。
- x18 (PR):平台寄存器。用於某些操作系統特定的特殊功能,或一個額外的調用者保存寄存器。
- x16 (IP0) 和 x17 (IP1):過程調用內部(Intra-Procedure-call)暫存(scratch)寄存器。
- x9 至 x15:局部變量,調用者保存。
- x8 (XR):間接返回值地址。
- x0 至 x7:傳遞給子例程的實際參數值和從子例程返回的結果值。
所有以x開始的寄存器都由一個對應的前綴着w的32-位寄存器,比如32-位x0叫做w0。
類似的,32個浮點寄存器被分配為[4]:
- v0 至 v7:傳入的實際參數值和從子例程返回的結果值。
- v8 至 v15:被調用者保存,但只有底部64位需要被保持。
- v16 至 v31:局部變量,調用者保存。
Remove ads
參見
參考文獻
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads