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.
CodePudding user response:
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;
}
I made a couple other fixes / improvements:
You were using
%0
for both the destination and first source operand oforr
, which only works if by luck the compiler has chosen the same register forx
andresult
, which it need not do. You should use the operand matching thex
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.)