x86-64 是否有类似于“i”约束的约束,但仅当操作数值适合 32 位有符号立即数时才匹配?
对于下面所示的函数,我希望 gcc 在操作数适合 32 位有符号立即数时使用
lock add mem, imm
,并且我希望它使用“r”约束并在立即数不适合时生成 mov r, imm; lock add mem, r
不适合。
当
v
是非常量值或适合有符号 32 位立即数的常量时,所示代码可以正常工作,但当与不适合有符号 32 位立即数的常量值一起使用时,gcc 会生成无效指令32 位立即数操作数。*
static inline void atomic_add(volatile unsigned long *m, unsigned long v)
{
asm volatile ("lock addq %1, %0" : "+m"(*m) : "ri"(v));
}
我尝试在约束中使用“n”而不是“i”,但它的工作原理似乎与“i”相同。 删除“i”约束在所有情况下都有效,但即使没有必要,它也会将立即数移至寄存器中。由于绝大多数用途都有适合 8 或 32 位的常量,因此我宁愿不使用该解决方案。
这是演示该问题的示例:https://godbolt.org/z/nPY46Kfdh
extern unsigned long x;
unsigned long m(volatile unsigned long *v)
{
atomic_add(v, 12ul);
atomic_add(v, 12345ul);
atomic_add(v, 123456789000ul);
atomic_add(v, x);
return *v;
}
* 这里有很多答案解释了为什么 add 指令中不允许使用 64 位立即数,因此不需要再解释为什么不支持它。
将评论变成答案...
查看 x86 系列的机器限制(向下滚动 way、way),我们看到:
e 32-bit signed integer constant, or a symbolic reference known to fit that range (for immediate operands in sign-extending x86-64 instructions).
这似乎符合您的要求。
而且,Peter 和我似乎都属于“不要使用内联汇编”的思想流派。因此,只要有可能,我建议使用内在函数而不是 asm 块。在这种情况下,可能是 __atomic_fetch_add(m, v, __ATOMIC_SEQ_CST)
。
最后一个想法:我注意到你没有在 asm 中使用内存破坏器。根据您使用此例程的方式,您可能会在此处引入计时错误。您可能需要快速检查一下,以确保它按照您的预期进行。