con - Cómo escribo el siguiente código de ensamblaje en línea en C usando GCC
gcc compilador (4)
Estaba leyendo algunas respuestas y preguntas aquí y continué con esta sugerencia, pero me di cuenta de que nadie realmente explicó "exactamente" lo que debe hacer para hacerlo, en Windows utilizando el compilador Intel y GCC. Comentada a continuación es exactamente lo que estoy tratando de hacer.
#include <stdio.h>
int main()
{
int x = 1;
int y = 2;
//assembly code begin
/*
push x into stack; < Need Help
x=y; < With This
pop stack into y; < Please
*/
//assembly code end
printf("x=%d,y=%d",x,y);
getchar();
return 0;
}
Puede usar el ensamblaje extendido en línea . Es una característica del compilador que le permite escribir instrucciones de ensamblaje dentro de su código C. Una buena referencia para el ensamblaje gcc en línea está disponible aquí .
El siguiente código copia el valor de x
en y
usando pop
instrucciones pop
y push
.
(compilado y probado usando gcc en x86_64)
#include <stdio.h>
int main()
{
int x = 1;
int y = 2;
asm volatile (
"pushq %%rax/n" /* Push x into the stack */
"movq %%rbx, %%rax/n" /* Copy y into x */
"popq %%rbx/n" /* Pop x into y */
: "=b"(y), "=a"(x) /* OUTPUT values */
: "a"(x), "b"(y) /* INPUT values */
: /*No need for the clobber list, since the compiler knows
which registers have been modified */
);
printf("x=%d,y=%d",x,y);
getchar();
return 0;
}
Resultado x=2 y=1
, como esperabas.
El compilador de inteligencia funciona de manera similar, creo que solo tiene que cambiar la palabra clave asm
por __asm__
. Puede encontrar información sobre el ensamblado en línea para el compilador INTEL aquí .
#include <stdio.h>
int main()
{
int x=1;
int y=2;
printf("x::%d,y::%d/n",x,y);
__asm__( "movl %1, %%eax;"
"movl %%eax, %0;"
:"=r"(y)
:"r"(x)
:"%eax"
);
printf("x::%d,y::%d/n",x,y);
return 0;
}
/* Load x to eax
Load eax to y */
Si desea intercambiar los valores, también se puede hacer de esta manera. Tenga en cuenta que esto instruye a GCC para que se ocupe del registro EAX clobbered. Para fines educativos, está bien, pero me parece más adecuado dejar micro-optimizaciones para el compilador.
Deje que el compilador elija los registros, usando el prefijo k
para denotar un registro de 32 bits para el tipo int
(para que funcione como se espera en x86-64):
__asm__ ("pushl %k0/n/t"
"movl %k1, %k0/n/t"
"popl %k1"
: "+r" (x), "+r" (y));
No es necesario (y realmente incorrecto) especificar cualquier operando bloqueado en este caso.
También es aparente que x
, y
son conmutativos aquí, es decir, al intercambiar los operandos todavía se debe obtener el mismo resultado usando : "+%r" (x), "+r" (y)
donde %
indica que este operando y el siguiente operando puede conmutar.
No se puede simplemente presionar / soltar con seguridad desde el asm en línea , si va a ser portátil para los sistemas con una zona roja. Eso incluye todas las plataformas que no sean de Windows x86-64. ( No hay forma de decirle a gcc que quieres castigarlo ). Bueno, podrías add rsp, -128
primero para saltear la zona roja antes de presionar / hacer estallar cualquier cosa, luego restaurarla más tarde. Pero entonces no puede usar restricciones "m"
porque el compilador puede usar direccionamiento relativo RSP con compensaciones que asumen que RSP no se ha modificado.
Pero realmente esto es algo ridículo de hacer en línea asm.
Así es como se usa inline-asm para intercambiar dos variables C:
#include <stdio.h>
int main()
{
int x = 1;
int y = 2;
asm("" // no actual instructions.
: "=r"(y), "=r"(x) // request both outputs in the compiler''s choice of register
: "0"(x), "1"(y) // matching constraints: request each input in the same register as the other output
);
// apparently "=m" doesn''t compile: you can''t use a matching constraint on a memory operand
printf("x=%d,y=%d/n",x,y);
// getchar(); // Set up your terminal not to close after the program exits if you want similar behaviour: don''t embed it into your programs
return 0;
}
Salida gcc -O3 (dirigida al sistema x86-64 System ABI, no a Windows) del explorador del compilador Godbolt :
.section .rodata
.LC0:
.string "x=%d,y=%d"
.section .text
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
xor eax, eax
mov edx, 1
mov esi, 2
#APP
# 8 "/tmp/gcc-explorer-compiler116814-16347-5i3lz1/example.cpp" 1
# I used "/n" instead of just "" so we could see exactly where our inline-asm code ended up.
# 0 "" 2
#NO_APP
call printf
xor eax, eax
add rsp, 8
ret
Las variables C son un concepto de alto nivel; no cuesta nada decidir que los mismos registros ahora contienen lógicamente diferentes variables con nombre, en lugar de intercambiar el contenido del registro sin cambiar el mapeo varname-> register.
Cuando escriba asm a mano, use los comentarios para realizar un seguimiento del significado lógico actual de diferentes registros o partes de un registro vectorial.
El asma en línea tampoco dio lugar a ninguna instrucción extra fuera del bloque de asterisco en línea, por lo que es perfectamente eficiente en este caso. Aún así, el compilador no puede ver a través de él, y no sabe que los valores todavía son 1 y 2, por lo que una propagación constante adicional sería derrotada. https://gcc.gnu.org/wiki/DontUseInlineAsm