我正在尝试为 GCC 编写内联 x86-64 程序集以有效地使用 MULQ 指令。MULQ 将 64 位寄存器 RAX 与另一个 64 位值相乘。另一个值可以是任何 64 位寄存器(甚至是 RAX)或内存中的值。MULQ 将乘积的高 64 位放入 RDX,将低 64 位放入 RAX。
现在,很容易将正确的 mulq 表达为内联汇编:
#include <stdint.h>
static inline void mulq(uint64_t *high, uint64_t *low, uint64_t x, uint64_t y)
{
asm ("mulq %[y]"
: "=d" (*high), "=a" (*low)
: "a" (x), [y] "rm" (y)
);
}
这段代码是正确的,但不是最优的。MULQ 是可交换的,所以如果y
碰巧已经在 RAX 中,那么离开y
它所在的位置并进行乘法是正确的。但是 GCC 不知道这一点,所以它会发出额外的指令来将操作数移动到它们预定义的位置。我想告诉 GCC,它可以将任一输入放在任一位置,只要一个以 RAX 结尾并且 MULQ 引用另一个位置。GCC 对此有一个语法,称为“多重替代约束”。请注意逗号(但整个 asm() 已损坏;见下文):
asm ("mulq %[y]"
: "=d,d" (*high), "=a,a" (*low)
: "a,rm" (x), [y] "rm,a" (y)
);
不幸的是,这是错误的。如果 GCC 选择第二个替代约束,它将发出“mulq %rax”。为了清楚起见,考虑这个函数:
uint64_t f()
{
uint64_t high, low;
uint64_t rax;
asm("or %0,%0": "=a" (rax));
mulq(&high, &low, 7, rax);
return high;
}
用 编译gcc -O3 -c -fkeep-inline-functions mulq.c
,GCC 发出这个程序集:
0000000000000010 <f>:
10: or %rax,%rax
13: mov $0x7,%edx
18: mul %rax
1b: mov %rdx,%rax
1e: retq
“mul %rax”应该是“mul %rdx”。
如何重写这个内联汇编,以便在每种情况下都生成正确的输出?