variable software qualifier language c gcc arm embedded-linux volatile

software - volatile char in c



Trabajo de__asm____volatile__(""::: "memoria") (4)

¿Qué hace básicamente __asm__ __volatile__ () y qué significa la "memory" para la arquitectura ARM?


El significado se explica aquí:

http://en.wikipedia.org/wiki/Memory_ordering

Básicamente implica que el código de ensamblado se ejecutará donde lo espere. Le dice al compilador que no reordene las instrucciones a su alrededor. Eso es lo que se codifica antes de que este fragmento de código se ejecute antes y lo que se codifica después se ejecutará después.


Esta secuencia es una barrera de programación de acceso a la memoria del compilador, como se señala en el artículo al que hace referencia Udo. Éste es específico de GCC; otros compiladores tienen otras formas de describirlos, algunos de ellos con declaraciones más explícitas (y menos esotéricas).

__asm__ es una extensión de gcc que permite ingresar sentencias de lenguaje ensamblador anidadas dentro de su código C, que se usa aquí para su propiedad de poder especificar efectos colaterales que evitan que el compilador realice ciertos tipos de optimizaciones (que en este caso podrían terminar generando código incorrecto).

__volatile__ es necesario para garantizar que la instrucción asm en sí misma no se reordena con ningún otro acceso volátil (una garantía en el lenguaje C).

memory es una instrucción a GCC que (más o menos) dice que la secuencia ASM en línea tiene efectos secundarios en la memoria global, y por lo tanto no solo se deben tener en cuenta no solo los efectos sobre las variables locales.


asm volatile("" ::: "memory");

crea una barrera de memoria de nivel de compilación que fuerza al optimizador a no reordenar los accesos de memoria a través de la barrera.

Por ejemplo, si necesita acceder a alguna dirección en un orden específico (probablemente porque esa área de memoria en realidad está respaldada por un dispositivo diferente en lugar de una memoria) necesita poder decirle esto al compilador; de lo contrario, puede optimizar sus pasos para por el bien de la eficiencia

Suponga que en este escenario debe incrementar un valor en la dirección, leer algo e incrementar otro valor en una dirección adyacente.

int c(int *d, int *e) { int r; d[0] += 1; r = e[0]; d[1] += 1; return r; }

El problema es que el compilador ( gcc en este caso) puede reordenar el acceso a la memoria para obtener un mejor rendimiento si lo solicita ( -O ). Probablemente conduce a una secuencia de instrucciones como a continuación:

00000000 <c>: 0: 4603 mov r3, r0 2: c805 ldmia r0, {r0, r2} 4: 3001 adds r0, #1 6: 3201 adds r2, #1 8: 6018 str r0, [r3, #0] a: 6808 ldr r0, [r1, #0] c: 605a str r2, [r3, #4] e: 4770 bx lr

Los valores anteriores para d[0] y d[1] se cargan al mismo tiempo. Supongamos que esto es algo que desea evitar, entonces necesita decirle al compilador que no reordene los accesos a la memoria y que use asm volatile("" ::: "memory") .

int c(int *d, int *e) { int r; d[0] += 1; r = e[0]; asm volatile("" ::: "memory"); d[1] += 1; return r; }

Así que obtendrás la secuencia de instrucciones como quieres que sea:

00000000 <c>: 0: 6802 ldr r2, [r0, #0] 2: 4603 mov r3, r0 4: 3201 adds r2, #1 6: 6002 str r2, [r0, #0] 8: 6808 ldr r0, [r1, #0] a: 685a ldr r2, [r3, #4] c: 3201 adds r2, #1 e: 605a str r2, [r3, #4] 10: 4770 bx lr 12: bf00 nop

Cabe señalar que esto es solo una barrera de memoria en tiempo de compilación para evitar que el compilador reordene los accesos a la memoria, ya que no pone instrucciones de nivel de hardware adicionales para vaciar las memorias o esperar a que se complete la carga o las tiendas. Las CPU todavía pueden reordenar los accesos a la memoria si tienen las capacidades arquitectónicas y las direcciones de memoria están en tipo normal lugar de strongly ordered o device ( ref ).


static inline unsigned long arch_local_irq_save(void) { unsigned long flags; asm volatile( " mrs %0, cpsr @ arch_local_irq_save/n" " cpsid i" //disabled irq : "=r" (flags) : : "memory", "cc"); return flags; }