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 -S
或 https://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 上添加注释。
作为(初学者)练习,我想使用 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") 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 -S
或 https://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 上添加注释。