使用__asm__ __volatile__(“”:::“memory”)
基本上__asm__ __volatile__ ()
是做什么的,ARM架构的"memory"
意义是什么?
asm volatile("" ::: "memory");
创build一个编译器级内存屏障,迫使优化器不重新sorting跨越障碍的内存访问。
例如,如果您需要按特定顺序访问某个地址(可能是因为该内存区域实际上是由另一个设备而不是内存支持的),则需要将其告知编译器,否则可能会优化您的步骤为了效率。
假设在这种情况下,你必须在地址上增加一个值,读一些东西并在相邻地址中增加另一个值。
int c(int *d, int *e) { int r; d[0] += 1; r = e[0]; d[1] += 1; return r; }
问题是编译器(在这种情况下gcc
)可以重新安排你的内存访问,以获得更好的性能,如果你问( -O
)。 可能导致如下的一系列指令:
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
d[0]
和d[1]
同时加载。 让我们假设这是你想避免的,那么你需要告诉编译器不要重新sorting内存访问,那就是使用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; }
所以你会得到你想要的指令序列:
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
应该注意的是,这只是编译时内存屏障,以避免编译器重新sorting内存访问,因为它不会提供额外的硬件级别指令来刷新内存或等待加载或存储完成。 如果CPU具有架构function并且内存地址是normal
types而不是strongly ordered
或者device
( ref ),CPU仍然可以重新sorting内存访问。
这个序列是一个编译器内存访问调度的障碍,正如在Udo引用的文章中指出的那样。 这个是GCC特有的 – 其他编译器有其他方式来描述它们,其中一些更加明确(不那么深奥)。
__asm__
是一个允许汇编语言语句embedded在C代码中的gcc扩展,在这里用于指定防止编译器执行某些types的优化的副作用(在这种情况下可能最终生成不正确的代码)。
__volatile__
是必需的,以确保asm语句本身不被任何其他易失性访问(C语言中的保证)重新sorting。
memory
是GCC的一个指令,(有点)说内联asm序列对全局内存有副作用,因此不仅要考虑对局部variables的影响。
这里解释的意思是:
http://en.wikipedia.org/wiki/Memory_ordering
基本上这意味着汇编代码将在您期望的地方执行。 它告诉编译器不要对其重新sorting。 那就是在这段代码之前被执行的代码将被执行之前以及之后被执行的代码将被执行之后。
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; }