Pregunta

Nunca pensé que publicaría una pregunta sobre la asamblea.:-)

En CCG, hay un versión extendida de la función asm.Esta función puede tomar cuatro parámetros:código ensamblador, lista de salida, lista de entrada y lista de sobrescritura.

Mi pregunta es, ¿Están los registros en la lista de sobrescritura puestos a cero??¿Qué sucede con los valores que estaban previamente allí (de otro código en ejecución)?

Actualizar:Al considerar mis respuestas hasta ahora (¡gracias!), quiero agregar que, aunque un registro aparece en la lista de clobber, (en mi caso) se está usando en un pop (popl) dominio.No hay otra referencia.

¿Fue útil?

Solución

Si por "poner a cero" quiere decir "los valores en los registros se reemplazan con 0 para evitar que sepa qué estaba haciendo alguna otra función", entonces no, los registros no se ponen a cero antes de su uso.Pero no debería importar porque le estás diciendo a GCC que planeas almacenar información allí, no es que quieras leer información que está ahí actualmente.

Le proporciona esta información a GCC para que (al leer la documentación) "no necesite adivinar qué registros o ubicaciones de memoria contendrán los datos que desea usar" cuando haya terminado con el código ensamblador (por ejemplo, no tiene recordar si los datos estarán en el registro de la pila o en algún otro registro).

GCC necesita mucha ayuda para el código ensamblador porque "El compilador...no analiza la plantilla de instrucciones del ensamblador y no sabe lo que significa o incluso si es una entrada válida del ensamblador.La función asm extendida se usa con mayor frecuencia para instrucciones de máquina que el propio compilador no sabe que existen".

Actualizar

GCC está diseñado como un compilador de múltiples pasadas.Muchos de los pases son, de hecho, programas completamente diferentes.Un conjunto de programas que forman "el compilador" traducen su código fuente de C, C++, Ada, Java, etc.en código ensamblador.Luego un programa separado (gas, para GNU Assembler) toma ese código ensamblador y lo convierte en un binario (y luego ld y collect2 hacer más cosas al binario).Los bloques de ensamblaje existen para pasar texto directamente a gas, y la lista clobber (y la lista de entrada) existen para que el compilador pueda hacer cualquier configuración necesaria para pasar información entre C, C++, Ada, Java, etc.lado de las cosas y el gas lado de las cosas, y para garantizar que cualquier información importante actualmente en los registros pueda protegerse del bloque de ensamblaje copiándola en la memoria antes de que se ejecute el bloque de ensamblaje (y copiándola nuevamente desde la memoria después).

La alternativa sería guardar y restaurar cada registro para cada bloque de código ensamblador.En una máquina RISC con una gran cantidad de registros, eso podría resultar costoso (el Itanium tiene 128 registros generales, otros 128 registros de coma flotante y 64 registros de 1 bit, por ejemplo).

Ha pasado un tiempo desde que escribí código ensamblador.Y tengo mucha más experiencia usando la función de registros con nombre de GCC que haciendo cosas con registros específicos.Entonces, mirando un ejemplo:

#include <stdio.h>

long foo(long l)
{
    long result;
    asm (
        "movl %[l], %[reg];"
        "incl %[reg];"
        : [reg] "=r" (result)
        : [l] "r" (l)
    );
    return result;
}

int main(int argc, char** argv)
{
    printf("%ld\n", foo(5L));
}

He solicitado un registro de salida, al que llamaré reg dentro del código ensamblador, y que GCC copiará automáticamente al result variable al finalizar.No es necesario darle a esta variable nombres diferentes en código C o en código ensamblador;Sólo lo hice para demostrar que es posible.Cualquiera que sea el registro físico que GCC decida utilizar, ya sea %%eax, %%ebx, %%ecx, etc.-- GCC se encargará de copiar cualquier dato importante de ese registro en la memoria cuando ingrese al bloque de ensamblaje para que tenga uso completo de ese registro hasta el final del bloque de ensamblaje.

También he solicitado un registro de entrada, al que llamaré l tanto en C como en ensamblaje.GCC promete que cualquier registro físico que decida darme tendrá el valor actual en la variable C l cuando entro al bloque de montaje.GCC también mantendrá los registros necesarios para proteger cualquier dato que se encuentre en ese registro antes de que yo ingrese al bloque de ensamblaje.

¿Qué pasa si agrego una línea al código ensamblador?Decir:

"addl %[reg], %%ecx;"

Dado que la parte compiladora de GCC no verifica el código ensamblador, no habrá protegido los datos en %%ecx.Si tengo suerte, %%ecx puede ser uno de los registros que GCC decidió utilizar para %[reg] o %[l].Si no tengo suerte, habré cambiado "misteriosamente" un valor en alguna otra parte de mi programa.

Otros consejos

No, no están puestos a cero.El propósito de la lista de sobrescritura (más comúnmente llamada lista de golpes) es para informar a GCC que, como resultado de las instrucciones asm, los registros enumerados en la lista clobber se modificarán y, por lo tanto, el compilador debe preservar los que estén activos actualmente.

Por ejemplo, en x86 el cpuid La instrucción devuelve información en cuatro partes utilizando cuatro registros fijos: %eax, %ebx, %ecx y %edx, basado en el valor de entrada de %eax.Si sólo estuviéramos interesados ​​en el resultado de %eax y %ebx, entonces podríamos (ingenuamente) escribir:

int input_res1 = 0; // also used for first part of result 
int res2;
__asm__("cpuid" : "+a"(input_res1), "=b"(res2) ); 

Esto obtendría la primera y segunda parte del resultado en variables C. input_res1 y res2;sin embargo si GCC estaba usando %ecx y %edx conservar otros datos;serían sobrescritos por el cpuid instrucción sin que gcc lo sepa.Para prevenir esto;usamos el lista de golpes:

int input_res1 = 0; // also used for first part of result 
int res2;
__asm__("cpuid" : "+a"(input_res1), "=b"(res2)
                : : "%ecx", "%edx" );

Como le hemos dicho a GCC que %ecx y %edx será sobrescrito por esta llamada asm, puede manejar la situación correctamente, ya sea no usando %ecx o %edx, o guardando sus valores en la pila antes del asm función y restauración posterior.

Actualizar:

Con respecto a su segunda pregunta (¿por qué ve un registro en la lista de clobber para un popl instrucción) - asumiendo su asm se parece a:

__asm__("popl %eax" : : : "%eax" );

Entonces, el código aquí está sacando un elemento de la pila, sin embargo, no le importa el valor real; probablemente solo esté manteniendo la pila equilibrada o el valor no sea necesario en esta ruta de código.Escribiendo de esta manera, en lugar de:

int trash // don't ever use this.
__asm__("popl %0" : "=r"(trash));

No es necesario crear explícitamente una variable temporal para contener el valor no deseado.Es cierto que en este caso no hay una gran diferencia entre los dos, pero la versión con el clobber deja claro que no te importa el valor de la pila.

Sospecho que la lista de sobreescritura es sólo para dar un toque GCC no almacenar cualquier cosa de valor en estos registros a través de la llamada ASM; desde GCC no analiza lo que está dando ASM, y ciertas instrucciones tener efectos secundarios que tocan otros registros que no se citan explícitamente en el código, esta es la forma de saber GCC al respecto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top