rep_movsl 的 Clobber 列表

标签 c gcc assembly x86 inline-assembly

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

  • 关于clobber的行为
    破坏实质上告诉 GCC 不要信任指定寄存器/内存中的值。

    "Well, it really helps when optimizing, when GCC can know exactly what you're doing with the registers before and after....It's even smart enough to know that if you tell it to put (x+1) in a register, then if you don't clobber it, and later C code refers to (x+1), and it was able to keep that register free, it will reuse the computation. Whew."

这一段是否意味着破坏将禁用公共(public)子表达式消除?

  • 教程中关于 clobber 列表的一些不一致之处:
    对于在输入/输出列表中指定的寄存器,没有必要将它们放入 GCC 知道的 clobber 列表中;然而,在有关 rep_movsl(或 rep_stosl)的示例中:

    asm ("cld\n\t" “代表\n\t” “斯托斯” :/* 没有输出寄存器 */ : "c"(count), "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]);
}

如果我使用注释过的 clobber 列表,GCC 会提示:

a.c:8:3: error: can't find a register in class ‘CREG’ while reloading ‘asm’ a.c:8:3: error: ‘asm’ operand has impossible constraints

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

最佳答案

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

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

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

You may not write a clobber description in a way that overlaps with an input or output operand. For example, you may not have an operand describing a register class with one member if you mention that register in the clobber list. Variables declared to live in specific registers (see Explicit Reg Vars), and used as asm input or output operands must have no part mentioned in the clobber description. There is no way for you to specify that an input operand is modified without also specifying it as an output operand. Note that if all the output operands you specify are for this purpose (and hence unused), you will then also need to specify volatile for the asm construct, as described below, to prevent GCC from deleting the asm statement as unused.

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

现在,现在插入 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]);
}

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

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

关于rep_movsl 的 Clobber 列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13258464/

相关文章:

c - 如何在 Linux 上从汇编代码中调用 c 库?

多个弱符号的后果(C 链接器)

c# - 将 C 输出转换为 C# 输入

debugging - 让 gcc 将相对文件名放入调试信息中

c - 海湾合作委员会 : undefined reference to 'function'

python - 如何在 python 中迭代文本文件中的行?

c - 伪终端不打印输出

linux - Intel Assembly 中的键盘缓冲区

assembly - 提高 CPU 流水线性能的 CMOV 是什么?

assembly - 这个只有一个操作数的 x86-64 addq 指令是什么意思? (摘自CSAPP书籍第三版)