Scelta di quattro registri di argomenti su x64 - comune a UN * X / Win64
Una delle cose da tenere a mente riguardo a x86 è che il nome del registro per la codifica "numero di registro" non è ovvio; in termini di codifica dell'istruzione (il byte MOD R / M , vedere http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm ), i numeri di registro 0 ... 7 sono - in quest'ordine - ?AX
, ?CX
, ?DX
, ?BX
, ?SP
, ?BP
, ?SI
, ?DI
.
Quindi scegliere A / C / D (regs 0..2) per il valore di ritorno e i primi due argomenti (che è la __fastcall
convenzione "classica" a 32 bit ) è una scelta logica. Per quanto riguarda i 64 bit, vengono ordinati i reg "superiori", e sia Microsoft che UN * X / Linux hanno scelto R8
/ R9
come primi.
Tenendo questo in mente, la scelta di Microsoft di RAX
(valore di ritorno) e RCX
, RDX
, R8
, R9
(arg [0..3]) sono una scelta comprensibile se si sceglie quattro registri per argomenti.
Non so perché l'AMD64 UN * X ABI abbia scelto RDX
prima RCX
.
Scelta di sei registri di argomenti su x64 - UN * X specifico
UN * X, su architetture RISC, ha tradizionalmente eseguito il passaggio di argomenti nei registri, in particolare per i primi sei argomenti (almeno così su PPC, SPARC, MIPS). Questo potrebbe essere uno dei motivi principali per cui i progettisti ABI AMD64 (UN * X) hanno scelto di utilizzare anche sei registri su quell'architettura.
Quindi, se volete sei registri per passare argomenti, ed è logico scegliere RCX
, RDX
, R8
e R9
per quattro di loro, che altri due si dovrebbe scegliere?
I reg "più alti" richiedono un byte di prefisso dell'istruzione aggiuntivo per selezionarli e quindi hanno un'impronta di dimensione dell'istruzione maggiore, quindi non vorresti sceglierne nessuna se hai delle opzioni. Dei registri classici, per il significato implicito di RBP
e RSP
questi non sono disponibili, e RBX
tradizionalmente ha un uso speciale su UN * X (tabella di offset globale) con cui apparentemente i progettisti ABI di AMD64 non volevano diventare inutilmente incompatibili.
Ergo, l' unica scelta era RSI
/ RDI
.
Quindi, se devi prendere RSI
/ RDI
come registro degli argomenti, quali argomenti dovrebbero essere?
Farli arg[0]
e arg[1]
ha alcuni vantaggi. Vedi il commento di cHao.
?SI
e ?DI
sono operandi sorgente / destinazione di istruzioni di stringa e, come menzionato da cHao, il loro uso come registri di argomenti significa che con le convenzioni di chiamata UN * X di AMD64, la strcpy()
funzione più semplice possibile , ad esempio, consiste solo delle due istruzioni della CPU repz movsb; ret
perché l'origine / destinazione gli indirizzi sono stati inseriti nei registri corretti dal chiamante. Esiste, in particolare nel codice "collante" generato dal compilatore e di basso livello (si pensi, ad esempio, ad alcuni allocatori di heap C ++ oggetti che riempiono zero durante la costruzione, o le pagine heap che riempiono lo zero del kernel susbrk()
, o copy-on-write pagefaults) un'enorme quantità di block copy / fill, quindi sarà utile per il codice così frequentemente usato per salvare le due o tre istruzioni della CPU che altrimenti caricherebbero tali argomenti di indirizzo di origine / destinazione nel registri "corretti".
Quindi, in un certo senso, UN * X e Win64 sono diversi solo in quella UN * X "antepone" due argomenti aggiuntivi, in volutamente scelti RSI
/ RDI
registri, per la scelta naturale di quattro argomenti a RCX
, RDX
, R8
e R9
.
Oltre a questo ...
Esistono più differenze tra le ABI UN * X e Windows x64 oltre alla semplice mappatura degli argomenti a registri specifici. Per la panoramica su Win64, controlla:
http://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx
Win64 e AMD64 UN * X differiscono notevolmente anche nel modo in cui viene utilizzato lo stackspace; su Win64, ad esempio, il chiamante deve allocare lo spazio dello stack per gli argomenti della funzione anche se gli argomenti 0 ... 3 vengono passati nei registri. Su UN * X d'altra parte, una funzione foglia (cioè una che non chiama altre funzioni) non è nemmeno richiesta per allocare lo spazio dello stack se non ha bisogno di più di 128 byte di esso (sì, tu possiedi e puoi usare una certa quantità di stack senza allocare ... beh, a meno che tu non sia codice del kernel, una fonte di bug ingegnosi). Tutte queste sono scelte di ottimizzazione particolari, la maggior parte delle ragioni per queste è spiegata nei riferimenti ABI completi a cui fa riferimento il riferimento di wikipedia del poster originale.