Selon Intel en x64, les registres suivants sont appelés registres à usage général (RAX, RBX, RCX, RDX, RBP, RSI, RDI, RSP et R8-R15) https://software.intel.com/en- us/articles/introduction-à-x64-Assembly .
Dans l'article suivant, il est écrit que RBP et RSP sont des registres à usage spécial (RBP pointe vers la base du cadre de pile actuel et RSP pointe vers le haut du cadre de pile actuel). https://www.recurse.com/blog/7-understanding-c-by-learning-Assembly
Maintenant, j'ai deux déclarations contradictoires. La déclaration d'Intel devrait être celle de confiance, mais qu'est-ce qui est correct et pourquoi RBP et RSP sont-ils appelés à usage général?
Merci pour toute aide.
Usage général signifie que tous ces registres peuvent être utilisés avec n'importe quelle instruction effectuant le calcul avec des registres à usage général alors que, par exemple, vous ne pouvez pas faire ce que vous voulez avec le pointeur d'instruction (RIP) ou le registre des drapeaux (RFLAGS).
Certains de ces registres ont été envisagés pour être utilisés à des fins spécifiques, et le sont généralement. Les plus critiques sont le RSP et le RBP.
Si vous avez besoin de les utiliser à vos propres fins, vous devez enregistrer leur contenu avant de stocker quelque chose d'autre à l'intérieur et les restaurer à leur valeur d'origine une fois terminé.
Si un registre peut être un opérande pour add
, ou utilisé dans un mode d'adressage, c'est "à usage général" , par opposition aux registres comme le registre de segment FS
, ou RIP. Les registres GP sont également appelés "registres entiers", même si d'autres types de registres peuvent également contenir des entiers.
Dans l'architecture informatique, il est courant que les CPU gèrent en interne les registres/instructions entiers séparément des registres/instructions FP/SIMD. par exemple. processeurs de la famille Intel Sandybridge ont des fichiers de registres physiques séparés pour renommer les registres GP entier en FP/vector. Ceux-ci sont simplement appelés l'entier vs. FP enregistrer les fichiers. (Où FP est un raccourci pour tout ce dont un noyau n'a pas besoin pour sauvegarder/restaurer) pour utiliser les registres GP tout en laissant intact l'état FPU/SIMD de l'espace utilisateur.) Chaque entrée du fichier de registre FP fait 256 bits de large (pour contenir un vecteur ymm AVX), mais un fichier de registre entier les entrées ne doivent avoir qu'une largeur de 64 bits.
Sur les processeurs qui renomment les registres de segments ( Skylake ne le fait pas ), je suppose que cela ferait partie de l'état entier, tout comme RFLAGS + RIP. Mais lorsque nous parlons de "registre entier", nous entendons normalement spécifiquement un registre à usage général.
Chaque registre a une certaine particularité pour certaines instructions, à l'exception de certains des registres complètement nouveaux ajoutés avec x86-64: R8-R15. Ceux-ci ne les disqualifient pas comme usage général Le (bas 16 des) 8 originaux remontent à 8086, et il y avait des utilisations implicites de chacun d'eux même dans le 8086 original.
Pour RSP, c'est spécial pour Push/pop/call/ret, donc la plupart du code ne l'utilise jamais pour autre chose. (Et en mode noyau, utilisé de manière asynchrone pour les interruptions, vous ne pouvez donc pas le cacher quelque part pour obtenir un registre GP supplémentaire comme vous le pouvez dans le code de l'espace utilisateur: Is ESP = aussi polyvalent que EAX? )
Mais en conditionnel contrôlé (comme aucun gestionnaire de signal), vous n'avez pas besoin d'utiliser RSP pour un pointeur de pile. par exemple. vous pouvez l'utiliser pour lire un tableau en boucle avec pop, comme dans cette réponse code-golf . (J'ai en fait utilisé esp
en code 32 bits, mais la même différence: pop
est plus rapide que lodsd
sur Skylake, alors que les deux font 1 octet.)
Voir aussi Assemblage x86 - Pourquoi [e] bx est-il conservé dans les conventions d'appel? pour une liste partielle.
Je limite principalement cela aux instructions de l'espace utilisateur, en particulier celles qu'un compilateur moderne peut réellement émettre à partir du code C ou C++. Je n'essaie pas d'être exhaustif pour les regs qui ont beaucoup d'utilisations implicites.
rax
: un-opérande [i] mul/[i] div/cdq/cdqe, instructions de chaîne (stos), cmpxchg
, etc. etc. Ainsi que des encodages spéciaux plus courts pour de nombreux immédiats des instructions comme 2 octets cmp al, 1
ou 5 octets add eax, 12345
(pas d'octet ModRM). Voir aussi codegolf.SE Conseils pour jouer au golf en code machine x86/x64 .
Il y a aussi xchg
- avec-eax qui est où 0x90 nop
venait de (avant que nop
ne devienne une instruction documentée séparément en x86-64, parce que xchg eax,eax
zéro-étend eax dans RAX et ne peut donc pas utiliser le 0x90
encodage. Mais xchg rax,rax
peut toujours assembler en REX.W = 1 0x90.)
rcx
: décalage compte, rep
- chaîne compte, l'instruction lente loop
rdx
: rdx:rax
est utilisé par diviser et multiplier, et cwd/cdq/cqo pour les configurer. rdtsc
. BMI2 mulx
.rbx
: 8086 xlatb
. cpuid
utilise les quatre EAX..EDX. 486 cmpxchg8b
, x86-64 cmpxchg16b
. La plupart des compilateurs 32 bits émettront cmpxchg8
pour std::atomic<long long>::compare_exchange_weak
. (La charge pure/le magasin pur peut utiliser SSE MOVQ ou x87 fild/fistp, cependant, si vous ciblez Pentium ou une version ultérieure.) Les compilateurs 64 bits utiliseront 64 bits lock cmpxchg
, pas cmpxchg8b.
Certains compilateurs 64 bits émettront cmpxchg16b
pour atomic<struct_16_bytes>
. RBX a le moins d'utilisations implicites du 8 d'origine, mais lock cmpxchg16b
est l'un des rares compilateurs à utiliser.
rsi
/rdi
: opérations de chaîne, y compris rep movsb
que certains compilateurs intègrent parfois. (gcc insère également rep cmpsb
pour les littéraux de chaîne dans certains cas, mais ce n'est probablement pas optimal).rbp
: leave
(seulement 1 uop plus lent que mov rsp, rbp
/pop rbp
. gcc l'utilise en fait dans des fonctions avec un pointeur de cadre, quand il ne peut pas simplement pop rbp
). Aussi le horriblement lent enter
que personne n'utilise jamais.rsp
: opérations de pile: Push/pop/call/ret et leave
. (Et enter
). Et en mode noyau (pas l'espace utilisateur) utilisation asynchrone par le matériel pour enregistrer le contexte d'interruption. C'est pourquoi le code du noyau ne peut pas avoir de zone rouge.
r11
: syscall
/sysret
utilisez-le pour enregistrer/restaurer les RFLAGS de l'espace utilisateur. (Avec RCX pour enregistrer/restaurer le RIP de l'espace utilisateur).
Cas particuliers d'encodage en mode d'adressage:
(Voir aussi rbp non autorisé comme base SIB? qui concerne uniquement les modes d'adressage, où j'ai copié cette partie de cette réponse.)
rbp
/r13
ne peut pas être un registre de base sans déplacement: cet encodage signifie à la place: (dans ModRM) rel32
(Relatif au RIP) ou (dans SIB) disp32
sans registre de base. (r13
utilise les mêmes 3 bits dans ModRM/SIB, donc ce choix simplifie le décodage en ne faisant pas regarder le décodeur de longueur d'instruction le bit REX.B pour obtenir le 4e bit du registre de base). [r13]
assemble à [r13 + disp8=0]
. [r13+rdx]
assemble à [rdx+r13]
(éviter le problème en échangeant base/index lorsque c'est une option).
rsp
/r12
car un registre de base a toujours besoin d'un octet SIB. (Le codage ModR/M de base = RSP est un code d'échappement pour signaler un octet SIB, et encore une fois, plus le décodeur devrait se soucier du préfixe REX si r12
a été traité différemment).
rsp
ne peut pas être un registre d'index . Cela permet de coder [rsp]
, ce qui est plus utile que [rsp + rsp]
. (Intel aurait pu concevoir les encodages ModRM/SIB pour les modes d'adressage 32 bits (nouveau en 386), donc SIB-sans-index n'était possible qu'avec base = ESP. Cela ferait [eax + esp*4]
possible et exclut uniquement [esp + esp*1/2/4/8]
. Mais ce n'est pas utile, ils ont donc simplifié le matériel en faisant index = ESP le code pour aucun index quelle que soit la base. Cela permet deux façons redondantes d'encoder n'importe quel mode d'adressage base ou base + disp: avec ou sans SIB.)
r12
peut être un registre d'index . Contrairement aux autres cas, cela n'affecte pas le décodage de la longueur de l'instruction. En outre, il ne peut pas être contourné avec un encodage plus long comme les autres cas. AMD voulait que le registre d'AMD64 soit aussi orthogonal que possible, il est donc logique qu'ils dépensent quelques transistors supplémentaires pour vérifier REX.X dans le cadre du décodage index/sans index. Par exemple, [rsp + r12*4]
requiert index = r12, donc avoir r12
le but non général ferait d'AMD64 une pire cible de compilation.
0: 41 8b 03 mov eax,DWORD PTR [r11]
3: 41 8b 04 24 mov eax,DWORD PTR [r12] # needs a SIB like RSP
7: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] # needs a disp8 like RBP
b: 41 8b 06 mov eax,DWORD PTR [r14]
e: 41 8b 07 mov eax,DWORD PTR [r15]
11: 43 8b 04 e3 mov eax,DWORD PTR [r11+r12*8] # *can* be an index
Les compilateurs aiment quand tous les registres peuvent être utilisés pour n'importe quoi, ne contraignant l'allocation des registres que pour quelques opérations de cas spécial. C'est ce que l'on entend par orthogonalité de registre.