在 32 位 ARM 汇编中有几个指令可用于原子加载和存储一对寄存器:
这些32位指令对选择传输寄存器对(Rt和Rt2)有一些要求:
我已经包含了一些示例 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 arm 内联汇编约束能够以某种方式描述自动寄存器映射的依赖寄存器对,如果这是单个指令所需要的。
我的问题是,对两个传输寄存器的要求怎么描述为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]
对于此类未记录甚至未记录的内容,您可以“窥视引擎盖下”并查看 GCC 内部如何在 config/arm/sync.md 中描述这些指令.
事实证明,绑定一个 DImode(64 位)操作数足以得到奇偶寄存器对。在 C 中,您可以绑定一个
uint64_t
变量并使用 H
修饰符拼出第二个寄存器(GCC 端没有记录 Arm 的修饰符,但是 LLVM 记录了它们):
uint64_t f(uint64_t *p)
{
uint64_t r;
asm volatile("ldaexd %0, %H0, [%1]"
: "=r"(r)
: "r"(p)
: "memory");
return r;
}