3
votes

There is some problem w/ inline assembler I could not understand.

I have a function with inline assembler. Inside the ASM block I need to use some scratch register to modify some system value.

void setHW(uint32_t val) {
    asm volatile (
        mrc 15, 0, r0, ...
        orr r0, r0, %0 
        mcr 15, 0, r0, ...
        : :"r"(val) :"r0"
    );
}

Actually function is inlined by compiler and it's alright, code still works well.

The problem appears when I try to replace hardcoded r0 with some stub variable so compiler could choose the best possible register to use. It looks like this

void setHW(uint32_t val) {
    uint32_t reg;
    asm volatile (
        mrc 15, 0, %[reg], ...
        orr %[reg], %[reg], %0 
        mcr 15, 0, %[reg], ...
        :[reg]"=r"(reg) :"r"(val) :
    );
}

Now compiler choose register by itself but actually corrupt the value of setHW caller function. In disassembler it looks like

        add r2, r4, r5       ; caller part, r2 contain some intermedia result
        mrc 15, 0, r2, ...   ; inlined setHW(), r2 is choosen as scratch reg
        orr r2, r2, r0 
        mcr 15, 0, r2, ...
        ; caller continue

As you could see r2 is corrupted and everything just fall apart.

How should I define scratch register to avoid this?

1
When you added [reg], val stopped being %0 and became %1. - David Wohlferd
@DavidWohlferd, yep thanks. BTW [reg]"=r"(reg) still doesn't work properly. In some cases compiler uses the same register for val and reg so I get mrc r3...; orr r3, r3, r3; mcr r3...;. [reg]"+r"(reg) actually fix the problem but now compiler shows error 'reg' is used uninitialized... and it's necessary to assign a dump value to reg and extra instruction appears. Could it be fixed? - user3124812
I know you've already solved it, but in the first code block, I believe the issue you're having is that according to the ARM EABI calling convention, the first argument to a function will be in r0, so that code is likely clobbering uint32_t val with mrc 15, 0, r0, .... Again, back in the ARM EABI, r4-8,r10,r11 are considered "variable registers", so I think if you had used one of those instead, you'd have been good. - rjp
@rjp ,1) the problem is not w/ the first block. 2) when r0 is chosen compiler will preserve the value from r0 before asm volatile and restore it after. - user3124812

1 Answers

4
votes

How about something like this:

void setHW(uint32_t val) {
    uint32_t reg;
    asm volatile (
        mrc 15, 0, %[reg], ...
        orr %[reg], %[reg], %[val] 
        mcr 15, 0, %[reg], ...
        :[reg]"=&r"(reg) :[val] "r"(val) :
    );
}

Note the =&r to fix the early clobber problem (see https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html for a description of &).