簡體   English   中英

C 代碼和 ARM Cortex 架構的擴展 ASM 中的內聯匯編語句

[英]Inline assembly statements in C code and extended ASM for ARM Cortex architectures

我正在嘗試使用ARM 編譯器 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();
}

任何幫助將非常感激。

是編譯器版本(ARM compiler 5)不支持Extended Asm導致的錯誤嗎?

那是 GNU C 內聯擴展 Asm 語法,所以是的,顯然不支持它的編譯器會出錯。


2016 年的 GCC 更改( PR24414 ) 為非空基本 Asm 語句提供了隱式的"memory"破壞,並使它們在 x86 等目標上隱式破壞"cc" ,而擴展 asm 就是這樣做的。 So if your GCC version is new enough, you I guess could be able to safely use Basic Asm here, assuming that undocumented GCC behaviour continues to be present in future GCC versions to help badly written or old code work the way it was probably intended. (假設這在 Keil 中實際上也是安全的,那里也有一個隱式的 memory 屏障)。 我不知道這是什么 GCC 版本

(在 asm 語句中存儲和重新加載全局變量的試金石將告訴您它在任何給定的 GCC 版本上的行為。如果您看到寄存器值被重用於asm(""::: ); (擴展為明確沒有 memory clobber) 但不適用於asm("# comment"); (non-empty Basic),這意味着非空 Basic asm 語句具有隱式 memory clobber。否則,您會期望與 Extended 相同的優化。這個Godbolt 編譯器資源管理器鏈接顯示 GCC 6.4沒有基本 asm 的隱式內存破壞器,但 GCC7.1 有。

或者更好的是,您可以使用 CPP 宏#if檢測到錯誤的編譯器並省略::: "memory"部分,它會阻塞它,假設它將 asm 語句視為具有 memory clobber。 或者檢測__GNUC____GNUC_MINOR__版本等,並使用它來檢測聲稱與支持擴展 asm 的 C 的 GNU 方言版本兼容的任何編譯器。


基本 Asm 永遠不應該在 GNU C 中的函數內部使用,即使對於像dsb這樣不讀取或寫入寄存器的指令也是如此,因為沒有書面保證可以訂購 wrt。 編譯器生成的 memory 訪問。 https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended 通常你應該只將它用於__attribute__((naked)) function 或全局 scope 的主體。

Basic asm 沒有任何好處。 內聯匯編是你永遠不應該隨便使用的東西,你不能用 Basic 做任何事情,你不能在非裸 function 中顯式地用 Extended 做任何事情。 (幾乎沒有任何事情你實際上可以安全地用 Basic 做:你不能安全地觸摸 memory 中的寄存器甚至全局變量(見這個,並且不能保證訂購任何東西。)

因此,依賴具有隱式"memory"破壞的未記錄的基本 Asm 行為非常糟糕。 沒有充分的理由對隱式內存破壞器 GCC 進行更改(除了可能使舊代碼和/或壞代碼更有可能正常工作); 在 GNU C 中,擴展 asm 總是更好。


Keil 是否支持stdatomic.h用於atomic_thread_fence(memory_order_seq_cst)發出一個不能用周圍代碼重新排序的“dmb”? (但對 dsb 和 isb 沒有幫助)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM