我正在学习如何使用 __asm__ volatile
在 GCC 中遇到了一个问题。我想实现一个执行原子比较和交换并返回先前存储在目标中的值的函数。
为什么 "=a"(expected)
输出约束起作用,而 "=r"(expected)
约束让编译器生成的代码不起作用工作?
案例 1.
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
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 <inttypes.h>
#include <stdint.h>
#include <stdio.h>
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
}
我检查了生成的程序集并注意到以下几点:
我。在这两种情况下,功能代码都是相同的,看起来像
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
) 产生错误的结果?我希望它在这两种情况下都有效?
更新。我使用以下标志编译 -Wl,-z,lazy -Warray-bounds -Wextra -Wall -g3 -O3
最佳答案
cmpxchg
指令总是将结果放入rax
寄存器。所以你需要使用 a
约束来告诉 GCC 从那个寄存器中移动。在情况 2 中,您通过使用 r
告诉 GCC 使用任意寄存器,但您没有在该寄存器中放置任何内容。
如果你想使用r
,你必须添加一个mov
指令来将结果从rax
移动到那个寄存器( movq %%rax, %0
).您还必须告诉 GCC rax 寄存器已被指令更改,例如将其添加到 asm
语句的“clobbers”部分。对于您的情况,没有理由以这种方式使事情复杂化。
关于c - 为什么我的 "=r"(var) 输出没有选择与 "a"(var) 输入相同的寄存器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58238986/