【问题标题】:Inline assembly statements in C code and extended ASM for ARM Cortex architecturesC 代码中的内联汇编语句和用于 ARM Cortex 架构的扩展 ASM
【发布时间】:2021-04-04 05:17:33
【问题描述】:

我正在尝试使用 ARM Compiler 5 为 Cortex A 微处理器编译以下两段代码:

第 1 部分

static inline void cp15_write_sctlr(uint32_t value)
{
    asm("mcr p15, 0, %0, c1, c0, 0" :: "r"(value));
}

static inline uint32_t cp15_read_actlr(void)
{
    uint32_t actlr;
    asm("mrc p15, 0, %0, c1, c0, 1" : "=r"(actlr));
    return actlr;
}

第 2 部分

static inline void dmb(void)
{
    asm("dmb" ::: "memory");
}

static inline void dsb(void)
{
    asm("dsb" ::: "memory");
}

static inline void isb(void)
{
    asm("isb" ::: "memory");
}

在这两种情况下,我都会遇到编译错误。请参阅下面的示例。

line 64: Error:  #18: expected a ")"
    asm("dsb" ::: "memory");

是编译器版本(ARM compiler 5)不支持Extended Asm导致的错误吗?

如果我重新编写 第 1 部分 中的代码,如下所示,我不会收到任何错误。下面的代码是否等同于第 1 部分中的代码?

static inline void cp15_write_sctlr(uint32_t value)
{
    __asm
    {
        MCR p15, 0, value, c1, c0, 0
    }
}

static inline uint32_t cp15_read_actlr(void)
{
    uint32_t actlr;
    __asm
    {
        MRC p15, 0, actlr, c1, c0, 1
    }
    return actlr;
}

如果编译器不支持扩展 Asm,我该如何重写第 2 部分中的代码? 我想到了以下几点,但我不确定它是否相同。

static inline void dmb(void)
{
    __schedule_barrier();
    __asm("dmb");
    __schedule_barrier();
}

static inline void dsb(void)
{
    __schedule_barrier();
    __asm("dsb");
    __schedule_barrier();
}

static inline void isb(void)
{
    __schedule_barrier();
    __asm("isb");
    __schedule_barrier();
}

任何帮助将不胜感激。

【问题讨论】:

  • 什么,具体是您的错误?请编辑您的问题,并在单独的代码块中发布exact错误文本。您使用的是什么编译器(例如gcc)?还有,什么汇编程序?编译器可能支持__asm 和/或__asm__ asm [它们具有相同的功能]。
  • @CraigEstey Post 已编辑。正如我在帖子中所写,编译器是 Arm Compiler 版本 5。谢谢。
  • 当我完成 [商业] arm 开发时,我在 Ubuntu 下使用了 gcc 交叉编译器。我以前从未听说过“arm compiler 5”。我查了一下,[AFAICT] 这是一个重新命名的 Keil 套件 [你必须付费]。众所周知,Keil 在某些事情上有些滞后。您可以将 asm 内容隔离到一个单独的文件中,并使用 gcc 对其进行编译,然后将 .o 加载到 IDE 中。来自:keil.com/support/man/docs/armcc/armcc_chr1359124246903.htm 似乎没有扩展了 asm。
  • 或者,如果你隔离了 asm 代码,你可以把它放到 .s 文件中定义的函数中。我怀疑性能会没问题[尽管没有纯内联那么快]。您可以使用 100% 的 asm 块并且是纯的 C 函数来实现相同的目的。它可能仍可用作static inline 函数。我会反汇编 .o 文件以查看为给定函数生成的实际代码,以了解它的工作情况。

标签: c assembly arm inline-assembly keil


【解决方案1】:

是编译器版本(ARM compiler 5)不支持Extended Asm导致的错误吗?

这是 GNU C 内联扩展 Asm 语法,所以是的,显然不支持它的编译器会出错。


A GCC change in 2016 (PR24414) 给非空的基本 Asm 语句一个隐式的 "memory" 破坏,以及使它们在扩展 asm 的 x86 等目标上隐式破坏 "cc"。因此,如果您的 GCC 版本足够新,我猜您 可以 在这里安全地使用 Basic Asm,假设在未来的 GCC 版本中继续存在未记录的 GCC 行为以帮助编写不良或旧代码工作它可能是预期的方式。 (假设这在 Keil 中实际上也是安全的,那里也有一个隐式内存屏障)。我不知道它变成了什么 GCC 版本

(在 asm 语句中存储和重新加载全局变量的试金石将告诉您这在任何给定的 GCC 版本上的行为方式。如果您看到寄存器值被重用于asm("" ::: );(扩展时明确没有内存破坏)但是不适用于asm("# comment");(非空基本),这意味着非空基本 asm 语句具有隐式内存破坏。否则,您会期望与扩展相同的优化。这 Godbolt compiler explorer link 显示 GCC 6.4 不 Basic asm 有一个隐式的内存破坏器,但 GCC7.1 有。

或者更好的是,您可以使用 CPP 宏来 #if 检测错误的编译器并省略它阻塞的 ::: "memory" 部分,假设它将 asm 语句视为具有内存破坏者。或者检测 __GNUC____GNUC_MINOR__ 版本等,并使用它来检测任何声称与支持扩展 asm 的 GNU 方言版本兼容的编译器。


基本 Asm 永远不应该在 GNU C 的函数中使用,即使是像 dsb 这样不读取或写入寄存器的指令,因为没有书面保证 wrt 的顺序。编译器生成的内存访问。 https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended。通常你应该只将它用于__attribute__((naked)) 函数的主体,或者在全局范围内。

Basic asm 没有任何好处;内联汇编是你永远不应该随便使用的东西,你不能用 Basic 做任何事情,你不能在非裸函数中显式地用 Extended 做任何事情。 (几乎没有任何事情你实际上可以用 Basic 安全地做:你不能安全地触摸寄存器甚至内存中的全局变量(请参阅this,并且不能保证任何东西都可以订购。)

因此,依赖具有隐式"memory" clobber 的未记录的基本 Asm 行为非常糟糕。这种隐式的内存破坏器 GCC 更改没有充分的理由(除了可能使旧代码和/或坏代码更有可能正常工作);在 GNU C 中扩展 asm 以使其明确化总是更好。


Keil 是否支持stdatomic.hatomic_thread_fence(memory_order_seq_cst) 发出一个不能用周围代码重新排序的“dmb”? (但对 dsb 和 isb 没有帮助)。

【讨论】:

  • 我需要检查是否可以使用stdatomic.h。但是,我看到在编译时管理内存障碍是有内在的:link。只是想知道__dmb(15)__dsb(15)__dmb(15) 的行为是否与第 2 部分中的定义相同。
  • @UmbertoD.:是的,我假设内在函数阻止重新排序 wrt。内存访问。否则它们将基本上无法使用,除非您手动将它们包围起来,而没有人想要那样。
  • 您可以使用我来自 Godbolt 的石蕊测试代码和您的 Keil 编译器围绕 __dmb(15) 或其他任何东西来查看它是否真的在前后强制访问内存。 (尽管如果它仍然强制存储在障碍之前发生,它实际上不需要阻止正确性优化。GCC“忘记”所有全局变量的值是一个实现细节。但如果发生这种情况,你可以很确定有一个与内在函数相关的隐式编译器内存屏障。)
猜你喜欢
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多