Question

je regardais les différentes instructions de montage et je suis confus sur la façon dont les longueurs des différents opérandes et opcodes sont décidées.

Est-ce quelque chose que vous devrait savoir de l'expérience, ou est-il un moyen de savoir quelle combinaison opérande / opérateur prend le nombre d'octets?

Pour exemple:

push %ebp ; takes up one byte
mov %esp, %ebp ; takes up two bytes

La question est:

En voyant une instruction donnée, comment puis-je déduire combien d'octets son opcode exigera?

Était-ce utile?

La solution

Il n'y a pas de règle rapide pour x86 sans base de données comme l'encodage d'instruction est assez complexe (et l'opcode lui-même peut varier de 1 à 3 octets). Vous pouvez consulter le Intel® 64 et IA-32 Architectures Logicielles Manuel du développeur 2A Document (Chapitre 2: Format d'instructions) pour voir comment les instructions et leurs opérandes sont codées:

Autres conseils

La longueur de l'op-code est construit avec (au moins) deux critères à l'esprit

  • la fréquence de l'op-code (mis sur 1 octet si souvent utilisé dans les programmes, et si possible)
  • les informations nécessaires à l'op-code pour la fonction (si vous avez besoin d'une adresse absolue, le code ne peut pas être codé sur un octet unique)

En outre,

  • entre les processeurs les plus récents 8088 initial Intel (3 décennies) beaucoup de nouvelles instructions ont été créées, et quelques-uns, tout en apparaissant fréquemment dans les programmes, ne pouvait pas être codé sur un seul octet, car les 256 valeurs entières ont été réservés .

Outre le lien fourni dans une autre réponse (que les listes spécifiquement la taille d'un code), vous pouvez aussi consulter les processeurs histoire .

Terminologie: "opcode" est la partie de l'instruction qui sélectionne l'opération, non y compris les opérandes ou les préfixes non obligatoires qui modifient l'opération (par exemple, l'opérande de taille). L'utilisation de « opcode » pour se référer à l'instruction tout est incorrect, bien que fait assez souvent par des gens qui parlent shellcode.

  

Est-ce quelque chose que vous devez savoir de l'expérience

Avec l'expérience regardant le code de la machine ou de l'expérience en particulier l'optimisation pour la taille de code, alors oui, vous allez commencer à se souvenir des choses que vous avez regardé à plusieurs reprises, et d'apprendre à regarder une ligne asm et de savoir combien de temps l'instruction sera, sans mémoriser ce les octets seront.

Les règles de codage opérandes ne dépendent pas de l'opcode, il vous suffit de se rappeler les longueurs d'opcode, et la courte spéciale cas des formes qui n'utilisent pas un octet ModR / M pour coder opérandes. Et puis rappelez-vous les règles séparément codant pour opérandes.

Pour moi personnellement, je tiens à répondre à des questions code de golf comme celui-ci avec le code machine x86. ( Voir aussi Conseils pour jouer au golf en x86 / x64 machines Code ). Je vous écris dans MSNA, la planification / sachant combien de temps chaque instruction sera, et que l'assembleur générer le hexdump du code de la machine réelle comme une liste. Pour les instructions courtes qui sont utiles pour le code-golf, je ne me souviens pas avoir eu tort sur la longueur de l'instruction tout moment récemment, mais je suis chanceux d'avoir une bonne mémoire pour plus de détails (comme le jeu d'instructions x86) que je trouve intéressant ou utiliser beaucoup. (Je ne dois essayer rorx pour voir combien de temps il était.)

Je ne tape pas la machine code moi-même octets; de le faire à la main je dois regarder chaque instruction dans le manuel. x86 ne pas court encodages pour PC-adressage relatif, afin de trouver / créer des constantes utiles dans le code de la machine (qui pourrait doubler en tant que données) ne sont pas une chose, il est donc généralement pas utile pour le code-golf à mémoriser l'un des numérique détails de l'encodage instruction.

Lors de l'optimisation de la performance, plus petit est généralement mieux quand tout le reste est égal, si attentionné sur le code de taille et surtout l'alignement est certainement une partie de la performance.

  

ou est-il un moyen de savoir quel opérande / combinaison opérateur prend le nombre d'octets?

Ceci est bien documenté dans les manuels. A part quelques instructions 1 octet cas spéciaux, opérande encodage est le même pour tout (ou presque).


Le codage code machine de la plupart des instructions x86 suivent ce modèle (meilleure version de schéma de ce Intel dans @ réponse de Mehrdad):

[prefixes] opcode ModR/M [extra addressing-mode bytes] [immediate]

(instructions sans opérandes explicites n'ont pas un octet ModR / M, juste l'octet opcode (s)).

x86 opcodes sont 1 octet pour la plupart des instructions communes, en particulier les instructions qui existent depuis 8086. Les instructions ajoutées par la suite (par exemple comme bsf et movsx en 386) utilisent souvent opcodes 2 octets avec un octet d'échappement 0f. Si vous traînez sur, vous verrez beaucoup de questions portant sur 8086 spécifiquement (en particulier emu8086); c'est la principale raison pour laquelle je sais quelque chose dont les instructions ne sont pas disponibles sur 8086. Si vous préférez simplement rappeler directement les instructions qui ont sans les détails historiques, qui est tout à fait bien opcodes 2-octet. Ou tout simplement le chercher à chaque fois dans le manuel: P

par exemple. 0f b6 c0 movzx eax,al, de sorte 0F B6 est le opcode pour mov r32, r/m8, et C0 est l'octet ModR / M qui code pour eaxcomme destination (champ /r = 0), registre pour le mode direct de la source (top 2 bits = 11), et al que le registre de source (/m champ = 0).

J'utilise la syntaxe Intel pour tous mes exemples (mnemonic dst, src1 [,src2, ...]) parce que correspond à ce que vous trouverez dans Intel et les manuels d'AMD. Autant que je sache, il n'y a pas de manuels codant pour des instructions détaillées qui utilisent la syntaxe AT & T. J'utilise également des exemples 32 ou 64 bits, même quand on parle de ce que 8086 avait. Bien sûr, 8086 seulement eu mode réel 16 bits, mais le même opcode et l'encodage est utilisé en mode 64 bits (ce qui est ce que nous nous soucions de nos jours).


Intel ref jeu d'instructions. manuel (SDM vol.2) a opcode cartes pour 1, 2, 3 opcodes octet (annexe A.3), de sorte que vous pouvez voir quelques modèles dans le choix du codage opcode. Ou pour une instruction donnée, regardez l'encodage répertorié avec la description complète dans ce manuel. (Voir aussi avec une page par instruction, comme https://github.com/ HJLebbink / asm-dude / wiki et http://felixcloutier.com/x86/ . les balises de page de HJ LEBBINK chaque instruction avec quand il a été introduit, de sorte que vous pouvez voir 8086 pour add, ou 386 pour de nouvelles formes de déplacements, et pour movzx).

Notez que certains un opérandes des instructions, comme shl ou not, utiliser le champ /r de l'octet ModR / M en tant que bits de code opération d'appoint. Aussi la plupart des instructions avec un immédiat sont toujours destructeurs parce qu'ils utilisent le champ /r sous forme de bits d'opcode. imul r32, r/m32, imm32 (386) fait exception à cette règle, ayant un effet immédiat et en utilisant l'octet complet ModR / M pour les deux opérandes. (Notez que le ModR / M ne peut signaler enregistrer ou la mémoire d'opérandes, le type d'encodage add r/m32, imm8 utilise l'opcode pour indiquer qu'il y a un jour mais le principal octet de code opération est partagé par plusieurs instructions, de sorte que le champ /r est utilisé dans le cadre de la. opcode et que de pourquoi nous n'avons pas add r/m32, r32, imm8. Mais pour ADD / SUB nous pouvons utiliser lea ecx, [rax + 1] comme copie et add.)


Opérande encodage:

La plupart des instructions avec un opérande immédiat sont la même longueur que la version source registre / mémoire, plus les octets pour coder l'immédiat. Immediates sont soit IMM8 ou imm32, donc les valeurs de -128..127 sont plus compactes. (Dans le mode 16 bits, il est soit IMM8 ou imm16).

L'octet ModR / M est tout ce qui est nécessaire pour enregistrer directement, soit le plus simple mode d'adressage d'un registre sans déplacement. (Sauf [esp]). Donc add eax, ecx est de 2 octets, tout comme add eax, [ecx]. Indexé les modes d'adressage (et modes avec esp / rsp comme registre de base) ont besoin d'un octet SIB (échelle / index / base).

déplacements constants dans les modes d'adressage ont besoin d'un 1 ou 4 octets (disp8 signe étendu ou disp32) supplémentaires sur le dessus du ModR / M + facultatif SIB.

AVX512 EVEX avec disp8 échelles de la disp8 par la largeur de vecteur, de sorte vaddps zmm31, zmm30, [rsi + 256] est à seulement 7 octets (4 octets EVX + opcode = 0x58 + ModRM + disp8), mais vaddps zmm31, zmm30, [rsi + 16] est de 11 octets: il doit utiliser un disp32 pour encode +16, parce que ce n'est pas un multiple de 64. mais la même instruction avec xmm registres pourraient utiliser un disp8.

Les manuels Voir Intel pour tous les détails.


formes courtes-spéciales de la plupart des instructions communes

Pour enregistrer la taille de code, 8086 (et x plus tard86) fournit des encodages spéciaux sans octet ModR / M pour certaines instructions très communes. Si l'instruction est pas un de ceux-ci, il utilise un octet ModR / M

  • Ajout / adc / sous / cmp / test / et / ou / XOR / etc. AL / AX / EAX avec une immédiate de la même taille que le registre. par exemple. and eax, imm32 (5 octets) ou and al,imm8 (2 octets). Mais il n'y a pas spécial pour coder and eax, imm8; qui doit encore utiliser le codage and r/m32, imm8 3 octets. L'utilisation al peut être très bon pour le code de taille lorsque vous travaillez avec des données 8 bits, surtout si vous avez évité ou ne sont pas inquiets stalles-registre partiel ou dépendances fausses causer des problèmes de performance.
  • décalage / rotation avec un nombre de 1: 8086 n'a pas eu IMM8 tourne, que par cl ou par un 1 implicite, donc il y a des opcodes comme shl r/m32,1 où le 1 est implicite

    .

    En utilisant l'encodage de imm8 a des implications de performance: stands potentiels sur P6- famille car il ne vérifie pas si le IMM8 est égal à zéro jusqu'à l'exécution. Mais la forme courte est rol r32,1 2 UOP, contre 1 pour rol r32, imm8 (même si le IMM8 est 1) sur Sandybridge-famille, y compris Skylake. Le formulaire court rcl r32,1 est beaucoup plus rapide qu'avec un IMM8. ( 3 uops vs 8 sur Skylake).

plusieurs où le registre est codé dans le bas de 3 bits de l'octet d'instruction , en consacrant efficacement 8 octets d'espace de codage opcode pour rendre le registre opérande forme de ces instructions 1 octet plus courte.

  • mov r8, imm8:. 2 octets au lieu de trois pour le codage mov r/m8, imm8 général
  • mov r32, imm32 : 5 octets au lieu de 6 octets pour mov r/m32, imm32. Fun fait: dans x86-64, la REX.W = 1 version du opcode court formulaire est la seule instruction qui peut utiliser une immédiate de 64 bits. mov r64, imm64 10 octets. La REX.W = 1 version du r/m32 opcode utilise toujours un immédiate de 32 bits (extension de signe, comme d'habitude), de sorte mov rax, -1 est mieux codé de cette façon, en prenant 7 octets vs. mov eax,-1 5 octets. (Ou si l'optimisation pour la taille de code, vous pouvez aussi tous les bits sont dans le registre du processeur à 1 efficace .)
  • push / pop registre , 1 octet par rapport à 2 octets pour la encodage pop r/m32.
  • push / pop registres de segments (autres que FS / GS). Bien qu'il n'y ait pas un codage pour r / M16 ceux-ci.
  • inc r32 / dec r32 (16 / mode 32 bits uniquement: les octets 0x4X sont préfixes REX dans x86-64, donc inc eax doit utiliser la inc r/m32 codage) de 2 octets.
  • xchg eax, reg : C'est là 0x90 nop vient: le court-forme de xchg eax,eax (ou en mode 16 bits, xchg ax,ax). Dans x86-64, 90 nop n'est pas aussi xchg eax,eax, parce que ce serait-extend zéro EAX dans RAX. Au lieu de cela, il a sa propre entrée manuelle jeu d'instructions .

    xchg reg,reg est jamais utilisé par Compilers, et est généralement pas plus vite que 3 des instructions de mov , donc ce serait bien si nous avions les 7 opcode octets de retour pour les extensions futures plus utiles. (Ou 8 si nop a été déplacé vers un autre opcode ...). Il était plus utile en 8086 lorsque l'accumulateur était « plus spécial », par exemple cbw à signer AL-s'étendre dans AX est la seule (bonne) façon parce movsx n'existait pas. Et seulement 1 opérandes mul / imul était disponible.

xchg eax, r32 est encore grand pour le code-golf, par exemple GCD à 8 octets de code machine x86 32 bits . Voir aussi mes autres réponses code golf pour différents tours de taille de code (principalement au détriment de la performance, qui est le point de code golf).

Je pense que ce couvre tous les étuis spéciaux de octet d'instructions qui ont également encodages r/m32.


Cette réponse ne vise pas à être exhaustive . Je ne l'ai pas parlé des instructions plus récentes beaucoup, et il y a beaucoup de cas particuliers pour obtenir des instructions rares. Les règles quand un préfixe préfixe REX ou opérande-size sont nécessaires sont assez simples. Voici quelques règles plus générales:

  • SSE1 / SSE3 instructions de codes d'opération ABCps avoir deux octets (0F xx)
  • entier SSE2 / instructions double précision ont généralement opcodes 3 octets (66 0F xx ou similaire)
  • instructions SSSE3 / SSE4.x avoir opcodes 4 octets (3 préfixes obligatoires)

instructions codées VEX peut utiliser un préfixe VEX 2 octets si la version SSE est SSE3 ou plus tôt, et le registre 2ème source n'est pas un "haut" registre (XMM / ymm8-15). Les versions XMM et YMM de la même instruction sont toujours la même taille. (Mais préfèrent XMM avec zéro extension implicite au lieu de YMM explicite quand vous ne voulez pas des soins ou la moitié haute remis à zéro.)

vpxor  ymm8,ymm8,ymm5    ; 2-byte VEX
vpxor  ymm7,ymm7,ymm8    ; 3-byte VEX
vpxor  ymm7,ymm8,ymm7    ; 2-byte VEX

On peut donc utiliser des registres « haut » comme la source ou la destination première sans avoir besoin d'un VEX 3 octets, mais pas comme la 2ème source (3ème opérande global). Pour les opérations commutatives, vous pouvez réduire la taille en plaçant celle de bas8 comme source 2.

Notez que pour 4 opérandes des instructions comme vblendvps , 4ème opérande est codé dans un imm8. Il est donc toujours le 3ème opérande (2ème source), pas le dernier opérande, ce qui affecte la taille préfixe VEX est nécessaire. Mais blendvps est SSE4.1, il a toujours besoin d'un préfixe VEX 3 octets de toute façon de représenter le codage de 66.0F3A du champ de préfixe.

En général, ce n'est pas quelque chose que vous devez savoir d'une instruction à l'autre lors de la programmation en langage assembleur. Si jamais il importe (comme si vous essayez d'adapter un code particulier dans un espace contraint), vous pouvez regarder la sortie de la liste de l'assembleur, ou une liste de démontage.

De mes 6510 jours de montage, la réponse généralement concernait les adresses et les décalages opérandes. Opcodes étaient toujours 1 octet pour les 6510. adresses étaient toujours deux octets. Si l'opcode nécessaire une adresse, alors je savais que la taille totale était de trois octets. Si deux adresses ont été spécifiées, alors je savais que la taille totale était de 5 octets.

En ce qui concerne les compensations, l'espace qu'ils occupaient était subordonnée à la longueur de la branche. Donc, considérez ceci:

bne FooBar

Si le « Foobar » décalage pointait vers une adresse qui était inférieure à 128 octets sur, l'opérande est un seul octet. Si compenser la pointe à une adresse au-delà, puis une adresse complète était nécessaire. Une adresse complète ne fut plus un décalage et d'adresses bien sûr occupé deux octets.

Donc, dans ce dernier cas, il pourrait ne pas être facile de dire si l'opcode + opérande requis deux ou trois octets.

Je suppose que, parfois, vous pouvez dire et d'autres fois il est pas si évident.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top