2010-10-10 336 views
15

我很难理解在GCC内联汇编(x86)中扮演的角色约束。我有read the manual,这正好解释了每个约束的作用。问题是,即使我理解每个约束所做的事情,但我很少理解为什么你会使用一个约束而不是另一个约束,或者可能会产生什么影响。GCC内联汇编:约束

我意识到这是一个非常广泛的话题,所以一个小例子应该有助于缩小焦点。以下是一个简单的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 

注意,而不是“0”时,我用b一个“M”的约束。这有一个奇怪的副作用,如果我使用优化标志进行编译并调用了两次函数,出于某种原因,加法操作的结果也将被存储在c中。我最终读到了“matching constraints”,它允许你指定一个变量被用作输入和输出操作数。当我将"m"(b)更改为"0"(b)时,它工作正常。

但我不明白为什么你会使用另一个约束。我的意思是说,我知道“r”表示变量应该在一个寄存器中,“m”表示它应该在内存中 - 但我不知道什么是选择一个而不是另一个的含义,或者是为什么如果我选择特定的约束组合,则添加操作无法正常工作。

问题:1)在上面的示例代码中,为什么b上的“m”约束会导致c被写入? 2)是否有任何教程或在线资源更详细地讨论约束条件?

回答

13

下面是一个例子,以更好地说明为什么你应该仔细选择的约束(相同的功能,你,但也许写多了几分简洁):

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; 
} 

所以,使用的制约因素:qrg

  • q意味着只有eaxecxedx,或ebx可以选择。这是因为set*指令必须写入一个8位寻址寄存器(alah,...)。在%b0中使用b表示使用最低的8位部分(al,cl,...)。
  • 对于大多数双操作数指令,至少有一个操作数必须是寄存器。因此,不要使用mg;对于至少一个操作数使用r
  • 对于最终的操作数,无论是寄存器还是内存都没关系,所以使用g(general)。

在上面的例子中,我选择使用g(而不是r),用于a因为引用通常作为存储器指针来实现,所以使用r约束将要求第一复制指涉到寄存器,然后复制回来。使用g,所指对象可以直接更新。


至于为什么你原来的版本中添加的值改写你的c,那是因为你在输出插槽指定=m,而不是(说)+m;这意味着编译器可以重复使用相同的内存位置进行输入和输出。

在你的情况,这意味着两种结果(因为在对bc相同的内存位置):

  • 的加入并没有溢出:那么,c得到了改写的b值(添加的结果)。
  • 加法确实溢出:然后,c变成1(并且b也可能变成1,这取决于代码的生成方式)。
+0

谢谢 - 这是一个很好的答案。只需要澄清一下:即使'b'和'c'是内存中不同位置的不同变量,为什么'='(只写)约束修饰符赋予编译器重用相同内存位置的权利? – Channel72 2010-10-10 03:06:01

+0

@ Channel72:“即使'b'和'c'是在记忆中不同位置的不同变量” - 这实际上是一个主要假设,通常不适用。如果'b'和'c'是局部变量,那么很可能它们都是由寄存器实际支持的,而不是内存位置。在这种情况下,内存位置仅仅是一个临时保存位置,它的设置纯粹是为了适应您的'm'约束 - 在这种情况下,'b'和'c'可以很好地使用相同的临时位置。 – 2010-10-10 03:17:21

+0

现在,如果'b'和'c'实际上都是由内存位置真正支持的,那么你就是对的,通常它们不应该重叠。而且,如果一个由内存支持,另一个由注册支持......那么这两种情况都是可能的。 – 2010-10-10 03:19:22