ここでは、1 つの質問に 2 つの質問があります。まず、倍幅の入力または出力の問題があります。結果の上位半分を含む、完全に拡大する乗算を行う 1 オペランドのMUL / IMULフォームを無視しています: N * N => 2N ビット、実行中 EDX:EAX = EAX * src
. これが役立つ理由については、他の回答を参照してください。
BMI2では、3 つの明示的なオペランド (2 つの出力と 1 つの入力) と 1 つの暗黙的なオペランド (2 番目のソース = EDX) を持つ、より柔軟な完全乗算命令 MULX も導入されました。
imul
次に、 DIV/IDIV では使用できないもう 1 つの即値オペランドを使用する例を示します。
16 / 8 => 8 ではなく、8 ビット / imm8 => 8 ビットの商/剰余を実行する、実際には即値 div であるあいまいな命令が 1 つあります。これはAAMと呼ばれ、64 ビット モードでは使用できません。アセンブラーはデフォルトで 10 で除算しますが (BCD の意図したユースケースの場合)、imm8 と同じオペコードです。 DIV または AAM を使用して 0 ~ 99 の整数を 2 つの ASCII 数字に変換する方法を次に示します。また、AAM とDIV r/m8
.
Intel はいつでも IDIV の即時バージョンを追加できたはずですが、実行しませんでした。私の推測では、DIV / IDIV は十分に遅い (そして十分にまれである) ため、余分なオーバーヘッドmov reg, imm32
は無視できるほどであり、そのような命令にオペコード スペース (およびデコーダ トランジスタ) を費やすことは決して価値があるとは見なされませんでした。
さらに重要なことは、コンパイル時の定数による実際のハードウェア除算は、通常、パフォーマンスではなくコードサイズにのみ役立ちます。モジュラー乗法逆数は、90 年代から (コンパイラーのライターによって) よく知られています。コンパイラは定数による除算さえ使用していないため、Intel は、この手法が知られるようになった後に設計された CPU にそのための命令を追加する可能性はほとんどありませんでした。たとえば、clang は次のようにコンパイルunsigned int div10(unsigned int a) { return a/10; }
されます。
mov ecx, edi # just to zero-extend to 64-bit
mov eax, 3435973837 # a sign-extended imm32 can't represent this constant, I guess. clang uses imul r,r,imm for other cases.
imul rax, rcx # 64-bit multiply instead of 32x32 => 64 in two separate regs
shr rax, 35 # extract part of the high-half result.
ret
符号付き除算にはさらにいくつかの命令が必要であり、単純ではない除数の結果をいじる追加/減算が必要になる場合があります。Godbolt でいくつかの例を参照してください。それでも、 Haswell で22 ~ 29 サイクルのレイテンシがDIV r64
あり、スループットが悪いなど、非常に遅いハードウェア除算命令よりも高速です。
彼らがより多くの命令にオペコード(およびデコーダトランジスタ/電力)を費やすつもりなら、単一幅の被除数を持つ2レジスタ形式のIDIVがコンパイラに役立つかもしれません.
ハードウェア除算器が内部でどのように実装されているかについてはあまり知らないので、通常の 2N / N => N の代わりに N / N => N ビット除算のみを行うことで節約できる場合は IDK を使用します。コンパイラ出力では、ほとんどすべての分割は、CDQ または の後に行われxor edx,edx
ます。多くの x86 マイクロアーキテクチャでは、除算は可変レイテンシーであるため、被除数が実際には N ビットしかない場合にスピードアップが必要な場合は、ハードウェアが既にそれを探していると思われます。ただし、Skylake DIV/IDIV r32 は一定の 26c レイテンシーです(ただし、64 ビット除数ははるかに遅く、依然として非常に変動するレイテンシーがあります)。
おそらく、DIV r32, r32
命令はまだ2つの出力(商と剰余)を生成するでしょう.2つの入力レジスタで推測しますか?そのため、入力を保存するために追加の MOV 命令が必要になることがよくあります。あるいは、商または余りを選択して 1 つの宛先に移動するか、商または剰余に 2 つの別々のオペコードを使用するのにすぐに時間がかかるでしょうか?
この時点で、3 つの明示的なオペランドを使用して、MULXのように機能する VEX コード バージョンを追加できます。ただし、MULX の使用目的は、拡張精度の乗算を拡張精度のキャリー付き加算とインターリーブできるようにすることであるため、DIVX r64(quotient), r64(remainder), r/m64(divisor)
(RDX で暗黙の被除数がある場合) は大幅に異なります (拡張精度にはあまり有用ではありません)。彼らはおそらく、暗黙の被除数を RDX:RAX にするでしょう。あるいは、DIVX はすでにビデオ コーデック / 会社の商標であるため、DIVX とは呼ばないかもしれません。