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”用于什么?能否完全解释一下所有代码?谢谢!
答案 0 :(得分:4)
您需要了解gcc扩展内联asm格式:
"=&c"
将d0与ecx寄存器相关联,并将其标记为只写。 &
表示可以在代码结束之前修改"=&D"
的含义相同
"0" (n)
将n与第一个提到的注册表联系起来。在您的情况下,使用ecx "a" (c)
将c与eax联系起来"1" (s)
与edi 所以你有它。重复这个ecx次(n次):将eax(c)存储到edi(s)然后递增它。
那么,为什么未使用的d0
和d1
?我不确定。我也认为在这种情况下它们没用,并且整个输出部分可以留空但我认为不可能在输入约束中指定“可写”和“早期 -clobbered”。所以我认为d0
和d1
可以使&
成为可能。
我会尝试这样写:
asm volatile (
"rep\n"
"stosb\n"
:
: "c" (n), "a" (c), "D" (s)
: "%ecx", "%edi", "memory"
);
答案 1 :(得分:0)
什么是“d0”和“d1”用于?
实际上,它表示%ecx
,%edi
(假设为32位)的最终值分别存储在d0
,d1
中。这有几个目的:
它让编译器知道,作为输出,这些寄存器被有效地破坏了。通过将它们分配给临时变量,优化编译器也知道不需要实际执行“存储”操作。
“=&”将这些指定为 early-clobber 操作数。可以在消耗所有输入之前写入它们。因此,如果编译器可以自由选择输入寄存器,则它不应该将这两个别名。
这对于%ecx
来说在技术上并不是必需的,因为它明确地命名为输入:"0" (n)
- 在这种情况下是'rep'计数。我不确定%edi
是否有必要,因为在输入"1" (s)
之前无法更新,并且执行了指令。再次,因为它的显式被命名为输入,编译器不能自由选择另一个寄存器。简而言之,“=&”这里没有受伤,但它没有做任何事情。
由于"a" (c)
指定仅输入寄存器%eax
设置为(c)
,编译器可能会认为%eax
仍然保留此值'asm' - "rep; stosb;"
确实如此。
"memory"
指定可以以编译器未知的方式修改内存 - 在这种情况下是正确的,它将(n)
字节从(r)
开始设置为值{{1假设方向标志被清除,它应该是。这确实具有强制重载值的效果,因为编译器不能假设寄存器反映了它们应该再次存储的值。它没有伤害,可能有必要使一般情况(c)
安全,但它往往是矫枉过正。
编辑:输入操作数可能与clobber操作数不重叠。将某些内容指定为仅输入 和 clobbered 是没有意义的。我不认为编译器允许这样做,即使它使用了模糊规范也是不明智的。从手册:
您不能以与输入或输出操作数重叠的方式编写clobber描述。例如,如果您在clobber列表中提到该寄存器,则可能没有描述具有一个成员的寄存器类的操作数。