3

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?

artless noise
  • 21,212
  • 6
  • 68
  • 105
user3124812
  • 1,861
  • 3
  • 18
  • 39
  • 1
    When you added `[reg]`, val stopped being `%0` and became `%1`. – David Wohlferd Jul 21 '16 at 02:06
  • @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 Jul 21 '16 at 02:39
  • 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 Aug 02 '16 at 14:14
  • @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 Aug 03 '16 at 00:14

1 Answers1

4

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 &).

David Wohlferd
  • 7,110
  • 2
  • 29
  • 56