gcc内联汇编的奇怪行为?

时间:2015-03-08 07:42:38

标签: c gcc 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?
许多人。 :)

2 个答案:

答案 0 :(得分:3)

变量add标出值为5,*mem1开头。

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

lock xadd %eax, (%edx)

GCC必须使用eax,因为您的约束表明%0应使用%eax。您的约束也会将%eax与变量add联系起来。我相信GCC对我们任何其他操作数所需的寄存器都是免费的(在我的测试中它碰巧使用了%edx)。

所以:

  • %eax5开头,%edx指向值1
  • 的内存位置
  • xadd指令交换两个操作数并将总和放在目标中,因此执行%eax1the memory pointed to by%edx {{1 }} 6`

    您的约束还表示应将contains存储回变量%eax,以便add获得add。这就是函数返回的内容。

答案 1 :(得分:2)

在x86中, XADD Exchange and Add指令。因此,在add指令之后,保存1参数的寄存器变为lock xaddadd会返回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