significado procesador mexico assembly x86 sse simd

assembly - procesador - Cómo mover inmediatos de 128 bits a registros XMM



sse wikipedia (5)

Como una de las 10000 formas de hacerlo, use SSE4.1 pinsrq

mov rax, first half movq xmm0, rax ; better than pinsrq xmm0,rax,0 for performance and code-size mov rax, second half pinsrq xmm0, rax, 1

Ya hay una pregunta sobre esto, pero fue cerrada como "ambigua", así que estoy abriendo una nueva. He encontrado la respuesta, tal vez también ayude a otros.

La pregunta es: ¿cómo se escribe una secuencia de código de ensamblaje para inicializar un registro XMM con un valor inmediato (constante) de 128 bits?


Hay varias formas de incrustar constantes en el flujo de instrucciones:

  1. utilizando operandos inmediatos
  2. cargando desde direcciones relativas a PC

Entonces, si bien no hay manera de realizar una carga inmediata en un registro XMM , es posible realizar una carga relativa a la PC (en 64 bits) desde un valor almacenado "justo al lado" hasta donde se ejecuta el código. Eso crea algo como:

.align 4 .val: .long 0x12345678 .long 0x9abcdef0 .long 0xfedbca98 .long 0x76543210 func: movdqa .val(%rip), %xmm0

Cuando desmonte:

0000000000000000 : 0: 78 56 34 12 f0 de bc 9a 8: 98 ca db fe 10 32 54 76 0000000000000010 : 10: 66 0f 6f 05 e8 ff ff movdqa -0x18(%rip),%xmm0 # 0

que es totalmente compacto , de 23 bytes.

Otras opciones son construir el valor en la pila y volver a cargarlo desde allí. En 32bit x86, donde no tiene %rip acceso relativo a la memoria, todavía se puede hacer eso en 24 bytes (asumiendo que el apilador está alineado en la entrada; de lo contrario, se requiere una carga no alineada):

00000000 : 0: 68 78 56 34 12 push $0x12345678 5: 68 f0 de bc 9a push $0x9abcdef0 a: 68 98 ca db fe push $0xfedbca98 f: 68 10 32 54 76 push $0x76543210 14: 66 0f 6f 04 24 movdqa (%esp),%xmm0

Mientras que en 64 bits (la alineación del punto de pila en la entrada de la función está garantizada por el ABI), tomaría 27 bytes:

0000000000000000 : 0: 48 b8 f0 de bc 9a 78 56 34 12 movabs $0x123456789abcdef0,%rax a: 50 push %rax b: 48 b8 10 32 54 76 98 ba dc fe movabs $0xfedcba9876543210,%rax 15: 50 push %rax 16: 66 0f 6f 04 24 movdqa (%rsp),%xmm0

Si compara alguno de estos con la versión MOVLHPS , notará que es el más largo:

0000000000000000 : 0: 48 b8 f0 de bc 9a 78 56 34 12 movabs $0x123456789abcdef0,%rax a: 66 48 0f 6e c0 movq %rax,%xmm0 f: 48 b8 10 32 54 76 98 ba dc fe movabs $0xfedcba9876543210,%rax 19: 66 48 0f 6e c8 movq %rax,%xmm1 1e: 0f 16 c1 movlhps %xmm1,%xmm0

a los 33 bytes.

La otra ventaja de cargar directamente desde la memoria de instrucciones es que el movdqa no depende de nada anterior. Lo más probable es que la primera versión, dada por @Paul R, sea la más rápida que pueda obtener.


La mejor solución (especialmente si desea mantener el SSE2, es decir, evitar el uso de AVX) para inicializar dos registros (por ejemplo, xmm0 y xmm1) con las dos mitades de 64 bits de su valor inmediato, haga MOVLHPS xmm0, xmm1 para: inicialice un valor de 64 bits, la solución más sencilla es usar un registro de propósito general (por ejemplo, AX) y luego usar MOVQ para transferir su valor al registro XMM. Entonces la secuencia sería algo como esto:

MOV RAX, <first_half> MOVQ XMM0, RAX MOV RAX, <second_half> MOVQ XMM1, RAX MOVLHPS XMM0,XMM1



Puedes hacerlo así, con solo una instrucción de movaps :

.section .rodata # put your constants in the read-only data section .p2align 4 # align to 16 = 1<<4 LC0: .long 1082130432 .long 1077936128 .long 1073741824 .long 1065353216 .text foo: movaps LC0(%rip), %xmm0

Por lo general, es preferible cargarlo con una carga de datos que incrustarlo en el flujo de instrucciones, especialmente debido a la cantidad de instrucciones que toma. Eso es varios uops adicionales para que la CPU los ejecute, para una constante arbitraria que no puede ser generada por todos con un par de turnos.

Si es más fácil, puede poner constantes justo antes o después de una función que compiló, en lugar de en una sección separada. Pero como las CPU han dividido los cachés L1d / L1i y los TLB, generalmente es mejor agrupar las constantes por separado de las instrucciones.

Si ambas mitades de su constante son las mismas, puede transmitirlas con SSE3.
movddup (m64), %xmm0 .