作为一个(初学者(练习,我想使用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;
其中$<X>$以及$<Y>$被("0","0"(,("0,"r"(,("r","r"(和("r",0"(代替。
("0"、"0"(不编译。(为什么?(
("0","r"(返回12,即预期结果。
("r","r"(和("r’,"0"(返回21,就好像什么都没发生一样。(为什么?(
那么,在这些情况下出了什么问题呢?为什么会失败?
您可以使用: "+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 -S
或https://godbolt.org),并查看它选择了哪些寄存器。如果它选择了相反的约束,您将看不到交换。
或者,对于"r"
,如果它选择了两个与输出操作数不同的寄存器,那么你就要修改它希望保持不变的寄存器,这是在踩编译器的脚(因为你告诉它这些是输入,可能与输出在不同的寄存器中。(所以不,"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/默认情况下会过滤注释,以便在NOP上放置注释。