我正在学习如何__asm__ volatile
在GCC中使用,并提出了一个问题。我想实现一个执行原子比较和交换并返回先前存储在目标中的值的函数。
为什么"=a"(expected)
输出约束起作用,但是"=r"(expected)
约束使编译器生成不起作用的代码?
情况1。
#include#include #include uint64_t atomic_cas(uint64_t * destination, uint64_t expected, uint64_t value){ __asm__ volatile ( "lock cmpxchgq %3, %1": "=a" (expected) : "m" (*destination), "a" (expected), "r" (value) : "memory" ); return expected; } int main(void){ uint64_t v1 = 10; uint64_t result = atomic_cas(&v1, 10, 5); printf("%" PRIu64 "\n", result); //prints 10, the value before, OK printf("%" PRIu64 "\n", v1); //prints 5, the new value, OK }
它按预期工作。现在考虑以下情况:
情况2
#include#include #include uint64_t atomic_cas(uint64_t * destination, uint64_t expected, uint64_t value){ __asm__ volatile ( "lock cmpxchgq %3, %1": "=r" (expected) ://<----- I changed a with r and expected GCC understood it from the inputs "m" (*destination), "a" (expected), "r" (value) : "memory" ); return expected; } int main(void){ uint64_t v1 = 10; uint64_t result = atomic_cas(&v1, 10, 5); printf("%" PRIu64 "\n", result); //prints 5, wrong printf("%" PRIu64 "\n", v1); //prints 5, the new value, OK }
我检查了生成的程序集并注意到以下内容:
I.在两种情况下,功能代码都是相同的,看起来像
0x0000555555554760 <+0>: mov rax,rsi 0x0000555555554763 <+3>: lock cmpxchg QWORD PTR [rdi],rdx 0x0000555555554768 <+8>: ret
二。GCC内联时出现了问题,atomic_cas
因此在以后的情况下,正确的值没有传递给printf
函数。这是有关的片段disas main
:
0x00000000000005f6 <+38>: lock cmpxchg QWORD PTR [rsp],rdx 0x00000000000005fc <+44>: lea rsi,[rip+0x1f1] # 0x7f4 0x0000000000000603 <+51>: mov rdx,rax ; <-----This instruction is absent in the Case 2. 0x0000000000000606 <+54>: mov edi,0x1 0x000000000000060b <+59>: xor eax,eax
问:为什么会出现替代rax
(a
用任意寄存器() r
)产生错误的结果?我希望这在两种情况下都能奏效?
UPD。我用以下标志编译-Wl,-z,lazy -Warray-bounds -Wextra -Wall -g3 -O3
该cmpxchg
指令总是把结果在rax
寄存器中。因此,您需要使用a
约束条件来告知GCC从该寄存器中移出。在情况2中,您通过使用告诉GCC使用任意寄存器,而不是r
在该寄存器中放置任何内容。
如果要使用r
,则必须添加一条mov
指令以将结果从rax
移至该寄存器(movq %%rax, %0
)。您还必须告诉GCC指令更改了rax寄存器,例如,将其添加到asm
语句的“ clobbers”部分。对于您的情况,没有理由以这种方式使事情复杂化。