8

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.

Jay
  • 373
  • 1
  • 10
  • It's unclear what you want to do. Could you show what you want to write and what result you want? Also why are you even using inline asm for this? PS: your inline asm seems broken anyway, you only reference `%0` you probably wanted `%1` the second time? – Jester Dec 16 '21 at 18:18
  • Hmm, but `ORR{S}{cond} Rd, Rn, Operand2` is the 32-bit version, from the `{cond}`. OP, can you clarify if this is for 32- or 64-bit ARM? – Nate Eldredge Dec 16 '21 at 18:25
  • @Jester thank you for catching that mistake, I have fixed the code accordingly, and also edited it to include what I wish to do. – Jay Dec 16 '21 at 18:40
  • @NateEldredge sorry for not clarifying that. this is for 64-bit ARM. However, I think it shouldn't matter too much in regards to my question? (if it does, please let me know) – Jay Dec 16 '21 at 18:40
  • 1
    Sounds like you want `orr %0, %1, %[num], LSL #60`. – Jester Dec 16 '21 at 18:45
  • @Jay: It matters because 32-bit ARM has different rules for allowable immediates to `orr`. I think the proper constraint for it would be `I`. – Nate Eldredge Dec 16 '21 at 19:15
  • 1
    @Jay: And by the way, your link is to the 32-bit instruction reference. You probably want to be looking at the 64-bit section of the combined [Architecture Reference Manual](https://developer.arm.com/documentation/ddi0487/ga) instead. Unfortunately it's only available as a giant PDF instead of separate HTML pages per instruction. – Nate Eldredge Dec 16 '21 at 19:27
  • @Jester thank you very much for your suggestion. I have attempted this and it works for `GCC`, but not for `clang`. I apologize I didn't make this question clear as I should have mentioned that I was using `clang`. Thank you again. – Jay Dec 18 '21 at 20:46
  • You have accepted Nate's answer which describes a constant method. I am confused what you want. In any case, I tested on godbolt, it works in clang. What error do you get? Also note that the compiler is perfectly capable of generating this from C code you do not need assembly at all. – Jester Dec 18 '21 at 23:42
  • Hello @Jester, an error is `unknown symbolic operand name in inline assembly string` (I tested on Godbolt as well, and also confused why it doesn't work). I agree with you that assembly is not needed at all for this, but this was a small snippet from a project that I am working on. Again, I really do appreciate your suggestion, and thank you again for your help. Sincerely. – Jay Dec 19 '21 at 00:32
  • That sounds like you made a typo. Can you give the godbolt link? – Jester Dec 19 '21 at 00:49
  • 1
    @Jester I apologize for the confusion, I meant to say it worked on Godbolt on my end as well as you said, but it wasn't working. I went back and double-checked on my code, and as you have mentioned, it was a typo and I was able to get it to work. Thank you very much. – Jay Dec 19 '21 at 01:06

1 Answers1

9

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.)

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • Just a nit: If it's possible that `x` might also be a constant, you could wrap that asm block with an `if __builtin_constant_p(x)`, and put the `return x | MASK` in the else. If there's any way to calculate the value at compile time (instead of run time), that's likely to be a win. – David Wohlferd Dec 17 '21 at 00:27
  • Thank you for your suggestion, I have learned a lot from the answer. To answer your other question, yes, my question was a small snippet for more complex code that I need to use other architecture-dependent asm instructions along with `orr`. – Jay Dec 17 '21 at 18:25