在进入内联汇编之前,如何确定通用寄存器没有被其他人使用?

How can I be sure a general purpose register is not being used by others before entering a inline assembly?

下面是一段GCC内联汇编代码。它以原子方式将输入 *Value 增加 1。并且 return 增加的值。

在汇编代码中,它使用了EAX寄存器。在调用此函数之前,我怎么知道 EAX 没有被其他人使用?如果正在使用,汇编代码会损坏一些东西。

UINT32
__cdecl
AtomicIncrement (
  IN      volatile UINT32    *Value
  )
{
  UINT32  Result;

  __asm__ __volatile__ (
    "movl    , %%eax  \n\t" ; <============== HERE, EAX is being modified.
    "lock               \n\t"
    "xadd    %%eax, %2  \n\t"
    "inc     %%eax          "
    : "=a" (Result),          // %0
      "=m" (*Value)           // %1
    : "m"  (*Value)           // %2
    : "memory",
      "cc"
    );

  return Result;
}

我从 here 中读到:

...Integer values and memory addresses are returned in the EAX register... (the cdecl call convention)

那么这是否意味着如果我遵循 cdecl 调用约定,编译器将确保 EAX 在输入时可以 安全使用 组装功能?如果我使用其他一些通用寄存器,比如说 EBX,我 必须 把它放在 clobber section? IE。在最后一个冒号之后?

您正在使用 "=a" 输出操作数,因此编译器知道您的 asm 写入该寄存器。

它会做出相应的计划,在你的 asm 运行后使用不同的寄存器来存放它想要的任何东西。是否向编译器准确描述你的 asm 取决于你,否则它就是一个黑盒子。

函数调用约定与内联汇编的相关性基本上为零。 此函数内联后,它将位于某个较大函数的中间。


使用内联 asm 而不是 GNU C 内置 __atomic_add_fetch 执行此操作没有任何好处,它将编译为 lock xaddlock add,具体取决于是否使用结果. https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html.

如果您坚持使用内联汇编,请对两个 input/output 操作数和 使用 "+r""+m" 约束在 asm 模板中有 lock xadd。让编译器完成剩下的工作,所以用 C 语言编写(在 asm 之前将寄存器输入设置为 1,然后递增)。这让编译器可以根据需要将 ++ 优化为稍后的操作。 GCC 知道如何将 1 放入寄存器,以及如何递增。

当然,gcc 也知道如何使用 locked 指令,这就是为什么你应该让它通过使用内置函数或 C++11 std::atomic.