Bienvenido a nuestro sitio web, aquí vas a encontrar la respuesta que buscabas.
Solución:
Hay muchas posibilidades de cómo mover 0 en ax bajo 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
Y quizás el más extraño ... 🙂
@movzx:
movzx eax, byte ptr[@movzx + 6] //Because the last byte of this instruction is 0
y...
@movzx:
movzx ax, byte ptr[@movzx + 7]
Editar:
Y para el modo de cpu x86 de 16 bits, no probado ...:
lea ax, [0]
y...
@movzx:
movzx ax, byte ptr cs:[@movzx + 7] //Check if 7 is right offset
los cs: prefix es opcional en caso de que el ds El registro de segmento no es igual al registro de segmento cs.
Vea esta respuesta para mejor camino a cero registros: xor eax,eax
(ventajas de rendimiento y codificación más pequeña).
Consideraré solo las formas en que una sola instrucción puede poner a cero un registro. Hay demasiadas formas si permite cargar un cero desde la memoria, por lo que en su mayoría excluiremos las instrucciones que se cargan desde la memoria.
Encontré 10 instrucciones únicas diferentes que ponen a cero un registro de 32 bits (y, por lo tanto, el registro completo de 64 bits en modo largo), sin condiciones previas ni cargas de ninguna otra memoria. Esto sin contar las diferentes codificaciones del mismo insn, o las diferentes formas de mov
. Si cuenta la carga desde la memoria que se sabe que tiene un cero, o desde registros de segmento o lo que sea, hay un montón de formas. También hay un trillón de formas de cero registros vectoriales.
Para la mayoría de estos, las versiones eax y rax son codificaciones separadas para la misma funcionalidad, ambas poniendo a cero los registros completos de 64 bits, ya sea poniendo a cero la mitad superior implícitamente o escribiendo explícitamente el registro completo con un REX.W prefix.
Registros enteros:
# 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
"Desplazar todos los bits en un extremo" no es posible para registros GP de tamaño normal, solo registros parciales. shl
y shr
Los recuentos de turnos están enmascarados (en 286 y posteriores): count & 31;
es decir, mod 32.
(Los cambios de recuento inmediato eran nuevos en 186 (anteriormente solo CL e implícito-1), por lo que hay CPU con cambios inmediatos sin máscara (que también incluyen NEC V30). Además, 286 y anteriores son solo de 16 bits, por lo que ax
es un registro "completo". Hubo CPU en las que un desplazamiento puede poner a cero un registro entero completo).
También tenga en cuenta que los recuentos de desplazamiento de los vectores se saturan en lugar de ajustarse.
# 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
Dependiendo de otras condiciones existentes (además de tener un cero en otro registro):
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
reglas de vector:
Algunas de estas instrucciones enteras SSE2 también se pueden utilizar en registros MMX (mm0
- mm7
). No voy a mostrar eso por separado.
Una vez más, la mejor opción es alguna forma de xor. Cualquiera PXOR
/ VPXOR
, o XORPS
/ VXORPS
. Consulte ¿Cuál es la mejor manera de establecer un registro en cero en el ensamblaje x86: xor, mov o and? para detalles.
AVX vxorps xmm0,xmm0,xmm0
pone a cero el ymm0 / zmm0 completo, y es mejor que vxorps ymm0,ymm0,ymm0
en las CPU AMD.
Estas instrucciones de puesta a cero tienen tres codificaciones cada una: SSE heredado, AVX (VEX prefix) y AVX512 (EVEX prefix), aunque la versión SSE solo pone a cero los 128 inferiores, que no es el registro completo en las CPU que admiten AVX o AVX512. De todos modos, dependiendo de cómo cuente, cada entrada puede ser de tres instrucciones diferentes (aunque el mismo código de operación, solo diferentes prefijos). Excepto vzeroall
, que AVX512 no cambió (y no pone a cero zmm16-31).
PXOR xmm0, xmm0 ;; recommended
XORPS xmm0, xmm0 ;; or this
XORPD xmm0, xmm0 ;; longer encoding for zero benefit
PXOR mm0, mm0 ;; MMX, not show for the rest of the integer insns
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
;; SSE4.1
INSERTPS xmm0, xmm1, 0x0F ; imm[3:0] = zmask = all elements zeroed.
DPPS xmm0, xmm1, 0x00 ; imm[7:4] => inputs = treat as zero -> no FP exceptions. imm[3:0] => outputs = 0 as well, for good measure
DPPD xmm0, xmm1, 0x00 ; inputs = all zeroed -> no FP exceptions. outputs = 0
VZEROALL ; AVX1 x/y/zmm0..15 not zmm16..31
VPERM2I/F128 ymm0, ymm1, ymm2, 0x88 ; imm[3] and [7] zero that output lane
# 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 for the same VCMPPD xmm,xmm,xmm, imm8 instruction. Same exception behaviour as LT_OQ.
SUBPS xmm0, xmm0
y similares no funcionarán porque NaN-NaN = NaN, no cero.
Además, las instrucciones FP pueden generar excepciones en argumentos NaN, por lo que incluso CMPPS / PD solo es seguro si sabe que las excepciones están enmascaradas y no le importa establecer los bits de excepción en MXCSR. Incluso la versión AVX, con su amplia variedad de predicados, aumentará #IA
en SNaN. Los predicados "silenciosos" solo suprimen #IA
para QNaN. CMPPS / PD también puede generar la excepción Denormal. (Los Las codificaciones AVX512 EVEX pueden suprimir las excepciones de FP para vectores de 512 bits, además de anular el modo de redondeo)
(Consulte la tabla en la entrada insn set ref para CMPPD, o preferiblemente en el PDF original de Intel, ya que el extracto HTML destruye esa tabla).
AVX1 / 2 y AVX512 EVEX de las formas anteriores, solo para PXOR: todas ponen a cero el destino ZMM completo. PXOR tiene dos versiones EVEX: VPXORD o VPXORQ, lo que permite el enmascaramiento con elementos dword o qword. (XORPS / PD ya distingue el tamaño del elemento en el mnemónico, por lo que AVX512 no cambió eso. En la codificación SSE heredada, XORPD siempre es un desperdicio inútil de tamaño de código (código de operación más grande) frente a XORPS en todas las CPU).
VPXOR xmm15, xmm0, xmm0 ; AVX1 VEX
VPXOR ymm15, ymm0, ymm0 ; AVX2 VEX, less efficient on some CPUs
VPXORD xmm31, xmm0, xmm0 ; AVX512VL EVEX
VPXORD ymm31, ymm0, ymm0 ; AVX512VL EVEX 256-bit
VPXORD zmm31, zmm0, zmm0 ; AVX512F EVEX 512-bit
VPXORQ xmm31, xmm0, xmm0 ; AVX512VL EVEX
VPXORQ ymm31, ymm0, ymm0 ; AVX512VL EVEX 256-bit
VPXORQ zmm31, zmm0, zmm0 ; AVX512F EVEX 512-bit
Los diferentes anchos de vector se enumeran con entradas separadas en la entrada manual PXOR de Intel.
usted pueden utilice el enmascaramiento cero (pero no el enmascaramiento combinado) con cualquier registro de máscara que desee; no importa si obtiene un cero del enmascaramiento o un cero de la salida normal de la instrucción vectorial. Pero esa no es una instrucción diferente. p.ej: VPXORD xmm16k1z, xmm0, xmm0
AVX512:
Probablemente haya varias opciones aquí, pero no tengo la curiosidad suficiente en este momento para buscar en la lista de conjuntos de instrucciones en busca de todas.
Sin embargo, hay uno interesante que vale la pena mencionar: VPTERNLOGD / Q puede configurar un registro para todos-uno en su lugar, con imm8 = 0xFF. (Pero tiene un false dependencia del valor anterior, de las implementaciones actuales). Dado que todas las instrucciones de comparación se comparan en una máscara, VPTERNLOGD parece ser la mejor manera de establecer un vector para todos en Skylake-AVX512 en mis pruebas, aunque no es un caso especial, el caso imm8 = 0xFF para evitar un false dependencia.
VPTERNLOGD zmm0, zmm0,zmm0, 0 ; inputs can be any registers you like.
Puesta a cero del registro de máscara (k0..k7): Instrucciones de máscara y comparación de vectores en máscara
kxorB/W/D/Q k0, k0, k0 ; narrow versions zero extend to max_kl
kshiftlB/W/D/Q k0, k0, 100 ; kshifts don't mask/wrap the 8-bit count
kshiftrB/W/D/Q k0, k0, 100
kandnB/W/D/Q k0, k0, k0 ; x & ~x
; compare into mask
vpcmpB/W/D/Q k0, x/y/zmm0, x/y/zmm0, 3 ; predicate #3 = always false; other predicates are false on equal as well
vpcmpuB/W/D/Q k0, x/y/zmm0, x/y/zmm0, 3 ; unsigned version
vptestnmB/W/D/Q k0, x/y/zmm0, x/y/zmm0 ; x & ~x test into mask
x87 FP:
Solo una opción (porque sub no funciona si el valor anterior era infinito o NaN).
FLDZ ; push +0.0
Un par de posibilidades más:
sub ax, ax
movxz, eax, ah
Editar: Debo señalar que el movzx
no pone a cero todo eax
- es solo cero ah
(más los 16 bits principales que no son accesibles como un registro en sí mismos).
En cuanto a ser el más rápido, si la memoria le sirve a la sub
y xor
son equivalentes. Son más rápidos que (la mayoría) de los demás porque son lo suficientemente comunes como para que los diseñadores de CPU les hayan agregado una optimización especial. Específicamente, con un normal sub
o xor
el resultado depende del valor anterior en el registro. La CPU reconoce el xor-con-uno mismo y el resta-de-uno mismo especialmente para saber que la cadena de dependencia está rota allí. Las instrucciones posteriores no dependerán de ningún valor anterior, por lo que puede ejecutar instrucciones anteriores y posteriores en paralelo utilizando registros de cambio de nombre.
Especialmente en los procesadores más antiguos, esperamos que 'mov reg, 0' sea más lento simplemente porque tiene 16 bits adicionales de datos, y la mayoría de los primeros procesadores (especialmente el 8088) estaban limitados principalmente por su capacidad para cargar el flujo desde la memoria. - de hecho, en un 8088 puede estimar el tiempo de ejecución con bastante precisión con cualquier hoja de referencia, y simplemente prestar atención a la cantidad de bytes involucrados. Eso se rompe por el div
y idiv
instrucciones, pero eso es todo. OTOH, probablemente debería callarme, ya que el 8088 realmente le interesa poco a nadie (desde hace al menos una década).
Si haces scroll puedes encontrar las anotaciones de otros sys admins, tú incluso tienes el poder insertar el tuyo si te apetece.