Choix de quatre registres d'arguments sur x64 - commun à UN * X / Win64
Une des choses à garder à l'esprit à propos de x86 est que le nom du registre pour l'encodage "reg number" n'est pas évident; en termes de codage d'instructions (l' octet MOD R / M , voir http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm ), les numéros de registre 0 ... 7 sont - dans cet ordre - ?AX
, ?CX
, ?DX
, ?BX
, ?SP
, ?BP
, ?SI
, ?DI
.
Par conséquent, choisir A / C / D (regs 0..2) pour la valeur de retour et les deux premiers arguments (qui est la __fastcall
convention 32 bits "classique" ) est un choix logique. En ce qui concerne le 64bit, les regs "supérieurs" sont commandés, et Microsoft et UN * X / Linux ont opté pour R8
/ R9
comme premiers.
En gardant cela à l' esprit, le choix de Microsoft RAX
(valeur de retour) et RCX
, RDX
, R8
, R9
(arg [0..3]) sont une sélection compréhensible si vous choisissez quatre registres pour les arguments.
Je ne sais pas pourquoi l'AMI AMD64 UN * X a choisi RDX
avant RCX
.
Choix de six registres d'arguments sur x64 - spécifique à UN * X
UN * X, sur les architectures RISC, a traditionnellement fait passer des arguments dans les registres - en particulier, pour les six premiers arguments (c'est le cas sur PPC, SPARC, MIPS au moins). Ce qui pourrait être l'une des principales raisons pour lesquelles les concepteurs ABI AMD64 (UN * X) ont également choisi d'utiliser six registres sur cette architecture.
Donc , si vous voulez six registres pour passer des arguments dans, et il est logique de choisir RCX
, RDX
, R8
etR9
pour quatre d'entre eux, qui deux autres si vous choisissez?
Les regs "supérieurs" nécessitent un octet de préfixe d'instruction supplémentaire pour les sélectionner et ont donc une plus grande empreinte de taille d'instruction, donc vous ne voudriez pas en choisir un si vous avez des options. Parmi les registres classiques, en raison de la signification implicite de RBP
et RSP
ceux-ci ne sont pas disponibles, et ont RBX
traditionnellement une utilisation spéciale sur UN * X (table de décalage globale) avec laquelle apparemment les concepteurs AMD64 ABI ne voulaient pas devenir inutilement incompatibles.
Ergo, le seul choix était RSI
/ RDI
.
Donc, si vous devez prendre RSI
/RDI
comme registres d'arguments, quels arguments devraient-ils être?
Les fabriquer arg[0]
et arg[1]
présente certains avantages. Voir le commentaire de cHao.
?SI
et ?DI
sont des opérandes source / destination d'instruction de chaîne, et comme cHao l'a mentionné, leur utilisation comme registres d'arguments signifie qu'avec les conventions d'appel AMD64 UN * X, la strcpy()
fonction la plus simple possible , par exemple, ne comprend que les deux instructions CPU repz movsb; ret
car la source / cible les adresses ont été placées dans les registres corrects par l'appelant. Il y a, en particulier dans le code "glue" de bas niveau et généré par le compilateur (pensez, par exemple, à certains objets d'allocations de tas C ++ à remplissage nul lors de la construction, ou aux pages de tas à remplissage zéro du noyau sursbrk()
, ou copie -write pagefaults) une énorme quantité de copie / remplissage de bloc, donc il sera utile pour le code si fréquemment utilisé pour sauvegarder les deux ou trois instructions CPU qui autrement chargeraient ces arguments d'adresse source / cible dans les registres "corrects".
D'une certaine manière, l' ONU * X et Win64 ne sont différentes que dans l' ONU * X « précèder » deux arguments supplémentaires, choisis à dessein RSI
/ RDI
registres, le choix naturel de quatre arguments RCX
, RDX
, R8
et R9
.
Au-delà de ça ...
Il y a plus de différences entre les ABI UN * X et Windows x64 que le mappage d'arguments à des registres spécifiques. Pour un aperçu sur Win64, vérifiez:
http://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx
Win64 et AMD64 UN * X diffèrent également de manière frappante dans la façon dont l'espace de pile est utilisé; sur Win64, par exemple, l'appelant doit allouer un espace de pile pour les arguments de fonction même si les arguments 0 ... 3 sont passés dans les registres. Sur UN * X en revanche, une fonction feuille (c'est-à-dire une fonction qui n'appelle pas d'autres fonctions) n'est même pas obligée d'allouer un espace de pile si elle n'en a pas besoin de plus de 128 octets (oui, vous possédez et pouvez une certaine quantité de pile sans l'allouer ... enfin, sauf si vous êtes un code noyau, une source de bugs astucieux). Ce sont tous des choix d'optimisation particuliers, dont la plupart des raisons sont expliquées dans les références ABI complètes auxquelles renvoie la référence wikipedia de l'affiche originale.