電腦32位元改64位元 - 為什麼Windows64在x86-64上使用與其他所有操作系統不同的調用約定?




x86 x64怎麼看 (3)

在x64上選擇四個參數寄存器 - 對於UN * X / Win64通用

關於x86的一點需要注意的是,註冊名稱為“reg number”編碼並不明顯; 在指令編碼方面( MOD R / M字節,請參見http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm ),寄存器編號0 ... 7是 - 按該順序 - ?AX?CX?DX?BX?SP?BP?SI?DI

因此,選擇A / C / D(regs 0..2)作為返回值,前兩個參數(這是“古典”32位__fastcall約定)是合乎邏輯的選擇。 就64bit而言,“更高”的regs是有序的,微軟和UN * X / Linux都是以R8 / R9作為第一個。

牢記這一點,如果您選擇四個寄存器作參數,Microsoft選擇RAX (返回值)和RCXRDXR8R9 (arg [0..3])是一個可以理解的選擇。

我不知道為什麼AMD64 UN * X ABI在RCX之前選擇了RDX

在x64上選擇六個參數寄存器 - UN * X specific

在RISC體系結構上,UN * X通常在寄存器中進行參數傳遞 - 具體來說,前六個參數(PPC,SPARC和MIPS至少如此)。 這可能是AMD64(UN * X)ABI設計人員選擇在該架構上使用六個寄存器的主要原因之一。

所以如果你想要六個寄存器來傳遞參數,那麼選擇其中四個RCXRDXR8R9是合乎邏輯的,你應該選擇哪兩個?

“較高”的寄存器需要額外的指令前綴字節來選擇它們,因此具有更大的指令大小的佔用空間,所以如果你有選項,你不會選擇任何這些。 在經典的寄存器中,由於RBPRSP隱含含義,這些都不可用,並且RBX傳統上在UN * X(全局偏移表)上有特殊用途,看起來AMD64 ABI設計者似乎不想不必要地變得不兼容用。
Ergo, 唯一的選擇RSI / RDI

所以如果你必須把RSI / RDI作為參數寄存器,它們應該是哪個參數?

使他們arg[0]arg[1]有一些優點。 見cHao的評論。
?SI?DI是字符串指令源/目標操作數,正如cHao提到的那樣,它們用作參數寄存器意味著使用AMD64 UN * X調用約定,例如,最簡單的可能strcpy()函數僅包含兩個CPU指令repz movsb; ret repz movsb; ret因為源/目標地址已被調用者放入正確的寄存器中。 特別是在低級和編譯器生成的“粘合”代碼中(例如,想一下,一些C ++堆分配器在構建時填零對象,或者在sbrk()上填充零填充堆頁,或者copy-on - 寫頁面錯誤)大量的塊複製/填充,因此對於那些經常用來保存兩三條CPU指令的代碼非常有用,否則這些指令會將這些源/目標地址參數加載到“正確的”寄存器中。

因此,從某種意義上說,UN * X和Win64的區別僅在於,在有目的地選擇的RSI / RDI寄存器中,UN * X“前置”了兩個附加參數,自然選擇了RCXRDXR8R9的四個參數。

除此之外 ...

UN * X和Windows x64 ABI之間有更多的差異,而不僅僅是將參數映射到特定的寄存器。 有關Win64的概述,請檢查:

http://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx

Win64和AMD64 UN * X在堆棧空間的使用方式上也有驚人的不同。 例如,在Win64上,調用者必須為函數參數分配堆棧空間,即使args 0 ... 3在寄存器中傳遞。 另一方面,在UN * X上,葉函數(即不調用其他函數的函數)根本不需要分配堆棧空間,如果它不需要超過128字節(是的,你擁有並可以使用一定數量的堆棧沒有分配它......好吧,除非你是內核代碼,一個漂亮的錯誤來源)。 所有這些都是特定的優化選擇,這些原理的大部分理由在原始海報的維基百科參考指向的完整ABI參考文獻中進行了解釋。

AMD有一個ABI規範,描述了在x86-64上使用的調用約定。 除了具有自己的x86-64調用約定的Windows之外,所有的操作系​​統都遵循它。 為什麼?

有沒有人知道這種差異的技術,歷史或政治原因,還是純粹是NIH綜合徵的問題?

我知道不同的操作系統可能對更高級別的東西有不同的需求,但這並不能解釋為什麼例如Windows上的寄存器參數傳遞順序是rcx - rdx - r8 - r9 - rest on stack人都使用rdi - rsi - rdx - rcx - r8 - r9 - rest on stack

PS我知道這些調用約定是如何不同的,我知道如果需要的話可以在哪裡找到細節。 我想知道的是為什麼

編輯:如何,請參閱例如維基百科條目和從那裡的鏈接。


IDK為什麼Windows做了他們所做的事情。 查看此答案的結尾以進行猜測。 我很好奇SysV調用約定是如何決定的,所以我深入到郵件列表檔案中 ,發現了一些整潔的東西。

閱讀AMD64郵件列表中的一些舊線程很有意思,因為AMD架構師對此很積極。 例如,選擇寄存器名稱是困難的部分之一:AMD考慮重命名原始的8個寄存器r0-r7,或者調用新的寄存器如UAX

此外,來自內核開發人員的反饋意見也確定了syscallswapgs的原始設計無法使用的問題 。 這就是AMD在發布任何實際芯片之前更新指令以完成這項指令的過程 。 另外有趣的是,在2000年年底,這個假設是英特爾可能不會採用AMD64。

SysV(Linux)調用約定以及關於應該保存多少個寄存器與保存調用者的決定,最初由Jan Hubicka (一名gcc開發人員) 在2000年11月完成 。 他編譯SPEC2000並查看代碼大小和指令數量。 這個討論線索圍繞著這個SO問題的答案和評論反駁了一些相同的想法。 在第二個線程中,他提出當前序列為最優並希望是最終的,產生比一些替代方案更小的代碼

他使用術語“全局”來表示調用保存的寄存器,如果使用它們必須被推/拉。

rdirsirdx作為前三個參數的選擇是由以下因素驅動的:

  • 在其參數中調用memset或其他C字符串函數(gcc內聯代表字符串操作?)的函數中保留少量代碼大小?
  • rbx是保留的,因為在沒有REX前綴(rbx和rbp)的情況下可以訪問兩個保留調用的reg是一個勝利。 大概選擇它是因為它是唯一沒有被任何指令隱式使用的其他reg。 (代表字符串,移位計數和mul / div輸出/輸入觸摸其他所有內容)。
  • 沒有特殊用途的寄存器是保留的(參見上一節),所以想要使用rep串指令或變量計數轉換的函數可能必須將函數參數移到其他地方,但不必保存/恢復來電者的價值。
  • 我們試圖在序列的早期避免RCX,因為它通常用於特殊目的的寄存器,例如EAX,所以它在序列中缺少相同的目的。 它也不能用於系統調用,我們希望盡可能使系統調用序列與函數調用序列匹配。

    (後台: syscall / sysret不可避免地破壞了rcx (帶有rip )和r11 (帶有RFLAGS ),所以當syscall運行時,內核看不到rcx 。)

內核系統調用ABI被選擇為匹配函數調用ABI,除了r10而不是rcx ,所以像mmap(2)這樣的libc包裝函數可以只是mov %rcx, %r10 / mov $0x9, %eax / syscall

請注意,i386 Linux使用的SysV調用約定與Window的32位__vectorcall相比更為糟糕。 它傳遞堆棧中的所有內容,並且只返回edx:eax for int64,而不是小結構 。 毫不奇怪,為保持與它的兼容性做出了一些努力。 當沒有理由不這樣做時,他們做了一些事情,比如保持rbx呼叫保存,因為他們決定在原來的8(不需要REX前綴)中有另一個很好。

使ABI最優化比任何其他考慮更重要。 我認為他們做得很好。 我並不完全確定將返回的結構包裝到寄存器中,而不是在不同的regs中的不同字段。 我認為,通過價值傳遞它們的代碼,實際上並不是在字段上運行,但是這種額外的解包工作似乎很愚蠢。 他們可能有更多的整數返回寄存器,不僅僅是rdx:rax ,所以返回一個包含4個成員的結構體可能會以rdi,rsi,rdx,rax或其他方式返回它們。

他們考慮傳遞向量regs中的整數,因為SSE2可以在整數上運行。 幸運的是,他們沒有這樣做。 整數常常用作指針偏移,而堆棧內存的往返行程相當便宜 。 另外SSE2指令比整數指令佔用更多的代碼字節。

我懷疑Windows ABI設計人員可能一直致力於將32位和64位之間的差異降至最低,以利於必須將asm從一個端口移到另一個端口的用戶,或者可以在某些ASM中使用幾個#ifdef ,以便同一個源可以更多輕鬆構建一個32位或64位版本的功能。

盡量減少工具鏈中的更改似乎不太可能。 一個x86-64編譯器需要一個單獨的表,哪個寄存器用於什麼,調用約定是什麼。 與32位的重疊不太可能會大大節省工具鏈代碼的大小/複雜性。


請記住,微軟最初“對AMD64早期的努力表示不服”(來自Matthew Kerner和Neil Padgett的“現代64位計算史” ),因為他們是Intel在IA64架構上的強大合作夥伴。 我認為這意味著即使他們本來願意與ABI的GCC工程師合作在Unix和Windows上使用它們,他們也不會這麼做,因為這意味著公開支持AMD64的努力,但尚未正式完成(並且可能會讓英特爾沮喪)。

最重要的是,在那些日子裡,微軟絕對沒有對開源項目友好的傾向。 當然不是Linux或GCC。

那麼他們為什麼會在ABI上進行合作呢? 我猜測ABI是不一樣的,因為它們或多或少是在同一時間和單獨設計的。

“現代64位計算的歷史”的另一句名言:

與微軟合作同時進行的,AMD還聘請開源社區為芯片做準備。 AMD與Code Sorcery和SuSE簽訂了工具鏈合作協議(紅帽已經被英特爾委託在IA64工具鏈端口上)。 Russell解釋說SuSE生產C和FORTRAN編譯器,Code Sorcery生成一個Pascal編譯器。 Weber解釋說,該公司還與Linux社區合作準備Linux端口。 這一努力非常重要:它激勵微軟繼續投資於AMD64 Windows工作,並確保在芯片發布後即將成為當時重要的操作系統的Linux將可用。

Weber甚至說,Linux的工作對AMD64的成功至關重要,因為它使AMD能夠在沒有任何其他公司的幫助下生產端到端系統。 這種可能性確保了即使其他合作夥伴退出,AMD也有最糟糕的生存戰略,這反過來又讓其他合作夥伴因擔心自己被拋在後面而參與其中。

這表明,即使AMD沒有覺得合作必然是MS和Unix之間的最重要的事情,但擁有Unix / Linux支持是非常重要的。 也許甚至試圖說服一方或雙方妥協或合作不值得努力或冒險(?)惹惱他們中的任何一個? 也許AMD認為,甚至提出一個共同的ABI可能會延遲或破壞一個更重要的目標,那就是在芯片準備就緒時簡單地準備好軟件。

在我看來,我認為ABIs不同的主要原因是MS和Unix / Linux方面並沒有一起工作,而AMD並沒有把它看作是一個問題。