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

你。 提交于 2021-02-17 05:18:30

问题


I am trying to compile the following two pieces of code with ARM Compiler 5 for a Cortex A microprocessor:

Part 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;
}

Part 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");
}

In both cases I get compile errors. See below, as an example.

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

Are the error caused by the compiler version (ARM compiler 5), which does not support Extended Asm?

If I re-write the code in Part 1 as follows, I do not get any error. Is the following code equivalent to that in Part 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;
}

How could I rewrite the code in Part 2, if the compiler does not support extended Asm? I have in mind the following, but I am not sure it is the same.

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();
}

Any help would be really appreciated.


回答1:


Are the error caused by the compiler version (ARM compiler 5), which does not support Extended Asm?

That's GNU C inline Extend Asm syntax, so yes obviously a compiler that doesn't support it will error.


A GCC change in 2016 (PR24414) gave non-empty Basic Asm statements an implicit "memory" clobber, as well as making them implicitly clobber "cc" on targets like x86 where Extended asm does that. 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. (Assuming that's actually also safe in Keil, with an implicit memory barrier there, too). I don't know what GCC versions this made it into

(A litmus test for storing and reloading a global across an asm statement will tell you how this behaves on any given GCC version. If you see the register value being reused for asm("" ::: ); (Extended with explicitly no memory clobber) but not for asm("# comment"); (non-empty Basic), that implies non-empty Basic asm statements have an implicit memory clobber. Otherwise you'd expect the same optimization as with Extended. This Godbolt compiler explorer link shows GCC 6.4 does not have an implicit mem clobber for Basic asm, but GCC7.1 does.

Or better, you could use CPP macros to #if detect the bad compiler and omit the ::: "memory" part that it chokes on, on the assumption that it treats the asm statement as having a memory clobber. Or detect __GNUC__ and __GNUC_MINOR__ version, etc. and use that to detect any compiler that claims compatibility with a version of the GNU dialect of C that supports Extended asm.


Basic Asm should never be used inside functions in GNU C, even for instructions like dsb that don't read or write registers, because there's no documented guarantee of ordering wrt. compiler-generated memory accesses. https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended. Normally you should only ever use it for the body of a __attribute__((naked)) function, or at global scope.

There's no upside to Basic asm; inline asm is something you should never ever use casually, and there's nothing you can do with Basic you can't do explicitly with Extended inside a non-naked function. (And almost nothing you actually can do safely with Basic at all: you can't safely touch registers or even global vars in memory (see this, and it's not guaranteed to be ordered wrt. anything.)

So it sucks a lot to depend on undocumented Basic Asm behaviour of having an implicit "memory" clobber. There was no good reason for that implicit mem clobber GCC change (except for maybe making old and/or bad code more likely to happen to work right); Extended asm to make that explicit is always better in GNU C.


Does Keil support stdatomic.h for atomic_thread_fence(memory_order_seq_cst) to emit a "dmb" that can't be reordered with surrounding code? (Doesn't help for dsb and isb, though).



来源:https://stackoverflow.com/questions/65460597/inline-assembly-statements-in-c-code-and-extended-asm-for-arm-cortex-architectur

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!