【问题标题】:Return values and constraints for asm (assembler code) using gnu Extended Asm使用 gnu Extended Asm 的 asm(汇编代码)的返回值和约束
【发布时间】:2020-09-27 16:27:40
【问题描述】:

作为(初学者)练习,我想使用 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") 返回 12,即预期结果。

("r", "r") 和 ("r", "0") 返回 21,就好像什么都没发生一样。 (为什么?)

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

【问题讨论】:

  • 0,0 没有意义,你不能把两个操作数放在同一个地方。使用r,编译器可以选择它想要的任何寄存器,并且由于您从未在 asm 块中引用 0 或 1,因此不知道结果在哪里。 0,r 恰好起作用只是一个意外,它仍然是错误的。正确的约束是 1,0。
  • @Jester "0,1" 有效,"1,0" 无效。看来我的主要误解是将“0”视为某个固定关键字,而不是对“%0”的引用,对吗?如果是这样,请将其作为答案(也许对这里的其他人提供更多解释)。

标签: c assembly gcc inline-assembly


【解决方案1】:

您可以使用: "+r" (c), "+r" (b) 声明读/写操作数1 (https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html) 输入/输出指令进入第一组(输出)约束。

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

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

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


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

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


如果您确实想手动使用匹配约束,您当然会使用"0"(c), "1"(b) 强制它为c 选择相同的寄存器作为输入,它为c 选择作为输出#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/ 默认会过滤 cmets,因此可以方便地对 NOP 进行评论。

【讨论】:

  • 那么,如果将输出变量声明为“+r”,我就不需要将它们重复为输入变量了吗?它在第一次测试中有效,但我不确定这是否是偶然的。
  • @GyroGearloose:是的,查看手册,这就是+ 的全部意义所在。 gcc.gnu.org/onlinedocs/gcc/Modifiers.html
  • 谢谢,但是“意味着这个操作数既可以被指令读取也可以被指令写入。”也可以解释为那个寄存器是可读写的。我想这就是新手和老手的区别。
  • @GyroGearloose:是的,编译器选择一个寄存器,你的 asm 模板读取和写入它。如果您在写入之前读取,则该值是 C 变量在 asm 语句之前的值。 (与 "=r" 输出不同,它被假定为仅将其连接到 C 变量的值,就像做 var = ... 一样。您似乎已经知道这一点,因为您知道您需要输入约束。@987654342 @ 就像在做 var += ...var -= ...,一个 RMW)。如果你使用"=r"(foo),你仍然可以在你的 asm 模板中读取那个寄存器你写了一个值之后。
  • nop 版本 +1 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-13
  • 1970-01-01
  • 2010-12-01
  • 2019-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多