MULQ命令を効率的に使用するために、GCC用のインラインx86-64アセンブリを作成しようとしています。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が2番目の代替制約を選択した場合、「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」である必要があります。
このインラインasmをどのように書き直して、すべての場合に正しい出力を生成できるでしょうか。