gcc 内联汇编的奇怪行为?

Strange behavior with gcc inline assembly?

我是 gcc 内联汇编的新手。 为什么此代码输出“1”而不是“5”?

代码:

#include <stdio.h>
static inline int atomic_add(volatile int *mem, int add)
{
    asm volatile(
            "lock xadd %0, (%1);"
            : "=a"(add)
            : "r"(mem), "a"(add)
            : "memory"
    );
    return add;
}

int main(void)
{
    int a=1;
    int b=5;
    printf ( "%d\n", atomic_add(&a, b) );
    return 0;
}

运行:

$ ./a.out
1  # why not 5?

非常感谢。 :)

变量 add 以值 5 开始,*mem1 开始。

lock xadd %0, (%1) 程序集模板被 gcc 编译为:

lock xadd %eax, (%edx)

GCC 必须使用 eax,因为您的约束表明 %0 应该使用 %eax。您的约束还将 %eax 与变量 add 联系起来。我相信 GCC 可以免费为我们提供任何它想要的其他操作数的寄存器(在我的测试中它碰巧使用 %edx)。

所以:

  • %eax5 开头,并且 %edx 指向具有值 1
  • 的内存位置
  • xadd 指令交换两个操作数并将和放在目标中,因此执行 %eax 后有 1the memory pointed to by% edxcontains6`

    您的约束还表明 %eax 应该存储回变量 add,因此 add 得到 1。这就是函数返回的内容。

在x86中,XADDExchange and Add指令。所以保存add参数的寄存器在lock xadd指令后变成了1add 然后由 atomic_add() 返回,因此您看到打印的是 1 而不是 5.

对于 atomic_add() 你可能只想使用 lock add 而不是 lock xadd:

#include <stdio.h>
static inline int atomic_add(volatile int *mem, int add)
{
    asm volatile(
            "lock add %0, (%1);"
            : "=a"(add)
            : "r"(mem), "a"(add)
            : "memory"
    );
    return add;
}

int main(void)
{
    int a=1;
    int b=5;
    printf ( "%d\n", atomic_add(&a, b) );
    return 0;
}

这会像您期望的那样打印 5

$ ./a.out
5