GCC 32位arm内联汇编约束,用于原子加载/存储寄存器对[重复]

kiz8lqtg  于 2023-05-07  发布在  其他
关注(0)|答案(1)|浏览(144)

此问题已在此处有答案

Using LDRD in GNU C inline asm? What constraints to use?(1个答案)
5天前关闭。
在32位ARM汇编中,有几条指令可用于原子加载和存储一对寄存器:

  • ldaexd和stlexd(用于ARMv 8 32位,具有获取-释放内存顺序)[https://developer.arm.com/documentation/dui0802/b/A32-and-T32-Instructions/LDAEX-and-STLEX]
  • ldrexd和strexd(适用于不含屏障的ARMv7)[https://developer.arm.com/documentation/dui0802/b/A32-and-T32-Instructions/LDREX-and-STREX]

这些32位指令对于选择传输寄存器对(Rt和Rt 2)具有一些要求:

  • Rt必须是偶数寄存器,而不是LR
  • “Rt 2必须是R(t+1)”

我已经包含了一些GCC内联汇编代码的例子(对于C/C++,下面描述的问题对于所有4条指令都是一样的)。此代码不符合要求的寄存器编号。

inline static void atomic_exclusive_load_pair_aquire(uint32_t atomic[2], uint32_t target[2])
{
    asm volatile("ldaexd %0, %1, [%2]"  // load-acquire exclusive register pair
                 : "=r"(target[0]),     // first transfer register
                   "=r"(target[1])      // second transfer register
                 : "r"(&atomic[0])      // atomic base register
                 : "memory");           // "memory" acts as compiler r/w barrier
}

我希望GCC臂内联汇编约束以某种方式能够描述用于自动寄存器Map的依赖寄存器对,如果这是单个指令所需要的话。
我的问题是,如何将两个传输寄存器的要求描述为GCC内联汇编约束,以自动选择正确的寄存器编号?这可能吗?使用“多种替代约束”是否是一种可能的解决方案([https://gcc.gnu.org/onlinedocs/gcc/Multi-Alternative.html])?

解决方案:

正如amonakov等人所写的,解决方案是使用uint64_t作为传输类型,它使用ARM 32位上的寄存器对。取决于Thumb是否禁用,寄存器对将是偶数/奇数对。也有或多或少的未记录的内联汇编约束访问对寄存器。

inline static void atomic_exclusive_load_pair_aquire(uint32_t atomic[2], uint32_t transfer[2])
{
    uint64_t pair;
    asm volatile("ldaexd %Q[pair], %R[pair], [%[addr]]"  // load-acquire exclusive register pair
                 : [pair] "=r"(pair)       // transfer register pair
                 : [addr] "r"(&atomic[0])  // atomic base register
                 :        "memory");       // "memory" acts as compiler r/w barrier

    transfer[0] = static_cast<uint32_t>(pair);
    transfer[1] = static_cast<uint32_t>(pair >> 32);
}

请在这里查看完整的解决方案和汇编代码:[https://godbolt.org/z/Mb5GYMfK5]

ikfrs5lh

ikfrs5lh1#

对于这些文档不足甚至没有文档的东西,您可以“窥视引擎盖下”,并查看GCC内部如何在config/arm/sync.md中描述这些指令。
事实证明,绑定一个DIMode(64位)操作数就足以获得一个奇偶寄存器对。在C中,你可以绑定一个uint64_t变量,并使用H修饰符来拼写第二个寄存器(Arm的修饰符在GCC端没有记录,但LLVM记录了它们):

uint64_t f(uint64_t *p)
{
    uint64_t r;
    asm volatile("ldaexd %0, %H0, [%1]"
                 : "=r"(r)
                 : "r"(p)
                 : "memory");
    return r;
}

相关问题