c-使用gnu扩展的asm返回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;

其中$<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上放置注释。

最新更新