rep_movsl的Clobber列表

Oxd*_*eef 1 c x86 assembly gcc inline-assembly

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

  • 关于clobber的行为
    Clobbering实质上告诉GCC不信任指定寄存器/存储器中的值.

    "好吧,当GCC能够确切地知道你之前和之后对寄存器做了什么时,这确实很有帮助......如果你告诉它将(x + 1)放入寄存器中,它甚至足够聪明. ,如果你没有破坏它,后来C代码引用(x + 1),并且它能够保持该寄存器空闲,它将重用计算.哇."

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

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

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

虽然"S,D,c"在输出操作数中,但它们再次被列为破坏.我在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]);
}
Run Code Online (Sandbox Code Playgroud)

如果我使用评论的clobber列表,GCC会抱怨:

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

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

zwo*_*wol 6

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

  • 输入:汇编操作从此操作数读取.GCC假设所有读取在组装操作的最初阶段同时发生.
  • 输出:汇编操作写入此操作数; 完成后,关联变量将具有有意义的值.(有没有办法告诉GCC该值是什么.)GCC假定所有的写操作发生的同时在最末端的组装操作.
  • Clobber:汇编操作会破坏此操作数中的任何有意义的值.就像写操作,则会覆盖所有假设发生的同时年底的操作.
  • Earlyclobber:与clobber相同,只是它在操作开始时发生.

此外,目前(GCC 4.7)手册包括这一关键段落:

您不能以与输入或输出操作数重叠的方式编写clobber描述.例如,如果在clobber列表中提及该寄存器,则可能没有描述具有一个成员的寄存器类的操作数.声明存在于特定寄存器中的变量(请参阅显式寄存器变量),并用作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]);
}
Run Code Online (Sandbox Code Playgroud)

您需要apbp中间变量,因为数组的地址不是左值,因此它不能出现在输出约束中."+ r"符号告诉GCC该寄存器既是输入又是输出.'volatile'是必要的,因为所有的输出操作数在之后都没有被使用asm,所以GCC否则会高兴地删除它(理论上它只是它对输出操作数的作用).将"内存"放在clobber列表中就是告诉GCC操作修改内存的方法.最后,一个微优化:GCC永远不会发出'std',所以你不需要'cld'(这实际上是由x86 ABI保证的).

我所做的大多数更改都不会影响像这样的微小测试程序是否正常运行; 但是,它们在全尺寸程序中都是必不可少的,以防止出现细微的优化错误.例如,如果你遗漏了"记忆"的破坏,GCC将有权提升b[1]上面的负荷asm!