【问题标题】:Working of __asm__ __volatile__ ("" : : : "memory")__asm__ __volatile__ ("" : : : "memory") 的工作
【发布时间】:2013-02-03 17:28:59
【问题描述】:

__asm__ __volatile__ () 的基本作用是什么?"memory" 对于 ARM 架构的意义是什么?

【问题讨论】:

  • asm _volatile_ 在 (ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#ss5.4) 中解释。但这不应该是两个分开的问题吗?
  • @phoeagon:这一行是编译器内存访问调度屏障的统一操作——虽然这两个问题可能分别足够相关,但不太可能回答所提出的问题。

标签: c gcc arm embedded-linux volatile


【解决方案1】:
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;
}

【讨论】:

  • 如果您实际上是在回答页面顶部的问题,请仅在此处发布答案。这里的问题要求解释,你没有提供类似的东西。
  • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
【解决方案2】:
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 ordereddevice (ref),CPU 仍然可以重新排序内存访问。

【讨论】:

  • 在这种特殊情况下,可以在标准 C 中通过声明如下参数来实现相同的效果:int c(volatile int *d, volatile int *e)
【解决方案3】:

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

__asm__ 是允许在 C 代码中嵌套输入汇编语言语句的 gcc 扩展 - 此处使用它的特性是能够指定阻止编译器执行某些类型优化的副作用(在此case 最终可能会生成不正确的代码)。

__volatile__ 用于确保 asm 语句本身不会与任何其他 volatile 访问重新排序(C 语言中的保证)。

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

【讨论】:

【解决方案4】:

这里解释一下意思:

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

基本上,这意味着汇编代码将在您期望的地方执行。它告诉编译器不要对其周围的指令重新排序。也就是这段代码之前执行的代码,之后执行的代码。

【讨论】:

    猜你喜欢
    • 2012-01-26
    • 2016-11-24
    • 2010-11-26
    • 2016-11-29
    • 2019-04-23
    • 1970-01-01
    • 2014-08-02
    • 2019-10-20
    • 1970-01-01
    相关资源
    最近更新 更多