【发布时间】:2019-10-04 15:06:24
【问题描述】:
我正在学习如何在 GCC 中使用 __asm__ volatile 并遇到了一个问题。我想实现一个执行原子比较和交换并返回先前存储在目标中的值的函数。
为什么"=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
问题: 为什么用任意寄存器 (r) 替换 rax(a) 会产生错误结果?我预计它在这两种情况下都有效?
UPD。我使用以下标志编译-Wl,-z,lazy -Warray-bounds -Wextra -Wall -g3 -O3
【问题讨论】:
-
它只是恢复使用的寄存器。它究竟是如何不起作用的?你传递了什么编译器标志?
-
@JL2210 第二种情况不会返回在 CAS 之前存储的正确值。它返回存储的值。最让我困惑的是,函数汇编是一样的,但是inline却导致结果不一样。
-
您使用的是什么版本的 GCC?我无法重现该程序集。
-
10与11的区别是故意的吗? -
我使用了
-fPIC -pie。大概就是这样。
标签: c gcc assembly x86 inline-assembly