4

I am trying to use C variables in assembly. The purpose is to read a 32bit memory and assign it to a C variable.

uint32_t ASMRegRd32(uint32_t addr) {

uint32_t data;

    asm volatile (

        "ldr %0, [%1]"          "\n"

        : "=r" (data)

        : "r"(addr)

          );

return data;

}

Sadly being on AARCH64, above ldr instruction is returning a 64bit value, as compiler is choosing a 64bit operand Xt.

How can I restrict it to use a 32bit operand?
Should this work?

ldr w0, [%1];

mov %0, w0;
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Do you mean `ldr %w0, [%1]`? Because it looks like you are trying to use the [x86 Operand Modifiers](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#x86Operandmodifiers). I don't speak aarch64 (at all), but I'm pretty sure the x86 modifiers won't work here. If you do (speak aarch64 assembler), you might try browsing the actual [gcc code](https://gcc.gnu.org/viewcvs/gcc/branches/gcc-4_9-branch/gcc/config/aarch64/aarch64.c?revision=224524&view=markup#l3474) for constraints. Perhaps you'll see something there you can use. – David Wohlferd Aug 23 '15 at 07:54
  • 3
    You need to write `%w0` instead of just `%0` to get `w0` instead of `x0`. Clang was supposed to get a warning to tell – Marc Glisse Aug 23 '15 at 09:18
  • 1
    Yes I meant %w0. and it worked. Thanks. – lone_wanderer Aug 28 '15 at 15:01

2 Answers2

2

You use the constraint template modifier "w".

asm (
    "ldr %w[DST],[%[SRC]]" "\n"
    : [DST] "=r" (data) // 32 bit variable
    : [SRC] "X" (addr) // source address variable (expands into a x register)
    : "memory"   // the compiler doesn't assume that pointed-to memory is also an input unless we tell it
);

See also How can I indicate that the memory *pointed* to by an inline ASM argument may be used? for ways to avoid the "memory" clobber, like using an "m"(*addr) input to get the compiler to pick an addressing mode.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • +1 yes apparently an operand modifier is needed because `uint32_t` picks a 64-bit register!?! (https://godbolt.org/z/h3gXGF), but this is still missing anything to tell the compiler that the memory pointed to by `addr` is an input, as well as the pointer value itself. Either a `"memory"` clobber, or a memory source operand like `"m"(*addr)`. Otherwise dead-store elimination could remove a store that updates that memory before the inline asm reads it. – Peter Cordes Apr 28 '19 at 02:01
-1

Maybe you should change your API like below if you don't want to recompile the code in A32 compiler.

uint32_t ASMRegRd32(uint64_t addr) {

uint32_t data;

asm volatile (
    "ldrh w0, [%1]\n"
    : "=&r" (data)
    : "r"(addr)
    : "cc"
      );

return data;

}

because the default register is 64-bit, so the var addr conversion from 64-bit to 32-bit will make the program core dump.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
jefby
  • 345
  • 1
  • 4
  • 14
  • @MarcGlisse Because ARM return value always store in the register x0/w0. – jefby Aug 24 '15 at 10:31
  • 1
    You are somehow relying on the fact that the register allocator is likely to assign x0 to data in the inline asm. That might work in some cases, but it is way too fragile. – Marc Glisse Aug 24 '15 at 12:07
  • Went with "%w0", As I'll have to push/pop w0 to stack before using this API, that is like 200% extra code for me. Thanks – lone_wanderer Aug 28 '15 at 15:05
  • **This answer is wrong** in several ways. First, as noted, it is unsafe to assume `w0` will be allocated. Second, `ldrh` does a 16-bit load, not 32-bit. – Nate Eldredge May 14 '23 at 03:48