我很难理解GCC内联程序集(x86)中的角色约束。我有阅读手册,它准确地解释了每个约束所做的事情。问题是,尽管我理解每个约束的作用,但对于为什么您会使用一个约束而不是另一个约束,或者可能会产生什么影响,我的理解非常有限。
我意识到这是一个非常广泛的话题,所以一个小的例子应该有助于缩小重点。下面是一个简单的asm例程,它只添加两个数字。如果发生整数溢出,则将值1写入输出C变量。
int32_t a = 10, b = 5;
int32_t c = 0; // overflow flag
__asm__
(
"addl %2,%3;" // Do a + b (the result goes into b)
"jno 0f;" // Jump ahead if an overflow occurred
"movl $1, %1;" // Copy 1 into c
"0:" // We're done.
:"=r"(b), "=m"(c) // Output list
:"r"(a), "0"(b) // Input list
);现在,这很好,除了我不得不任意摆弄约束,直到我使它正确工作。最初,我使用了以下约束:
:"=r"(b), "=m"(c) // Output list
:"r"(a), "m"(b) // Input list请注意,对于b,我使用的不是"0",而是"m“约束。这有一个奇怪的副作用,如果我用优化标志编译并两次调用函数,由于某种原因,添加操作的结果也会存储在c中。我最终读到了"匹配约束",它允许您指定变量同时用作输入和输出操作数。当我将"m"(b)改为"0"(b)时,它起了作用。
但我真的不明白为什么你会用一个约束来代替另一个约束。我的意思是,是的,我理解"r“表示变量应该在寄存器中,"m”表示应该在内存中--但我并不真正理解选择一个而不是另一个的含义,或者如果选择某种约束组合,加法操作为什么不能正确工作。
问题: 1)在上面的示例代码中,为什么b上的"m“约束会导致c被写入?
发布于 2010-10-10 02:15:04
下面是一个更好地说明为什么您应该谨慎选择约束的例子(与您的函数相同,但可能编写得更简洁一些):
bool add_and_check_overflow(int32_t& a, int32_t b)
{
bool result;
__asm__("addl %2, %1; seto %b0"
: "=q" (result), "+g" (a)
: "r" (b));
return result;
}因此,使用的约束是:q、r和g。
q意味着只能选择eax、ecx、edx或ebx。这是因为set*指令必须写入8位可寻址寄存器(al,ah,.)。b在%b0中的使用意味着,使用最低的8位部分(al,cl,.).m或g;至少对其中一个操作数使用r。g (general)。在上面的示例中,我选择使用g (而不是r)作为a,因为引用通常是作为内存指针实现的,因此使用r约束需要先将引用复制到寄存器,然后再复制回来。使用g,可以直接更新引用。
至于原始版本为什么用加法值覆盖c,这是因为您在输出槽中指定了=m,而不是(例如) +m;这意味着编译器可以重用相同的内存位置进行输入和输出。
在您的示例中,这意味着两种结果(因为b和c使用了相同的内存位置):
c被b的值覆盖(添加的结果)。c变成1( b也可能变成1,这取决于代码是如何生成的)。https://stackoverflow.com/questions/3898704
复制相似问题