C 内联asm未知

C 内联asm未知,c,gcc,assembly,inline-assembly,att,C,Gcc,Assembly,Inline Assembly,Att,“d0”和“d1”用于什么?你能完整地解释一下所有的代码吗?谢谢 您需要了解gcc扩展内联asm格式: 第一部分是实际装配。在这种情况下,只有2条说明 第二部分指定输出约束,第三部分指定输入约束。第四部分指定程序集将阻塞内存 输出 “=&c”将d0与ecx寄存器关联,并将其标记为仅写&表示可以在代码结束前对其进行修改 “=&D”对于edi寄存器来说意味着相同的事情 输入 “0”(n)将n与第一个提到的寄存器相关联。在您的情况下,使用ecx “a”(c)将c与eax关联 “1”(s)将s

“d0”和“d1”用于什么?你能完整地解释一下所有的代码吗?谢谢

您需要了解gcc扩展内联asm格式:

  • 第一部分是实际装配。在这种情况下,只有2条说明
  • 第二部分指定输出约束,第三部分指定输入约束。第四部分指定程序集将阻塞内存
输出
  • “=&c”
    将d0与ecx寄存器关联,并将其标记为仅写<代码>&表示可以在代码结束前对其进行修改
  • “=&D”
    对于edi寄存器来说意味着相同的事情
输入
  • “0”(n)
    将n与第一个提到的寄存器相关联。在您的情况下,使用ecx
  • “a”(c)
    将c与eax关联
  • “1”(s)
    将s与edi关联
装配 好了。重复此ecx次(n次):将eax(c)存储到edi中,然后递增


那么,为什么要使用未使用的
d0
d1
?我不确定。我也认为在这种情况下,它们是无用的,整个输出部分可以留空,但我认为不可能在输入约束中指定“writable”和“early-clobbed”。因此,我认为
d0
d1
是使
成为可能的

我会试着这样写:

static inline void *__memset(void *s, char c, size_t n) {
int d0, d1;
asm volatile (
    "rep; stosb;"
    : "=&c" (d0), "=&D" (d1)
    : "0" (n), "a" (c), "1" (s)
    : "memory");
return s;
}
“d0”和“d1”用于什么

实际上,它表示
%ecx
%edi
(假设为32位)的最终值分别存储在
d0
d1
中。这有两个目的:

它让编译器知道,作为输出,这些寄存器被有效地关闭。通过将它们分配给临时变量,优化编译器也知道不需要实际执行“存储”操作

“=&”将这些指定为早期碰撞操作数。在消耗所有输入之前,可以将其写入。因此,如果编译器可以自由选择输入寄存器,则不应将这两个寄存器别名

对于
%ecx
,这在技术上是不必要的,因为它被显式地命名为一个输入:
“0”(n)
-在这种情况下是“rep”计数。我也不确定
%edi是否有必要,因为在使用输入
“1”(s)
并执行指令之前,它无法更新。同样,由于它被显式命名为输入,编译器不能自由选择另一个寄存器。简言之,“=&”在这里不痛,但它没有任何作用

由于
“a”(c)
指定一个仅输入寄存器
%eax
设置为
(c)
,编译器可能会假定
%eax
在“asm”之后仍然保持该值,这与
的“rep;stosb;”
的情况确实相同

“memory”
指定可以以编译器不知道的方式修改内存-在本例中是这样的,它将
(n)
(r)
开始的字节设置为
(c)
-假设方向标志被清除,它应该被清除。这确实具有强制重新加载值的效果,因为编译器不能假设寄存器反映了它们应该再使用的内存值。它不会造成伤害,并且可能需要确保它在一般情况下的安全性
memset
,但这通常是过分的

编辑:输入操作数不能与缓冲操作数重叠。仅将某个内容指定为输入clobbered是没有意义的。我认为编译器不允许这样做,即使使用不明确的规范也不明智。从手册中:

不能以与输入或输出操作数重叠的方式写入clobber描述。例如,如果在clobber列表中提到一个寄存器,则可能没有一个操作数来描述一个具有一个成员的寄存器类



回顾一些旧的答案,我想我应该添加一个链接到优秀的GCC内联ASM教程。本文以前面的章节为基础,与gcc手册不同,gcc手册被最好地描述为“参考”,并不真正适合任何类型的结构化学习。

。我甚至看不出“c”是如何为代表stosb进入AL的,更不用说有趣的当地人了。只有习惯于使用3页bash脚本将一个文件复制到另一个文件的UNIX开发人员才能在这种语法下生存(好吧,好吧,也许还有COBOL程序员:)。描述GCC内联汇编程序语法。太可怕了。这就像是模糊汇编程序,好像汇编程序没有像现在这样模糊,('rep stosb'-非常清楚)…我现在有一个想法,输出部分是告诉编译器除了输出之外不要使用%ecx和%edi,这样它们才能保持正确。这是真的吗?@akirast,是的,尽管输出从未真正存储过。如果编译器想再次使用%ecx,%edi,它知道必须重新加载它们。@akirast,我应该澄清:我不希望它们被存储。如果优化关闭,它们可能会关闭。如果d0和d1可以通过调试进行检查,则可能是。谢谢!我想我需要努力了解到底发生了什么。不,不能在用作输入操作数的寄存器上声明缓冲区。这就是为什么代码使用伪输出操作数而不是clobber。(因为它在包装函数中,所以我只需要使用
“+c”
(c)和
“+D”(s)
约束来修改给定的c变量。)当所有操作数都有固定寄存器时,不需要提前关闭;如果编译器知道
a==n
并将它们放在同一个寄存器中,那么编译器就不会像
“+r”
“r”
那样选择错误。
asm volatile (
    "rep\n"
    "stosb\n"
    :
    : "c" (n), "a" (c), "D" (s)
    : "%ecx", "%edi", "memory"
);