0

Trying to save a variable in an arm register using inline assembly.

    unsigned int lma_offset = 0x1234; // typically calculated, hardcoded for example

    __asm volatile ("MOV R10, %[input]"
        : [input] "=r" (lma_offset)
      );

This changes lma_offset to 0xd100 in my case, instead of setting the register. What am I doing wrong?

PS: when I declare lma_offset as const it gives a compiler error because lma_offset is used as output. So obviously something is wrong, still I cant find the correct syntax for this.

bas
  • 13,550
  • 20
  • 69
  • 146
  • Outputs are listed after the first colon. Inputs are listed after another colon. Do not use `=`; that is for output operands. Given that you report `lma_offset` changes as a result of this, I presume you are using an assembler with AT&T syntax (source on left, destination on right). Also, `__asm` is suspicious; I would expect either `__asm__` or `asm`. You could try `__asm__ volatile("MOV %[input], R10" : : [input] "r" (lma_offset));`. However, while this might copy `lma_offset` to `R10`, it will not keep it there. The compiler is free to use R10 for other purposes… – Eric Postpischil May 01 '22 at 13:21
  • 1
    … You could add more information to the `__asm__` to tell the compiler you are putting something in R10, but there is no way in an `__asm__` construct to tell the compiler to keep it there and not to use R10 for any other purpose. You can declare a register variable that is assigned to R10, but what is the point? Why do you want something in R10, and for how long? – Eric Postpischil May 01 '22 at 13:23
  • 1
    What are you trying to achieve? The moment your ASM statement ends, the compiler is free to overwrite r10 with whatever it wants. Also, as you have not declared `r10` to be clobbered, it may expect R10 to keep its previous value. So overall, it's not really clear what effect you expect. – fuz May 01 '22 at 14:10
  • @EricPostpischil AT&T syntax is a thing for x86, not ARM. There is no AT&T syntax for ARM. What happens is that as `lma_offset` is listed as an output operand, the compiler picks a random register and assumes its contents will hold the variable `lma_offset` after the inline assembly is done. – fuz May 01 '22 at 14:12
  • @EricPostpischil I am using r10 to pass a value between my bootloader and the actual app. – bas May 01 '22 at 15:40
  • @fuz could you tell me how to list `lma_offset` as a input instead? – bas May 01 '22 at 15:41
  • @bas You will not be able to do it this way. Instead, you have to do the parameter passing and the function call in a single `asm` statement. As for passing it as an input, do what Eric Postpischil says. You can also use register variables to tell the compiler to directly put `lma_offset` into `r10` in your asm statement so you don't have to emit a possibly useless `mov`. – fuz May 01 '22 at 15:45
  • @EricPostpischil, right, if I had read your comment better I would have found this out a bit sooner... adding another `:` and removing the `=` indeed does the trick. – bas May 01 '22 at 16:50
  • @EricPostpischil ps, `__asm` and `__asm__` seem to be identical (some fuzz about namespace) according to https://stackoverflow.com/questions/3323445/what-is-the-difference-between-asm-asm-and-asm. the nxp sdk uses `__asm` everywhere – bas May 01 '22 at 16:53

1 Answers1

0

For future reference, according to Erics comment

const unsigned int lma_offset = 0x10000;

__asm__ volatile ("MOV R10, %[input]"
    : // no C variable outputs
    : [input] "r" (lma_offset)
    : "R10"      // tell the compiler R10 is modified
      );

using double : and replacing the "=r" with "r" indeed solves the problem.

It would also be possible to ask the compiler to have that constant already in R10 for an asm statement, by using a register local variable to force the "r" input to pick r10. (Then we can omit the redundant mov r10, r10).

 register unsigned r10 __asm__("r10") = lma_offset;  // picks r10 for "r" constraints, no other *guaranteed* effects
 __asm__ volatile ("@ inline asm went here"  // empty template, actually just a comment you can see if looking at the compiler's asm output
   : // no C variable outputs
   : [input] "r" (lma_offset)
   : // no clobbers needed
 );

When writing a register to some output C variable it would result in

unsigned int lma_offset = 0x0;

__asm__ volatile ("MOV %[output], R11"
    : [output] "=r" (lma_offset)
    // no clobbers needed; reading a register doesn't step on the compiler's toes
      );
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
bas
  • 13,550
  • 20
  • 69
  • 146