GCC内联汇编中`+&`修饰符的含义是什么?
+&r修饰符的疑问 背景知识
在GCC内联汇编中,默认情况下编译器会假设所有输入操作数在写入任何输出操作数前已被消耗。如果需要在消耗完所有输入前写入输出操作数,必须指定early-clobber(&修饰符),避免该寄存器被输入操作数复用。
疑问来源:GCC官方文档示例
我在GCC官方参考文档中看到如下示例:
void dscal (size_t n, double *x, double alpha) { asm ("/* lots of asm here */" : "+m" (*(double (*)[n]) x), "+&r" (n), "+b" (x) // <-- There's the "+&r" (n) : "d" (alpha), "b" (32), "b" (48), "b" (64), "b" (80), "b" (96), "b" (112) : "cr0", "vs32","vs33","vs34","vs35","vs36","vs37","vs38","vs39", "vs40","vs41","vs42","vs43","vs44","vs45","vs46","vs47"); }
这里的疑问是:为什么要对一个读写操作数(+r类型)使用early-clobber修饰符,写成+&r?这操作数本身就是同一个寄存器,文档对此没有给出解释。
GCC文档相关描述
我进一步查阅GCC修饰符文档,看到两段相关内容:
An operand which is read by the instruction can be tied to an earlyclobber operand if its only use as an input occurs before the early result is written. Adding alternatives of this form often allows GCC to produce better code when only some of the read operands can be affected by the earlyclobber. See, for example, the ‘mulsi3’ insn of the ARM.
Furthermore, if the earlyclobber operand is also a read/write operand, then that operand is written only after it’s used.
最后一段专门描述了+&r的情况,但我无法理解其中“used”的具体所指。
Linux内核中的实际案例
我在Linux内核中执行grep -r '+&'后发现使用极少,仅在x86架构的arch/x86/crypto/curve25519-x86_64.c文件中找到一处:
/* Computes the addition of four-element f1 with value in f2 * and returns the carry (if any) */ static inline u64 add_scalar(u64 *out, const u64 *f1, u64 f2) { u64 carry_r; asm volatile( /* Clear registers to propagate the carry bit */ " xor %%r8d, %%r8d;" " xor %%r9d, %%r9d;" " xor %%r10d, %%r10d;" " xor %%r11d, %%r11d;" " xor %k1, %k1;" /* Begin addition chain */ " addq 0(%3), %0;" " movq %0, 0(%2);" " adcxq 8(%3), %%r8;" " movq %%r8, 8(%2);" " adcxq 16(%3), %%r9;" " movq %%r9, 16(%2);" " adcxq 24(%3), %%r10;" " movq %%r10, 24(%2);" /* Return the carry bit in a register */ " adcx %%r11, %1;" : "+&r"(f2), "=&r"(carry_r) : "r"(out), "r"(f1) : "%r8", "%r9", "%r10", "%r11", "memory", "cc"); return carry_r; }
我实在搞不懂,这里为什么用+r不够,必须要使用+&r?
内容的提问来源于stack exchange,提问作者ChristmasTree

