Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding hard-number shift of flexible second operand in ASM

This question pertains to the ARM assembly language.

My question is whether it is possible to use a macro to replace the immediate value in the ASM code to shift a register value so that I don't have to hard-code the number.

I'm not sure whether the above question makes sense, so I will provide an example with some asm codes:

So there exist few instructions such as ror instruction in the ARM (https://developer.arm.com/documentation/dui0473/m/arm-and-thumb-instructions/ror), where it is possible to use a register value to rotate the value as we wish:

#define rotate(number, ptr) ({                       \
  asm volatile( \
    "ror %[output], %[output], %1\n" \ 
    : [output]"+r"(ptr) \ // this is the in/output 
    : "r"(number)); \ // this is the rotator
})

Now, let's take a look at orr instruction in the ARM (https://developer.arm.com/documentation/dui0473/m/arm-and-thumb-instructions/orr).

The syntax is as follows: ORR{S}{cond} Rd, Rn, Operand2

where Operand2 is a flexible operand, which means it can either be a constant or a register with optional shift (source: https://www.keil.com/support/man/docs/armasm/armasm_dom1361289851539.htm)

So something like this will work:

#define orr_test(ptr) ({                       \
  uint64_t __result;                    \
  asm volatile (\
    "orr %0, %1, #4 << 60\n"\
    : "=r" (__result) : "r" (ptr)); \
  __result;                 \
})

However, I question whether that #4 in the line "orr %0, %0, #4 << 60\n"\ can somehow be replaced with a macro so that I don't have to hard code #4. I think that this is not possible (because it could cause a big problem if not managed properly), but I still wanted to ask as I couldn't find any information online regarding it.

Please let me know if anything I mentioned above is unclear; thank you.

Edit (as per request):

Here is basically what I am hoping would be possible in terms of pseudocode:

#define orr_test(ptr, number) ({                       \
  uint64_t __result;                    \
  asm volatile (\
    "orr %0, %1, %[num] << 60\n"\
    : "=r" (__result) : "r" (ptr), [num]"r"(number)); \
  __result;                 \
})

In other words, rather than needing to hard-code #4 with shift, I wish to use a macro to determine what number to shift.

like image 596
Jay Avatar asked Dec 16 '21 18:12

Jay


1 Answers

The ARM64 orr immediate instruction takes a bitmask immediate, see Range of immediate values in ARMv8 A64 assembly for an explanation. And GCC has a constraint for an operand of this type: L.

So I would write:

#define MASK (4UL << 60)

inline uint64_t set_bit(uint64_t x) {
    uint64_t result;
    asm("orr %0, %1, %2"
        : "=r" (result)
        : "r" (x), "L" (MASK));
    return result;
}

Try on godbolt

I made a couple other fixes / improvements:

  • You were using %0 for both the destination and first source operand of orr, which only works if by luck the compiler has chosen the same register for x and result, which it need not do. You should use the operand matching the x input, here %1. The compiler is still free to choose the same register for both if it finds that advantageous (in other settings where this is not appropriate, you can suppress it with the & modifier).

  • Prefer an inline function to a macro whenever possible.

Note that if you change the value of MASK to something that is not encodable as a bitmask immediate, e.g. #define MASK (4UL << 60 | 1), you will get a compiler error. If you would rather have this continue to work, you can change the L constraint to Lr. The compiler will continue to emit an immediate if the expression MASK is a compile-time constant with a value appropriate for a bitmask immediate. If not, it will emit extra code to load the value into a register, then emit orr with register operand. Try it.

(Of course, this example is silly for inline asm. GCC generates perfectly good code for the C equivalent return x | MASK;. But I presume it was an example for some more complex code. Even so, you could think about whether the | operation could be factored out, and just used as an input for whatever really needs to be in asm. As usual, always consider whether don't use inline asm might apply.)

like image 105
Nate Eldredge Avatar answered Oct 17 '22 19:10

Nate Eldredge