GCC内联汇编:约束

我很难理解角色约束在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的“m”约束而不是“0”。 这有一个奇怪的副作用,如果我用优化标志编译并调用函数两次,由于某种原因,加法运算的结果也会存储在c 。 我最终读到了“ 匹配约束 ”,它允许您指定变量将用作输入和输出操作数。 当我将"m"(b)改为"0"(b)它起作用了。

但我真的不明白为什么你会使用一个约束而不是另一个约束。 我是说是的,我明白“r”意味着变量应该在寄存器中,“m”意味着它应该在内存中 – 但我真的不明白选择一个是另一个的意义是什么,或者为什么添加如果我选择某种约束组合,则操作无法正常工作。

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

这里有一个例子可以更好地说明为什么你应该仔细选择约束(与你的function相同,但也许更简洁一点):

 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表示只能选择eaxecxedxebx 。 这是因为set*指令必须写入8位可寻址寄存器( alah ,…)。 在%b0使用%b0表示使用最低的8位部分( alcl ,…)。
  • 对于大多数双操作数指令,至少有一个操作数必须是寄存器。 所以不要两者都使用mg ; 将r用于至少一个操作数。
  • 对于最后的操作数,无论是寄存器还是内存都没关系,所以使用g (通用)。

在上面的例子中,我选择使用g (而不是r )作为因为引用通常被实现为内存指针,因此使用r约束将需要首先将引用对象复制到寄存器,然后再复制回来。 使用g ,可以直接更新指示对象。


至于为什么你的原始版本用附加值覆盖你的c ,那是因为你在输出槽中指定了=m ,而不是(比方说) +m ; 这意味着允许编译器为输入和输出重用相同的内存位置。

在您的情况下,这意味着两个结果(因为相同的内存位置用于bc ):

  • 添加没有溢出:然后, cb的值覆盖(添加的结果)。
  • 添加确实溢出:然后, c变为1(并且b也可能变为1,具体取决于代码的生成方式)。