code assembler asm c gcc inline-assembly

asm - use assembler in c



¿Qué hace__asm____volatile__ en C? (3)

Miré en algún código C de

http://www.mcs.anl.gov/~kazutomo/rdtsc.html

Usan cosas como "en línea ", " asm ", etc. como las siguientes:

código1:

static __inline__ tick gettick (void) { unsigned a, d; __asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) ); return (((tick)a) | (((tick)d) << 32)); }

code2:

volatile int __attribute__((noinline)) foo2 (int a0, int a1) { __asm__ __volatile__ (""); }

Me preguntaba qué hacen el código1 y el código2.


El atributo __asm__ especifica el nombre que se utilizará en el código de ensamblador para la función o variable.

El calificador __volatile__ , generalmente utilizado en la computación en tiempo real de sistemas integrados, soluciona un problema con las pruebas de compilación del status register para el bit ERROR o READY causa problemas durante la optimización. __volatile__ se introdujo como una forma de decirle al compilador que el objeto está sujeto a cambios rápidos y para obligar a que cada referencia del objeto sea una referencia genuina.


El modificador __volatile__ en un bloque __asm__ obliga al optimizador del compilador a ejecutar el código tal como está. Sin él, el optimizador puede pensar que puede eliminarse directamente o sacarse de un bucle y almacenarse en caché.

Esto es útil para la instrucción rdtsc así:

__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) )

Esto no requiere dependencias, por lo que el compilador podría suponer que el valor puede almacenarse en caché. Volatile se utiliza para forzarlo a leer una nueva marca de tiempo.

Cuando se usa solo, así:

__asm__ __volatile__ ("")

En realidad, no ejecutará nada. Sin embargo, puede ampliar esto para obtener una barrera de memoria en tiempo de compilación que no permita reordenar las instrucciones de acceso a la memoria:

__asm__ __volatile__ ("":::"memory")

La instrucción rdtsc es un buen ejemplo de volátil. rdtsc se usa generalmente cuando necesita rdtsc tiempo que tardan algunas instrucciones en ejecutarse. Imagina un código como este, donde quieres r2 la ejecución de r1 y r2 :

__asm__ ("rdtsc": "=a" (a0), "=d" (d0) ) r1 = x1 + y1; __asm__ ("rdtsc": "=a" (a1), "=d" (d1) ) r2 = x2 + y2; __asm__ ("rdtsc": "=a" (a2), "=d" (d2) )

Aquí, el compilador puede almacenar en caché la marca de tiempo, y una salida válida podría mostrar que cada línea tomó exactamente 0 relojes para ejecutarse. Obviamente, esto no es lo que quieres, por lo que debes introducir __volatile__ para evitar el almacenamiento en caché:

__asm__ __volatile__("rdtsc": "=a" (a0), "=d" (d0)) r1 = x1 + y1; __asm__ __volatile__("rdtsc": "=a" (a1), "=d" (d1)) r2 = x2 + y2; __asm__ __volatile__("rdtsc": "=a" (a2), "=d" (d2))

Ahora obtendrá una nueva marca de tiempo cada vez, pero todavía tiene el problema de que tanto el compilador como la CPU pueden reordenar todas estas declaraciones. Podría terminar ejecutando los bloques asm después de que r1 y r2 ya hayan sido calculados. Para evitar esto, agregaría algunas barreras que fuerzan la serialización:

__asm__ __volatile__("mfence;rdtsc": "=a" (a0), "=d" (d0) :: "memory") r1 = x1 + y1; __asm__ __volatile__("mfence;rdtsc": "=a" (a1), "=d" (d1) :: "memory") r2 = x2 + y2; __asm__ __volatile__("mfence;rdtsc": "=a" (a2), "=d" (d2) :: "memory")

Tenga en cuenta la instrucción mfence aquí, que impone una barrera del lado de la CPU, y el especificador de "memoria" en el bloque volátil que impone una barrera de tiempo de compilación. En las CPU modernas, puede reemplazar mfence:rdtsc con rdtscp por algo más eficiente.


asm es para incluir código ensamblador nativo en el código fuente C. P.ej

int a = 2; asm("mov a, 3"); printf("%i", a); // will print 3

Los compiladores tienen diferentes variantes de esto. __asm__ debería ser, quizás con algunas diferencias específicas del compilador.

volatile significa que la variable puede modificarse desde afuera (también conocido como no por el programa C). Por ejemplo, cuando se programa un microcontrolador en el que la dirección de memoria 0x0000x1234 se asigna a alguna interfaz específica del dispositivo (es decir, cuando se accede a la codificación del GameBoy, se accede a botones / pantalla / etc.).

volatile std::uint8_t* const button1 = 0x00001111;

Esto desactiva las optimizaciones del compilador que dependen de que *button1 no cambie a menos que el código lo cambie.

También se utiliza en la programación de subprocesos múltiples (¿ya no es necesario?) Donde una variable podría ser modificada por otro hilo.

inline es una sugerencia para el compilador de llamadas "en línea" a una función.

inline int f(int a) { return a + 1 } int a; int b = f(a);

Esto no debe compilarse en una llamada de función a f sino a int b = a + 1 . Como si f donde una macro. Los compiladores realizan esta optimización de forma automática en función del uso / contenido de la función. __inline__ en este ejemplo podría tener un significado más específico.

__attribute__((noinline)) (sintaxis específica de GCC) previene que una función sea inlineada.