Comment beaucoup de façons de mettre un registre à zéro?

je suis curieux de savoir combien de façons sont là pour mettre un registre à zéro dans x86 assembly. En utilisant une seule instruction. Quelqu'un m'a dit qu'il a réussi à trouver au moins 10 façons de le faire.

ceux auxquels je pense sont:

xor ax,ax
mov ax, 0
and ax, 0
26
demandé sur GJ. 2011-01-28 18:30:07

7 réponses

il y a beaucoup de possibilités de mov 0 dans la hache sous IA32...

    lea eax, [0]
    mov eax, 0FFFF0000h         //All constants form 0..0FFFFh << 16
    shr eax, 16                 //All constants form 16..31
    shl eax, 16                 //All constants form 16..31

Et peut-être le plus étrange... :)

@movzx:
    movzx eax, byte ptr[@movzx + 6]   //Because the last byte of this instruction is 0

et...

  @movzx:
    movzx ax, byte ptr[@movzx + 7]

Edit:

et en mode cpu 16 bits x86, non testé...:

    lea  ax, [0]

et...

  @movzx:
    movzx ax, byte ptr cs:[@movzx + 7]   //Check if 7 is right offset

le préfixe cs: est facultatif dans le cas où le registre de segment ds n'est pas égal au registre de segment cs.

12
répondu GJ. 2011-01-28 20:14:29

Voir cette réponse pour le meilleur de façon à zéro des registres: xor eax,eax (les avantages de performance, et les plus petites de l'encodage).


je vais considérer juste les façons dont une seule instruction peut zéro un registre. Il y a beaucoup trop de façons si vous autorisez le chargement d'un zéro depuis la mémoire, donc nous exclurons principalement les instructions qui chargent depuis la mémoire.

j'ai trouvé 10 différentes instructions simples qui zéro un registre de 32 bits (et donc le registre complet de 64 bits en mode long), sans pré-conditions ou charges de toute autre mémoire. Il ne s'agit pas de compter les différents encodages du même insn, ou les différentes formes de mov . Si vous comptez le chargement de la mémoire qui est connu pour contenir un zéro, ou des registres de segment ou autre, il y a une cargaison de moyens. Il existe également un zillion de façons de zéro des registres vectoriels.

Pour la plupart de ces, les versions EAX et rax sont des codages séparés pour la même fonctionnalité, les deux mettant à zéro les registres complets de 64 bits, soit mettant à zéro la moitié supérieure implicitement ou écrivant explicitement le registre complet avec un REX.W préfixe.

Integer registres:

# Works on any reg unless noted, usually of any size.  eax/ax/al as placeholders
and    eax, 0         ; three encodings: imm8, imm32, and eax-only imm32
andn   eax, eax,eax   ; BMI1 instruction set: dest = ~s1 & s2
imul   eax, any,0     ; eax = something * 0.  two encodings: imm8, imm32
lea    eax, [0]       ; absolute encoding (disp32 with no base or index).  Use [abs 0] in NASM if you used DEFAULT REL
lea    eax, [rel 0]   ; YASM supports this, but NASM doesn't: use a RIP-relative encoding to address a specific absolute address, making position-dependent code

mov    eax, 0         ; 5 bytes to encode (B8 imm32)
mov    rax, strict dword 0   ; 7 bytes: REX mov r/m64, sign-extended-imm32.    NASM optimizes mov rax,0 to the 5B version, but dword or strict dword stops it for some reason
mov    rax, strict qword 0   ; 10 bytes to encode (REX B8 imm64).  movabs mnemonic for AT&T.  normally assemblers choose smaller encodings if the operand fits, but strict qword forces the imm64.

sub    eax, eax         ; recognized as a zeroing idiom on some but maybe not all CPUs
xor    eax, eax         ; Preferred idiom: recognized on all CPUs

@movzx:
  movzx eax, byte ptr[@movzx + 6]   //Because the last byte of this instruction is 0.  neat hack from GJ.'s answer

.l: loop .l             ; clears e/rcx... eventually.  from I. J. Kennedy's answer.  To operate on only ECX, use an address-size prefix.
; rep lodsb             ; not counted because it's not safe (potential segfaults), but also zeros ecx

"Maj tous les bits à une fin" n'est pas possible pour le format standard GP registres, seulement partielle des registres. Les nombres de postes shl et shr sont masqués: count &= 31; , équivalent à count %= 32; . (Mais les numéros 286 et antérieurs sont de 16 bits seulement, de sorte que ax est un registre" complet". La forme shr r/m16, imm8 de comptage de variables de l'instruction a été ajoutée 286, donc il y avait des CPU où un décalage peut zéro un registre entier complet.)

aussi noter que le nombre de déplacements pour les vecteurs saturent au lieu de les envelopper.

# Zeroing methods that only work on 16bit or 8bit regs:
shl    ax, 16           ; shift count is still masked to 0x1F for any operand size less than 64b.  i.e. count %= 32
shr    al, 16           ; so 8b and 16b shifts can zero registers.

# zeroing ah/bh/ch/dh:  Low byte of the reg = whatever garbage was in the high16 reg
movxz  eax, ah          ; From Jerry Coffin's answer

zéro dans un autre reg):

bextr  eax,  any, eax  ; if al >= 32, or ah = 0.  BMI1
BLSR   eax,  src       ; if src only has one set bit
CDQ                    ; edx = sign-extend(eax)
sbb    eax, eax        ; if CF=0.  (Only recognized on AMD CPUs as dependent only on flags (not eax))
setcc  al              ; with a condition that will produce a zero based on known state of flags

PSHUFB   xmm0, all-ones  ; xmm0 bytes are cleared when the mask bytes have their high bit set

vecteur regs:

certaines de ces instructions D'entier SSE2 peuvent également être utilisées sur les registres MMX ( mm0 - mm7 ). Encore une fois, le meilleur choix est une certaine forme de xor. Soit PXOR / VPXOR , soit XORPS / VXORPS .

AVX vxorps xmm0,xmm0,xmm0 zéros le plein ymm0/ zmm0, et est meilleur que vxorps ymm0,ymm0,ymm0 sur AMD CPUs . Ces instructions de mise à zéro ont trois encodages: SSE legacy, AVX (préfixe VEX), et AVX512 (préfixe EVEX), bien que la version SSE zéros seulement le 128 inférieur, qui n'est pas le registre complet sur les CPU qui supportent AVX ou AVX512. Quoi qu'il en soit, selon la façon dont vous comptez, chaque entrée peut être trois instructions différentes (même opcode, cependant, juste des préfixes différents). Sauf vzeroall , qui AVX512 n'a pas changé (et ne fait pas zéro zmm16-31).

ANDNPD    xmm0, xmm0
ANDNPS    xmm0, xmm0
PANDN     xmm0, xmm0     ; dest = ~dest & src

PCMPGTB   xmm0, xmm0     ; n > n is always false.
PCMPGTW   xmm0, xmm0     ; similarly, pcmpeqd is a good way to do _mm_set1_epi32(-1)
PCMPGTD   xmm0, xmm0
PCMPGTQ   xmm0, xmm0     ; SSE4.2, and slower than byte/word/dword


PSADBW    xmm0, xmm0     ; sum of absolute differences
MPSADBW   xmm0, xmm0, 0  ; SSE4.1.  sum of absolute differences, register against itself with no offset.  (imm8=0: same as PSADBW)

  ; shift-counts saturate and zero the reg, unlike for GP-register shifts
PSLLDQ    xmm0, 16       ;  left-shift the bytes in xmm0
PSRLDQ    xmm0, 16       ; right-shift the bytes in xmm0
PSLLW     xmm0, 16       ; left-shift the bits in each word
PSLLD     xmm0, 32       ;           double-word
PSLLQ     xmm0, 64       ;             quad-word
PSRLW/PSRLD/PSRLQ  ; same but right shift

PSUBB/W/D/Q   xmm0, xmm0     ; subtract packed elements, byte/word/dword/qword
PSUBSB/W   xmm0, xmm0     ; sub with signed saturation
PSUBUSB/W  xmm0, xmm0     ; sub with unsigned saturation

PXOR       xmm0, xmm0
XORPD      xmm0, xmm0
XORPS      xmm0, xmm0

VZEROALL

# Can raise an exception on SNaN, so only usable if you know exceptions are masked
CMPLTPD    xmm0, xmm0         # exception on QNaN or SNaN, or denormal
VCMPLT_OQPD xmm0, xmm0,xmm0   # exception only on SNaN or denormal
CMPLT_OQPS ditto

VCMPFALSE_OQPD xmm0, xmm0, xmm0   # This is really just another imm8 predicate value fro the same VCMPPD xmm,xmm,xmm, imm8 instruction.  Same exception behaviour as LT_OQ.

SUBPS xmm0, xmm0 et similaire ne fonctionnera pas parce que NaN-NaN = NAN, pas zéro.

aussi, les instructions FP peuvent soulever des exceptions sur les arguments NaN, donc même CMPPS/PD n'est sûr que si vous savez que les exceptions sont masquées, et que vous ne vous souciez pas de définir éventuellement les bits d'exception dans MXCSR. Même la version AVX, avec son choix élargi de prédicats, soulèvera #IA sur SNaN. Le" calme "prédit seulement supprimer #IA pour QNaN. Le CMPPS / PD peut également élever le Dénormal exception.

(Voir le tableau dans la section l'insn set ref entrée pour CMPPD , ou, de préférence, d'Intel d'origine PDF depuis le code HTML d'extraire mangles la table.)

AVX512:

il y a probablement plusieurs options ici, mais je ne suis pas assez curieux en ce moment d'aller creuser à travers la liste des instructions les recherchant toutes.

il y en a un intéressant qui vaut la peine d'être mentionné, bien que: VPTERNLOGD / Q peut définir un registre à all-ones à la place, avec imm8 = 0xff. (Mais il a une fausse dépendance sur l'ancienne valeur, sur les implémentations actuelles). Puisque les instructions de comparaison toutes comparent dans un masque, VPTERNLOGD semble être la meilleure façon de définir un vecteur à tous les autres sur Skylake-AVX512 dans mes tests, bien que il n'est pas Spécial-cas le cas imm8=0xFF pour éviter une fausse dépendance .

VPTERNLOGD zmm0, zmm0,zmm0, 0     ; inputs can be any registers you like.

x87 FP:

un seul choix (parce que sub ne fonctionne pas si l'ancienne valeur était infinity ou NaN).

FLDZ    ; push +0.0
6
répondu Peter Cordes 2017-11-28 14:39:37

deux autres possibilités:

sub ax, ax

movxz, eax, ah

Edit: je dois noter que le movzx ne Zéro pas tout de eax -- c'est juste zéro ah (plus les 16 premiers bits qui ne sont pas accessibles comme un registre en eux-mêmes).

comme pour être le plus rapide, si la mémoire sert le sub et xor sont équivalents. Ils sont plus rapides que (la plupart) d'autres parce qu'ils sont assez communs que les concepteurs de CPU ont ajouté spécial optimisation pour eux. Plus précisément, avec un sub ou xor normal, le résultat dépend de la valeur précédente dans le registre. Le CPU reconnaît le xor-avec-soi et soustrait-du-soi spécialement pour qu'il sache que la chaîne de dépendances y est brisée. Toutes les instructions qui suivent ne dépendront d'aucune valeur antérieure, de sorte qu'il peut exécuter les instructions précédentes et suivantes en parallèle en utilisant les registres de renommage.

en particulier sur les processeurs plus anciens, nous attendons le " mov reg, 0' à être plus lent, tout simplement parce qu'il a un supplément de 16 bits de données, et la plupart des premiers processeurs (en particulier le 8088) ont été principalement limitée par leur capacité à charger le flux de la mémoire -- en fait, sur un 8088 vous pouvez estimer les temps d'exécution assez correctement avec toutes les feuilles de référence à tous, et il suffit de payer l'attention sur le nombre d'octets impliqués. Cela ne se décomposent pour les instructions div et idiv , mais c'est à peu près tout. OTOH, je devrais probablement me taire, puisque le 8088 est vraiment de peu d'intérêt pour la plupart des gens (depuis au moins une décennie maintenant).

4
répondu Jerry Coffin 2011-01-28 16:06:13

vous pouvez définir register CX à 0 avec LOOP $ .

3
répondu I. J. Kennedy 2011-01-31 17:32:23

bien sûr, les cas spécifiques ont des façons supplémentaires de définir un registre à 0: par exemple, si vous avez eax défini à un entier positif, vous pouvez définir edx à 0 avec un cdq/cltd (cette astuce est utilisée sur un shellcode célèbre de 24 octets, qui apparaît sur"la programmation non sécurisée par exemple").

1
répondu ninjalj 2011-01-28 19:10:07

ce fil est ancien mais quelques autres exemples. Simples:

xor eax,eax

sub eax,eax

and eax,0

lea eax,[0] ; it doesn't look "natural" in the binary

combinaisons plus complexes:

; flip all those 1111... bits to 0000
or  eax,-1  ;  eax = 0FFFFFFFFh
not eax     ; ~eax = 0

; XOR EAX,-1 works the same as NOT EAX instruction in this case, flipping 1 bits to 0
or  eax,-1  ;  eax = 0FFFFFFFFh
xor eax,-1  ; ~eax = 0

; -1 + 1 = 0
or  eax,-1 ;  eax = 0FFFFFFFFh or signed int = -1
not eax    ;++eax = 0
1
répondu Bartosz Wójcik 2015-09-22 13:44:11
mov eax,0  
shl eax,32  
shr eax,32  
imul eax,0 
sub eax,eax 
xor eax,eax   
and eax,0  
andn eax,eax,eax 

loop $ ;ecx only  
pause  ;ecx only (pause="rep nop" or better="rep xchg eax,eax")

;twogether:  
push dword 0    
pop eax

or eax,0xFFFFFFFF  
not eax

xor al,al ;("mov al,0","sub al,al",...)  
movzx eax,al
...
0
répondu ARISTOS 2017-06-13 19:32:54