热门问题
时间线
聊天
视角

調用約定

来自维基百科,自由的百科全书

Remove ads

計算機科學中,調用約定(calling convention)是一種定義子過程從調用處接受參數以及返回結果的方法的約定。不同調用約定的區別在於:

  • 參數和返回值放置的位置(在寄存器中;在調用棧中;兩者混合)
  • 參數傳遞的順序(或者單個參數不同部分的順序)
  • 調用前設置和調用後清理的工作,在調用者和被調用者之間如何分配
  • 被調用者可以直接使用哪一個寄存器有時也包括在內。(否則的話被當成ABI的細節)
  • 哪一個寄存器被當作volatile的或者非volatile的,並且如果是volatile的,不需要被調用者恢復

架構舉例

x86 (32-位)

32-位版本的x86架構使用了很多不同的調用約定。由於有着少量的架構寄存器,以及在歷史上聚焦於簡單性和更小代碼大小,很多x86調用約定傳遞實際參數於堆棧之上。返回值(或到它的指針)被返回在一個寄存器中。一些約定對前幾個實際參數使用寄存器,這可以增進性能,特別是對於被頻繁調用的短小而簡單的葉子例程英語leaf routine(就是不調用其他例程的例程)。

例子調用:

  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

x86-64

64-位版本的x86架構,叫做x86-64、x64、AMD64或Intel 64,有兩個公用的調用序列[1]。一個調用序列是Microsoft定義的x64調用約定,用於Windows;另一個調用序列規定於System V AMD64 ABI,用於類Unix系統,並在改動後用於OpenVMS。由於x86-64比32-位x86有更多的通用寄存器,兩個約定都傳遞一些實際參數於寄存器之中。

ARM (A32)

標準32-位ARM調用約定分配16個通用寄存器為:

  • r15:程序計數器(依據指令集規定)。
  • r14:鏈接寄存器英語Link register。BL指令,用在子例程調用中,存儲返回地址於此寄存器。
  • r13:堆棧寄存器。PUSH和POP指令在「Thumb」運行模態下只使用這個寄存器。
  • r12:過程調用內部(Intra-Procedure-call)暫存(scratch)寄存器。
  • r4 至 r11:局部變量。
  • r0 至 r3:傳給子例程的實際參數值和從子例程返回的結果值。

如果返回值的類型對於放入r0至r3太大,或者其大小在編譯時間不能靜態的確定,那麼調用者必須在運行時間為這個值分配空間,並傳遞到這個空間的一個指針於r0,

子例程必須保持(preserve)r4至r11和棧指針的內容,這可以通過在函數前言英語Function prologue and epilogue中將其保存至堆棧,接着使用它們為暫存空間,接着在函數結語英語Function prologue and epilogue中從堆棧復原它們。特別是,調用其他子例程的子例程,在調用這些其他子例程之前,「必須」將鏈接寄存器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

ARM (A64)

64-位ARM(AArch64)調用約定分配31個通用寄存器為[3]

  • x31 (SP):棧指針零寄存器,依賴上下文。
  • x30 (LR):過程鏈接寄存器英語Link register,用於從子例程返回。
  • 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

參見

參考文獻

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads