为什么我的“ = r”(var)输出没有选择与“ a”(var)输入相同的寄存器?

St.*_*rio 0 c x86 assembly gcc inline-assembly

我正在学习如何__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
}
Run Code Online (Sandbox Code Playgroud)

它按预期工作。现在考虑以下情况:

情况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 
}
Run Code Online (Sandbox Code Playgroud)

我检查了生成的程序集并注意到以下内容:

I.在两种情况下,功能代码都是相同的,看起来像

   0x0000555555554760 <+0>:     mov    rax,rsi
   0x0000555555554763 <+3>:     lock cmpxchg QWORD PTR [rdi],rdx
   0x0000555555554768 <+8>:     ret 
Run Code Online (Sandbox Code Playgroud)

二。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
Run Code Online (Sandbox Code Playgroud)

问:为什么会出现替代raxa用任意寄存器() r)产生错误的结果?我希望这在两种情况下都能奏效?

UPD。我用以下标志编译-Wl,-z,lazy -Warray-bounds -Wextra -Wall -g3 -O3

int*_*jay 5

cmpxchg指令总是把结果在rax寄存器中。因此,您需要使用a约束条件来告知GCC从该寄存器中移出。在情况2中,您通过使用告诉GCC使用任意寄存器,而不是r在该寄存器中放置任何内容。

如果要使用r,则必须添加一条mov指令以将结果从rax移至该寄存器(movq %%rax, %0)。您还必须告诉GCC指令更改了rax寄存器,例如,将其添加到asm语句的“ clobbers”部分。对于您的情况,没有理由以这种方式使事情复杂化。