Return 使用 gnu Extended Asm 的 asm(汇编代码)的值和约束

Return values and constraints for asm (assembler code) using gnu Extended Asm

作为(初学者)练习,我想使用 GNU asm 在 C 中实现交换操作。

我对这些限制感到困惑。我的代码是:

    int c = 1;
    int b = 2;
    asm ("xchg %2,%3"
            : "=r" (c), "=r" (b)
            : "<X>" (c), "<Y>" (b)
            );
    return c+10*b;

其中 $$ 和 $$ 替换为 ("0", "0"), ("0", "r"), ("r", "r") 和("r", 0").

("0", "0") 不编译。 (为什么?)

("0", "r") returns 12,预期结果。

("r", "r") and ("r", "0") return 21,好像什么都没发生过一样。 (为什么?)

那么在这些情况下有什么问题呢?为什么会失败?

您可以使用: "+r" (c), "+r" (b)声明read/write操作数1 (https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html) In/out指令放在第一个(输出)约束组。

当然你应该使用 xchg %0, %1 来确保 asm 模板只引用你实际声明的操作数。

asm ("xchg %0,%1"
            : "+r" (c), "+r" (b)
  );

脚注 1:有趣的事实:GCC 通过为这些输出发明匹配的输入约束来在内部实现 read/write 操作数。因此,如果您没有修复模板中的 %2,%3,它仍然可以编译。但是您无法保证 GCC 选择执行匹配约束的顺序。


要找出你的错误约束发生了什么,让你的 asm 模板打印未使用的操作数名称作为 asm 注释,例如xchg %2,%3 # inputs= %0,%1 因此您可以查看编译器的 asm 输出(gcc -Shttps://godbolt.org),并查看它选择了哪些寄存器。如果它选择了相反的约束,你将看不到交换。

或者 "r" 如果它选择了 2 个与输出操作数不同的寄存器,那么你通过修改它期望保持不变的寄存器来踩编译器的脚趾(因为你告诉它那些是输入,并且可能位于与输出不同的寄存器中。)所以不,"r" 不安全。


如果您确实想手动使用匹配约束,您当然会使用 "0"(c), "1"(b) 强制它为 c 选择相同的寄存器作为它为 [=20 选择的输入=] 作为输出 #0,对于 b 作为输入与输出 #1 相同。

当然,根本不需要运行时指令来告诉编译器 C 变量具有彼此的值。

asm ("nop  # c input in %2, output in %0.  b input in %3, output in %1"
     :  "=r" (c), "=r" (b)
     :  "1"(c),   "0"  (b)   // opposite matching constraints
  );

即使 nop 也不是必需的,但 https://godbolt.org/ 默认情况下会过滤注释,因此可以方便地在 NOP 上添加注释。