1

我正在尝试以下内联汇编的示例:http: //www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html 但是有些事情让我对破坏感到困惑:

  • 关于 clobber 的行为
    Clobbering 本质上是告诉 GCC 不信任指定寄存器/内存中的值。

    “好吧,当 GCC 可以准确地知道你在之前和之后对寄存器做了什么时,它真的很有帮助......它甚至足够聪明地知道如果你告诉它把 (x+1) 放在一个寄存器中,那么如果你不破坏它,然后 C 代码引用 (x+1),并且它能够保持该寄存器空闲,它将重用计算。唷。”

这一段是否意味着破坏将禁用常见的子表达式消除?

  • 教程中关于clobber列表的一些不一致之处:
    对于输入/输出列表中指定的寄存器,不需要像GCC所知的那样将它们放入clobber列表中;但是在关于 rep_movsl(或 rep_stosl)的示例中:

    asm ("cld\n\t" "rep\n\t" "stosl" : /* 没有输出寄存器 */ : "c" (count), "a" (fill_value), "D" (dest) : " %ecx", "%edi" );

尽管“S,D,c”在输出操作数中,但它们再次被列为clobbered。我在 C 中尝试了一个简单的片段:

#include<stdio.h>
int main()
{
  int a[] = {2, 4, 6};
  int b[3];
  int n = 3;
  int v = 12;
  asm ("cld\n\t"
       "rep\n\t"
       "movsl"
       :
       : "S" (a), "D" (b), "c" (n)
       : );
//     : "%ecx", "%esi", "%edi" );
  printf("%d\n", b[1]);
}

如果我使用注释掉的clobber 列表,GCC 会抱怨:

ac:8:3: 错误: 在重新加载 'asm' 时找不到类 'CREG' 中的寄存器 ac:8:3: 错误: 'asm' 操作数有不可能的约束

如果我使用空的clobber列表,它将编译并且输出为4。

4

1 回答 1

6

您引用的文件似乎非常不准确。以下是 asm 操作数约束对 GCC 的实际意义:

  • 输入:汇编操作从此操作数中读取。GCC 假设所有的读取在汇编操作的最开始同时发生
  • 输出:汇编操作写入该操作数;完成后,关联变量将具有有意义的值。(没有办法告诉 GCC 那个值是什么。) GCC 假设所有的写操作都在汇编操作的最后同时发生。
  • Clobber:组装操作会破坏此操作数中任何有意义的值。像写入一样,所有的破坏都被假定在操作结束时同时发生
  • Earlyclobber:与clobber相同,只是它发生在操作开始时。

此外,当前的(GCC 4.7)手册包括以下关键段落:

您不能以与输入或输出操作数重叠的方式编写clobber 描述。例如,如果您在 clobber 列表中提及该寄存器,则您可能没有描述具有一个成员的寄存器类的操作数。声明为存在于特定寄存器中的变量(参见 Explicit Reg Vars),并用作 asm 输入或输出操作数,不得在 clobber 描述中提及任何部分。您无法在不将输入操作数指定为输出操作数的情况下指定修改输入操作数。请注意,如果您指定的所有输出操作数都用于此目的(因此未使用),那么您还需要为 asm 构造指定 volatile,如下所述,以防止 GCC 将 asm 语句删除为未使用。

这就是为什么尝试输入和破坏某些寄存器对您来说失败的原因。

现在,插入 rep movsl 现在有点傻 - 只需使用memcpy并让 GCC 用适合您的最佳指令序列替换它 - 但是编写示例的正确方法是

int main()
{
  int a[] = {2, 4, 6};
  int b[3];
  int n = 3;
  int v = 12;

  int *ap = a, *bp = b;

  asm volatile ("rep movsl" : "+S" (ap), "+D" (bp), "+c" (n) : : "memory");
  printf("%d\n", b[1]);
}

您需要 theapbp中间变量,因为数组的地址不是左值,因此它不能出现在输出约束中。“+r”符号告诉 GCC,这个寄存器既是输入也是输出。'volatile' 是必要的,因为在 之后所有输出操作数都未使用asm,因此 GCC 否则会愉快地删除它(理论上它只是因为它对输出操作数所做的事情而存在)。将“内存”放入 clobber 列表是告诉 GCC 操作修改了内存的方式。最后,进行微优化:GCC 永远不会发布“std”,因此您不需要“cld”(这实际上是由 x86 ABI 保证的)。

我所做的大部分更改都不会影响像这样的小型测试程序是否正确运行;但是,它们对于全尺寸程序来说都是必不可少的,以防止细微的优化错误。例如,如果您遗漏了“内存”破坏者,GCC 将有权b[1]asm!

于 2012-11-06T20:43:51.843 回答