Yo sé eso movzx
se puede usar para romper la dependencia, pero me topé con algunos movzx
usos de Clang y GCC que realmente no puedo ver para qué sirven. Aquí hay un ejemplo simple que probé en el explorador del compilador Godbolt:
#include <stdint.h>
int add2bytes(uint8_t* a, uint8_t* b) {
return uint8_t(*a + *b);
}
con CCG 12 -O3
:
add2bytes(unsigned char*, unsigned char*):
movzx eax, BYTE PTR [rsi]
add al, BYTE PTR [rdi]
movzx eax, al
ret
Si entiendo bien, la primera movzx
aquí rompe la dependencia de la anterior eax
valor, pero ¿cuál es el segundo movzx
¿haciendo? No creo que haya ninguna dependencia que pueda romper, y tampoco debería afectar el resultado.
con sonido metálico 14 -O3
es aún más raro:
add2bytes(unsigned char*, unsigned char*): # @add2bytes(unsigned char*, unsigned char*)
mov al, byte ptr [rsi]
add al, byte ptr [rdi]
movzx eax, al
ret
Usa mov
dónde movzx
parece más razonable, y entonces cero se extiende al
a eax
pero no sería mucho mejor hacer movzx
¿al principio?
Tengo 2 ejemplos más aquí: https://godbolt.org/z/z45xr4hq1
GCC genera tanto sensato como extraño. movzx
y el uso de Clang de mov r8 m
y movzx
simplemente no tiene sentido para mí. También intenté agregar -march=skylake
para asegurarse de que esta no sea una función para arquitecturas realmente antiguas, pero el ensamblaje generado se ve más o menos igual.
La publicación más cercana que he encontrado es https://stackoverflow.com/a/64915219/14730360 donde mostraron similar movzx
usos que parecen inútiles y/o fuera de lugar.
¿Los compiladores realmente usan movzx
mal aquí, o me estoy perdiendo algo?
Editar: he abierto informes de errores para Clang y GCC:
https://github.com/llvm/llvm-project/issues/56498
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106277
Soluciones temporales mediante ensamblaje en línea:
https://godbolt.org/z/7qob8G3j7
#define addb(a, b) asm (\
"addb %1, %b0"\
: "+r"(a) : "mi"(b))
int add2bytes(uint8_t* a, uint8_t* b) {
int ret = *a;
addb(ret, *b);
return ret;
}
Ahora Clang -O3 produce:
add2bytes(unsigned char*, unsigned char*): # @add2bytes(unsigned char*, unsigned char*)
movzx eax, byte ptr [rdi]
add al, byte ptr [rsi]
ret
Quizás relacionado: stackoverflow.com/questions/43491737/…
– Jakob Stark
12 de julio a las 8:37
¿A qué llamas “romper la dependencia”?
– Yves Daoust
12 de julio a las 8:38
@YvesDaoust stackoverflow.com/a/43910889/14730360 tiene un ejemplo de cómo movzx rompe la dependencia y ayuda a la ejecución de OoO. Sin embargo, no creo que movzx haga eso aquí.
– Hintro
12 de julio a las 8:43
@YvesDaoust: Las CPU x86 modernas tienen muchos más registros de los que tienen nombres. Es por eso que los registros físicos están constantemente siendo renombrado. Este es un proceso complicado. Una preocupación importante es que debería ser invisible para el código. Esto es importante cuando se usan nombres de registros parciales como
al
. Que debería preservar los bits superiores, deteniendo un cambio de nombre. Peromovzx
hace una extensión cero, por lo que permite un cambio de nombre.– MSalters
12 de julio a las 8:45
Actualicé mi respuesta con una sección sobre su enlace Godbolt
a[a[i] | a[j]]
mostrando clang esperando que la persona que llama ya se haya extendidoi
yj
a 32 bits.– Peter Cordes
13 de julio a las 4:42