rex online linea ensamblador compilador gcc assembly x86 gas

gcc - linea - compilador de ensamblador online



Ensamblador en línea GCC, mezcla de tamaños de registro(x86) (5)

¿Alguien sabe cómo puedo deshacerme de la siguiente advertencia de ensamblador?

El código es x86, 32 bit:

int test (int x) { int y; // do a bit-rotate by 8 on the lower word. leave upper word intact. asm ("rorw $8, %0/n/t": "=q"(y) :"0"(x)); return y; }

Si lo compilo recibo la siguiente advertencia (muy válida):

Warning: using `%ax'' instead of `%eax'' due to `w'' suffix

Lo que estoy buscando es una forma de decirle al compilador / ensamblador que quiero acceder al sub-registro inferior de 16 bits de% 0. También sería bueno tener acceso a los sub-registros de bytes (en este caso AL y AH).

Ya he elegido el modificador "q", por lo que el compilador se ve obligado a usar EAX, EBX, ECX o EDX. Me he asegurado de que el compilador tenga que elegir un registro que tenga sub-registros.

Sé que puedo forzar que el código asm use un registro específico (y sus sub-registros), pero quiero dejar el trabajo de asignación de registros al compilador.


Entonces, aparentemente hay trucos para hacer esto ... pero puede que no sea tan eficiente. Los procesadores x86 de 32 bits generalmente son lentos en la manipulación de datos de 16 bits en registros de propósito general. Deberías compararlo si el rendimiento es importante.

A menos que (a) el rendimiento sea crítico y (b) demuestre ser mucho más rápido, me ahorraría algunos problemas de mantenimiento y simplemente lo haré en C:

uint32_t y, hi=(x&~0xffff), lo=(x&0xffff); y = hi + (((lo >> 8) + (lo << 8))&0xffff);

Con GCC 4.2 y -O2 esto se optimiza en hasta seis instrucciones ...


Mientras estoy pensando en eso ... debes reemplazar la restricción "q" con una restricción mayúscula "Q" en la segunda solución de Chris:

int test(int x) { int y; asm ("xchg %b0, %h0" : "=Q" (y) : "0" (x)); return y; }

"q" y "Q" son ligeramente diferentes en el modo de 64 bits, donde puede obtener el byte más bajo para todos los registros enteros (ax, bx, cx, dx, si, di, sp, bp, r8-r15) . Pero solo puede obtener el segundo byte más bajo (por ejemplo, ah) para los cuatro registros 386 originales (ax, bx, cx, dx).


Puedes usar %w0 si recuerdo bien. Yo también lo probé. :-)

int test(int x) { int y; asm ("rorw $8, %w0" : "=q" (y) : "0" (x)); return y; }

Editar: en respuesta al OP, sí, también puedes hacer lo siguiente:

int test(int x) { int y; asm ("xchg %b0, %h0" : "=Q" (y) : "0" (x)); return y; }

Actualmente, el único lugar (que yo sepa) en el que está documentado es gcc/config/i386/i386.md , no en ninguna de la documentación estándar.


Hace mucho tiempo, pero probablemente necesite esto para mi futura referencia ...

Añadiendo a la respuesta precisa de Chris, la tecla está usando un modificador entre el ''%'' y el número del operando de salida. Por ejemplo, "MOV %1, %0" podría convertirse en "MOV %q1, %w0" .

No pude encontrar nada en constraints.md, pero /gcc/config/i386/i386.c tenía este comentario potencialmente útil en la fuente de print_reg() :

/* Print the name of register X to FILE based on its machine mode and number. If CODE is ''w'', pretend the mode is HImode. If CODE is ''b'', pretend the mode is QImode. If CODE is ''k'', pretend the mode is SImode. If CODE is ''q'', pretend the mode is DImode. If CODE is ''x'', pretend the mode is V4SFmode. If CODE is ''t'', pretend the mode is V8SFmode. If CODE is ''h'', pretend the reg is the ''high'' byte register. If CODE is ''y'', print "st(0)" instead of "st", if the reg is stack op. If CODE is ''d'', duplicate the operand for AVX instruction. */

Un comentario a continuación para ix86_print_operand() ofrece un ejemplo:

b - imprime el nombre del modo QI del registro para el operando indicado.

% b0 imprimiría% al si operandos [0] es reg 0.

Algunas otras opciones útiles se enumeran en la Plantilla de salida de la documentación de GCC Internals :

''% cdigit'' se puede usar para sustituir un operando que es un valor constante sin la sintaxis que normalmente indica un operando inmediato.

''% ndigit'' es como ''% cdigit'', excepto que el valor de la constante se anula antes de imprimir.

''% adigit'' se puede usar para sustituir un operando como si fuera una referencia de memoria, con el operando real tratado como la dirección. Esto puede ser útil al generar una instrucción de "carga de dirección", porque a menudo la sintaxis del ensamblador para tal instrucción requiere que se escriba el operando como si fuera una referencia de memoria.

''% ldigit'' se usa para sustituir un label_ref en una instrucción de salto.

''% ='' genera un número que es exclusivo para cada instrucción en toda la compilación. Esto es útil para hacer referencia a las etiquetas locales más de una vez en una sola plantilla que genera múltiples instrucciones de ensamblador.

El constructo '' %c2 '' permite formatear correctamente una instrucción LEA usando un desplazamiento:

#define ASM_LEA_ADD_BYTES(ptr, bytes) / __asm volatile("lea %c1(%0), %0" : / /* reads/writes %0 */ "+r" (ptr) : / /* reads */ "i" (bytes));

Tenga en cuenta la ''c'' crucial pero escasamente documentada en '' %c1 ''. Esta macro es equivalente a

ptr = (char *)ptr + bytes

pero sin hacer uso de los puertos de ejecución aritméticos enteros usuales.

Editar para agregar:

Hacer llamadas directas en x64 puede ser difícil, ya que requiere otro modificador no documentado: '' %P0 '' (que parece ser para PIC)

#define ASM_CALL_FUNC(func) / __asm volatile("call %P0") : / /* no writes */ : / /* reads %0 */ "i" (func))

Un modificador ''p'' en minúscula también parece funcionar igual en GCC, aunque solo la ''P'' mayúscula es reconocida por ICC. Más detalles están probablemente disponibles en /gcc/config/i386/i386.c . Busque "''p''".


Gotcha. Bueno, si es una rutina primitiva que vas a reutilizar una y otra vez, no tengo ningún argumento con eso ... el truco de nombres de registro que Chris señaló es bueno que voy a tener que recordar.

¡Sería bueno si también se convirtiera en los documentos estándar de GCC!