2013-02-19 97 views
36

基本上__asm__ __volatile__()对ARM架构有什么影响"memory"__asm__ __volatile__(“”:::“memory”)的工作

+5

'(http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#ss5.4)中解释了'asm _volatile_'。但这不应该成为两个分开的问题吗? – phoeagon 2013-02-19 05:54:02

+1

@phoeagon:这条线是一个编译器内存访问调度屏障的统一操作 - 虽然这两个问题可能分开足够相关,但这不可能回答所问的问题。 – unixsmurf 2013-02-19 07:38:50

回答

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

创建一个编译器级内存屏障,迫使优化器不对整个屏障重新排序内存访问。

例如,如果您需要按特定顺序访问某个地址(可能是因为该内存区域实际上是由另一个设备而不是内存支持的),则需要将其告知编译器,否则它可能只是为了提高效率优化您的步骤。

假设在这种情况下,你必须增加一个地址值,读一些东西并增加一个相邻地址的值。

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]以上数值在同一时间被加载。让我们假设这是你想避免的事情,那么你需要告诉编译器不要重新排序内存访问,那就是使用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 

应当指出的是,这只是编译时内存屏障,以避免编译器重新排序存储器存取,如它不会提供额外的硬件级别指令来刷新内存或等待加载或存储完成。如果CPU具有体系结构功能,并且内存地址为normal类型,而不是strongly ordereddeviceref),则CPU仍然可以重新排序内存访问。

+2

在这种特殊情况下,通过声明像这样的参数,可以在标准C中实现相同的效果:'int c(volatile int * d,volatile int * e)' – 2014-11-25 10:26:21

9

含义说明如下:

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

基本上,它意味着,你期望它的汇编代码就会被执行。它告诉编译器不要对它周围的指令重新排序。那就是在这段代码之前被执行的代码以及之后将被执行的代码。

17

该序列是编译器内存访问调度屏障,如Udo引用的文章中所述。这个是GCC特有的 - 其他编译器有其他方式来描述它们,其中一些用更明确(不那么深奥)的语句。

是一个允许汇编语言语句嵌入在C代码中的gcc扩展 - 在此处用于指定防止编译器执行某些类型优化的副作用(在此情况下可能最终会生成不正确的代码)。

__volatile__需要确保语句本身不会与任何其他volatile存取(任何C语言中的保证)重新排序。

memory是GCC的一条指令,表明内联asm序列对全局内存有副作用,因此不仅需要考虑对局部变量的影响。

+0

+1这个答案加** Udo **是正确的。这只是*编译器*的内存屏障。它不适用于SMP硬件。 – 2013-03-05 15:52:48

+0

那么如何决定,在哪里插入asm易失性语句? – Soundararajan 2014-01-06 04:56:01

+1

@Soundararajan:这个问题没有简短的答案。我建议阅读Paul McKenney关于内存访问顺序要求的优秀论文:http://www.rdrop.com/~paulmck/scalability/paper/whymb.2009.04.05a.pdf和Linux内核内存概述:https:/ /git.kernel.org/cgit/linux/kernel/git/torvalds/linux/kernel/git/torvalds/linux.git/tree/Documentation/memory-barriers.txt – unixsmurf 2014-01-06 15:48:58

0
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; 
} 
+0

如果您实际回答页面顶部的问题,请仅在此发布答案。这里的问题要求解释,你没有提供那样的东西。 – Mat 2016-10-26 04:25:59

+0

虽然此代码片段可能会解决问题,但[包括解释](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers)确实有助于提高帖子的质量。请记住,您将来会为读者回答问题,而这些人可能不知道您的代码建议的原因。 – 2016-10-28 13:23:58