2
votes

In A64 assembler, there are different ways to specify addresses.

/*
[base{,#0}]                 Simple register (exclusive) - Immediate Offset
[base{,#imm}]               Offset - Immediate Offset
[base,Xm{,LSL #imm}]        Offset - Register Offset
[base,Wm,(S|U)XTW {#imm}]   Offset - Extended Register Offset
[base,#imm]!                Pre-indexed - Immediate Offset
[base],#imm                 Post-indexed - Immediate Offset
label                       PC-relative (literal) load - Immediate Offset
*/

I would like to use "Offset - Immediate Offset" in inline assembler.

__asm__("ldp x8, x9, %0, 16 \n\t"
        :
        : "m" (*somePointer)
        : "x8", "x9");

This gets translated to

ldp x8, x9, [x0], 16

My goal is to reach

ldp x8, x9, [x0, 16]

How can I write such instructions with inline assembler?

1
I'm not sure how you can use %1 with only 1 parameter? What about using a register constraint? Warning: I have not tried this, but what about something like asm("ldp x8, x9, [%0, %1]" : : "r" (somePointer), "i"(16) : "x8", "x9");?David Wohlferd
Fixed this - it should obviously read %0. Extracted from a longer statement with more inputs / outputs, and forgot to update the argument :-) Aren't those hints kind of important for the compiler, so that it knows which memory regions are accessed by my code? e.g. if I'm using it in a write context, the compiler probably needs to know which memory regions I have clobbered - in which case, the register constraint will not work?Etan
True enough. So since the compiler does not actually parse the asm, you can do: asm("ldp x8, x9, [%0, %1]" : : "r" (somePointer), "i"(16), "m" (*somePointer) : "x8", "x9"); (ie have the constraint, but don't reference it). Or add the "memory" clobber. Note that as written, you are only telling the compiler that the asm reads the memory, not that it changes it.David Wohlferd

1 Answers

1
votes

I don't have an ARM 64-bit toolchain to test this with, but you should be able to do something like this:

asm("ldp x8, x9, %0\n\t"
    :
    : "Ump" (*((__int128 *) somePointer + 1))
    : "x8", "x9");

The Ump constraint limits the memory operand to those permitted by the integer LDP instruction, otherwise it works like the m constraint. If somePointer is already a pointer to a 128-bit type you can just use somePointer[1] as the operand.

If the above doesn't work then David Wohlferd's suggestion should:

asm("ldp x8, x9, [%0, %1]" 
    : 
    : "r" (somePointer), "i"(16), "m" (*somePointer) 
    : "x8", "x9");