¿Qué hace __asm__ __volátil__ en C?

8 minutos de lectura

¿Que hace asm volatil en C
usuario3692521

Busqué en un código C de
http://www.mcs.anl.gov/~kazutomo/rdtsc.html

Usan cosas como __inline__, __asm__ etc como la siguiente:

código1:

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

código2:

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

Me preguntaba qué hacen el código 1 y el código 2.

(Nota del editor: para este caso de uso específico de RDTSC, se prefieren los intrínsecos: ¿Cómo obtener el recuento de ciclos de la CPU en x86_64 desde C++? Consulte también https://gcc.gnu.org/wiki/DontUseInlineAsm)

1647585129 306 ¿Que hace asm volatil en C
cory nelson

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

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

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

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

Cuando se usa solo, así:

__asm__ __volatile__ ("")

En realidad, no ejecutará nada. Sin embargo, puede extender esto para obtener una barrera de memoria en tiempo de compilación que no permitirá reordenar ninguna instrucción de acceso a la memoria:

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

los rdtsc instrucción es un buen ejemplo de volátil. rdtsc generalmente se usa cuando necesita cronometrar cuánto tardan en ejecutarse algunas instrucciones. Imagina un código como este, donde quieres cronometrar r1 y r2la ejecución de:

__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 la salida válida podría mostrar que cada línea tomó exactamente 0 relojes para ejecutarse. Obviamente esto no es lo que quieres, así que presentas __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 aún 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 se hayan calculado. 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")

Nota la mfence instrucción 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.

  • Entonces, con un bloque vacío, ¿es una especie de barrera de instrucción?

    –Bryan Chen

    20 de octubre de 2014 a las 0:20

  • Tenga en cuenta que el compilador solo puede controlar el orden del código estático que genera y evitar mover cosas más allá de esta barrera en el momento de la compilación, pero no puede controlar el orden de ejecución real dentro de la CPU, lo que aún puede cambiarlo (la CPU no sabe sobre el atributo volátil, o el bloque de código vacío). Con rdtsc esto puede causar potencialmente algunas imprecisiones.

    – Leeor

    20 de octubre de 2014 a las 0:28

  • @Leeor De hecho, de ahí la “barrera del tiempo de compilación”.

    –Cory Nelson

    20 de octubre de 2014 a las 0:31

  • En su mayoría, el código en la pregunta simplemente apesta. Debería usar el __rdtsc intrínseco. volatile es inútil en asm volatile(""). Y tu explicación de volátil no es buena, con asm("rdtsc":... el compilador puede incluso reordenar los bloques asm (o eliminarlos si a0 y d0 no se usan), mientras que con volatile tiene que mantenerlos en este orden, pero aún puede mover las adiciones y las tiendas.

    – Marc Glisse

    20 de octubre de 2014 a las 11:41


  • Nota: Aunque no particularmente relacionado, rdtsc debe evitarse para la supervisión del rendimiento, ya que muchos factores pueden alterar el resultado.

    – edmz

    20/10/2014 a las 16:29

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

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

Los compiladores tienen diferentes variantes de la misma. __asm__ también debería ser, tal vez con algunas diferencias específicas del compilador.

volatile significa que la variable se puede modificar desde el exterior (también conocido como no por el programa C). Por ejemplo, al programar un microcontrolador donde la dirección de memoria 0x0000x1234 se asigna a alguna interfaz específica del dispositivo (es decir, cuando se codifica para GameBoy, se accede a los botones/pantalla/etc. de esta manera).

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

Esto deshabilitó las optimizaciones del compilador que se basan en *button1 no cambia a menos que sea cambiado por el código.

También se usa en la programación de subprocesos múltiples (¿ya no se necesita hoy en día?) donde una variable puede ser modificada por otro subproceso.

inline es una pista 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 para f pero en int b = a + 1. Como si f donde una macro. Los compiladores en su mayoría hacen esta optimización automáticamente dependiendo del uso/contenido de la función. __inline__ en este ejemplo podría tener un significado más específico.

Del mismo modo __attribute__((noinline)) (sintaxis específica de GCC) evita que una función se inserte.

  • ¡¡Gracias!! ¿Y cuál es el beneficio de noinline?

    – usuario3692521

    19/10/2014 a las 23:43

  • Supongo que solo se asegura de que llamar foo2 se traduce a una llamada de función a una función vacía con dos argumentos enteros y que devuelve un entero, en el ensamblado. En lugar de ser optimizado lejos. Esa función luego podría implementarse en el código ensamblador generado.

    – tiempo

    19/10/2014 a las 23:47


  • ¿Cómo sabe devolver un número entero (¿qué número entero?) si la función está vacía?

    – usuario3692521

    19/10/2014 a las 23:49

  • Diría que volátil en un bloque ASM es bastante diferente de volátil en una variable. Aunque permanece el tema común, a saber, que restringe las libertades del optimizador.

    – MvG

    20 de octubre de 2014 a las 8:31

  • “También se usa en la programación de subprocesos múltiples (¿ya no se necesita hoy en día?) Donde una variable puede ser modificada por otro subproceso”. – si bien se usa, es incorrecto ya que garantiza solo el orden de las instrucciones de los accesos, no la atomicidad del acceso a la memoria (aunque el acceso alineado es atómico en la mayoría de las arquitecturas) o las vallas de memoria (excepto la extensión MSVC, que está deshabilitada en ARM). Para un uso adecuado, es necesario utilizar atómicos C(++)11 o intrínsecos del compilador.

    – Maciej Piechotka

    20 de octubre de 2014 a las 8:38

1647585130 779 ¿Que hace asm volatil en C
David C Rankin

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

los __volatile__ El calificador, generalmente utilizado en Real-Time-Computing de sistemas integrados, soluciona un problema con las pruebas del compilador del status register Para el ERROR o READY bit que 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 forzar que cada referencia del objeto sea una referencia genuina.

  • En realidad, no, es para cualquier cosa con efectos secundarios que no describa/no pueda describir con restricciones de operandos, por ejemplo, cuando desea que suceda incluso si todos los operandos de salida no se usan.

    – Peter Cordes

    24 oct 2017 a las 9:40

  • ¿No es eso lo que dice obligar a que cada referencia del objeto sea una referencia genuina? La razón por la que estoy un poco confundido por el “no realmente” es que la descripción se tomó casi textualmente de la documentación de referencia tal como existía en octubre de 2014. Veré si puedo desenterrar la cita.

    –David C. Rankin

    24/10/2017 a las 23:07


  • En su mayoría, no estaba de acuerdo con decir que solo es relevante para RTC. No se trata de un cambio “rápido”, sino de cualquier cosa que pueda tener efectos secundarios. Que “toda referencia una referencia genuina” suena como una descripción del volatile calificador de tipo (p. ej. volatile int), no GNU C asm volatile. Con asm en línea no hay “el objeto”.

    – Peter Cordes

    24/10/2017 a las 23:13


  • Te tengo, supongo que estaría mejor redactado para decir volatile deshabilita la optimización que descarta las declaraciones de asm si determinan que no hay necesidad de las variables de salida, de todos modos :)

    –David C. Rankin

    24/10/2017 a las 23:15

  • si, además algunos prevención de reordenación, y más si utiliza un "memory" clobber para convertirlo en una barrera del compilador.

    – Peter Cordes

    24/10/2017 a las 23:21

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad