避免 ASM 中灵活的第二个操作数的硬数字移位

Avoiding hard-number shift of flexible second operand in ASM

此问题与 ARM 汇编语言有关。

我的问题是是否可以使用宏来替换 ASM 代码中的立即值来移位寄存器值,这样我就不必对数字进行硬编码。

我不确定上面的问题是否有意义,所以我将提供一些asm代码的示例:

所以在ARM(https://developer.arm.com/documentation/dui0473/m/arm-and-thumb-instructions/ror)中存在很少的指令,例如ror指令,其中可以使用寄存器值按照我们的意愿旋转值:

#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
})

现在,让我们看一下ARM(https://developer.arm.com/documentation/dui0473/m/arm-and-thumb-instructions/orr)中的orr指令。

语法如下:ORR{S}{cond} Rd, Rn, Operand2

其中 Operand2 是一个灵活的操作数,这意味着它可以是 constanta register with optional shift(来源:https://www.keil.com/support/man/docs/armasm/armasm_dom1361289851539.htm

所以这样的事情会起作用:

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

但是,我怀疑 "orr %0, %0, #4 << 60\n"\ 行中的 #4 是否可以以某种方式用宏替换,这样我就不必费力了代码 #4。我认为这是不可能的(因为如果管理不当可能会导致大问题),但我仍然想问一下,因为我在网上找不到任何相关信息。

如果我上面提到的任何地方不清楚,请告诉我;谢谢。

编辑(根据要求):

这基本上是我希望在伪代码方面可能实现的内容:

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

换句话说,我不想用 shift 硬编码 #4,而是希望使用宏来确定要移动的数字。

ARM64 orr 立即数指令采用 位掩码立即数 ,请参阅 for an explanation. And GCC has a constraint 了解此类操作数:L

所以我会写:

#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

我做了一些其他修复/改进:

  • 您对 orr 的目标操作数和第一个源操作数都使用了 %0,只有当幸运的是编译器为 [=15 选择了相同的寄存器时才有效=] 和 result,它不需要这样做。您应该使用匹配 x 输入的操作数,这里是 %1。如果编译器发现有利的话,它仍然可以自由地为两者选择相同的寄存器(在其他不合适的设置中,您可以使用 & modifier)抑制它。

  • 尽可能使用内联函数而不是宏。

请注意,如果您将 MASK 的值更改为不可编码为位掩码立即数的值,例如#define MASK (4UL << 60 | 1),你会得到一个编译错误。如果您想让它继续工作,您可以将 L 约束更改为 Lr。如果表达式 MASK 是一个编译时常量,其值适合位掩码立即数,编译器将继续发出立即数。如果不是,它将发出额外的代码以将值加载到寄存器中,然后发出带有寄存器操作数的 orrTry it.

(当然,这个例子对于内联 asm 来说是愚蠢的。GCC 生成 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 可能适用。)