看完ddk裏相關的部分,總結下吧,規則倒是不復雜,相對x86時代的stdcall cdecl fastcall 三分天下要簡明的多。按ddk裏的說法,m$就是要趁這次統一調用規則…… -__-
在x64下函數調用的前4個參數總是放在寄存器中傳遞,剩餘的參數則壓入堆棧中。而x86上則是全部壓入堆棧中(除了fastcall方式)。這4個用於存放參數的寄存器分別是:存放整數參數的RCX,RDX,R8,R9;存放浮點數參數的XMM0,XMM1,XMM2,XMM3。
按照所傳參數是整數還是浮點數的不同,寄存器的使用規則如下:
全部整數參數:
func1(int a, int b, int c, int d, int e);
參數a放入RCX,參數b放入RDC,參數c放入R8,參數d放入R9,參數e麼壓棧。 參數傳遞規則:按照參數表聲明的順序,從左向右,前4個參數依次放入RCX,RDX,R8,R9中。 全部浮點數參數:
func1(float a, float b, float c, double d, float e);
a放入XMM0,b放入XMM1,c放入XMM2,d放入XMM3,e壓棧
參數傳遞規則:按照參數聲明的順序,從左向右,前4個參數依次放入XMM0,XMM1,XMM2,XMM3中
整數和浮點數參數混合出現:
func3(float a, int b, double c, int d)
a放入XMM0中,b放入RDX,c放入XMM2,d放入R9。
這裏比較特殊,其實就是按照這個規則:
a b c d
RDX R9
XMM0 XMM2
也就是說4個整數寄存器嚴格的一一對應前4個參數,同樣前4個XMM寄存器嚴格的一一應前4個參數,如果是整數浮點數間隔出現,那麼就保持對應關係,選擇對應的寄存器即可。
指針參數:
指針的傳遞遵循整數參數傳遞方式。
結構體參數:
結構體特殊一點,按照ddk的描述,如果結構體長度小於64bit,則使用整數參數的傳遞規則。但如果是一個很大的結構體,那麼應該還是要在堆棧中申請臨時空間的(但ddk沒有明說這一點,參考x86的規則應該如此)。
未聲明函數的調用:
ddk裏特別列舉了這樣一個例子:
func1();
func2(){
func1(2, 1.0, 7)
}
在這種情況下,func1()的參數表其實不明確,那麼參數的傳遞要怎樣進行?這裏採用了一個比較保守的規則,就是:整數參數還是按照寄存器映射關係放入對應的寄存器中,浮點數在按照映射關係放入XMM寄存器後,還需要按照整數參數的寄存器映射關係放入整數寄存器中一次,這就是爲啥我說是“比較保守的規則”。就現在這個例子而言,結果如下:
2在RCX中,1.0在RDX和XMM1中,7在R8中。
|
版權爲 win_hate 所有, 轉載請保留作者名字 我這段時間要把以前的一個 x86_32 的 linux 程序移植到 x86_64(AMD) 的 linux 環境裏. 由於寫的是數學算法, 64 與 32 位有很大不同, 代碼實際上要重寫. 看了點資料後, 覺得 AMD64 的擴展於以前 16 到 32 位的擴展很類似, e**, 擴展爲 r**, 此外還多了8個通用寄存器 r8~r15.指令格式與32位的極爲相似. 我覺得比較容易, 所以沒再仔細看, 就開始動手寫了. 我的程序由若干個彙編模塊於與若干個c模塊構成, 很多c模塊要調用匯編模塊. 作爲試驗, 我先寫了個簡單的彙編函數, 然後用c來調用. 結果算出來的值始終是錯誤的. 這令我很惱火, 因爲函數很簡單, 沒有多少出錯的餘地. 後來我把程序反匯編出來, 錯誤馬上浮現出來了, 函數的參數居然是通過寄存器來傳遞的. 我憑以前的經驗, 從堆棧裏取參數, 算出的結果當然不對了. 我以前不是沒碰到過用寄存器傳遞參數的情況, 但所在的環境都不是 pc. 在 x86_32/linux 中, 即使用 -O3 優化選項, gcc 仍通過棧來傳遞參數的. 所以我們現在知道, 在 x86_64/linux/gcc3.2 中, 即使不打開優化選項, 函數的參數也會通過寄存器來傳遞, 這肯定是闊了的表現(通用寄存器多了). 我試驗了多個參數的情況,發現一般規則爲, 當參數少於7個時, 參數從左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。當參數爲 7 個以上時, 前 6 個與前面一樣, 但後面的依次從 "右向左" 放入棧中。
例如:
(1) 參數個數少於7個:
g (a, b) 有趣的是, 實際上將參數放入寄存器的語句是從右到左處理參數表的, 這點與32位的時候一致. CODE
2) 參數個數大於 7 個的時候
教訓:
補充: 環境爲 AMD Athlon64, Mandrak linux 9.2, GCC3.3.1 |