使用する理由:
MOV EAX, 22
SHL EAX, 2
...単にMUL
命令を使用するのではなく、4 を掛けるときは? これは、代わりに同様
に行うこともできることを理解しています。SHR
DIV
これを行う利点は何ですか?
また、これを奇数で行うことはできますか、それとも偶数のみにすることができますか?
「MUL定数」よりも高速なコードイディオムがいくつかあります。
最近の x86 CPU は、最低でも数クロックで MUL を実行します。したがって、積を 1 ~ 2 クロックで計算するコード シーケンスは、MUL よりも優れています。高速な命令 (ADD、SHL、LEA、NEG) と、プロセッサがこれらの命令の一部を 1 つのクロックで並行して実行できるという事実を使用して、MUL を置き換えることができます。おそらくこれは、データの依存関係を回避すれば、これらの命令のうち 4 つを多くの組み合わせで 2 クロックで実行できることを意味します。
LEA 命令は、いくつかの小さな定数 (1、2、3、4、5、8、9) を掛けて、積を別のレジスタに移動できるため、特に興味深いものです。これは、データの依存関係を解消する簡単な方法の 1 つです。これにより、元のオペランドを破壊することなく副積を計算できます。
いくつかの例:
EAX を 5 倍し、積を ESI に移動します。
LEA ESI, [EAX+4*EAX] ; this takes 1 clock
EAX に 18 を掛けます。
LEA EAX, [EAX + 8*EAX]
SHL EAX, 1
EAX に 7 を掛け、結果を EBX に移動します。
LEA EBX, [8*EAX]
SUB EBX, EAX
EAX に 28 を掛けます。
LEA EBX, [8*EAX]
LEA ECX, [EAX+4*EAX] ; this and previous should be executed in parallel
LEA EAX, [EBX+4*ECX]
1020 を掛ける:
LEA ECX, [4*EAX]
SHL EAX, 10 ; this and previous instruction should be executed in parallel
SUB EAX, ECX
35倍
LEA ECX, [EAX+8*EAX]
NEG EAX ; = -EAX
LEA EAX, [EAX+ECX*4]
したがって、適度なサイズの定数を乗算する効果を実現したい場合は、LEA 命令が生成できるさまざまな積にどのように「因数分解」できるか、およびどのようにシフト、加算、または減算するかを考える必要があります。最終的な答えを得るための部分的な結果。
この方法で生成できる定数による乗算の数は注目に値します。これは非常に小さな定数の場合にのみ役立つと思うかもしれませんが、上記の 1020 の例からわかるように、驚くほど中規模の定数も取得できます。これは、構造体のサイズでインデックスを乗算する必要があるため、構造体の配列にインデックスを付けるときに非常に便利です。このように配列にインデックスを付ける場合、要素のアドレスを計算して値を取得したいことがよくあります。この場合、最終的な LEA 命令を MOV 命令にマージできますが、これは実際の MUL では実行できません。これにより、このタイプのイディオムで MUL を実行するための追加のクロック サイクルが得られます。
[私は、これらの命令を使用して、命令の組み合わせを少し徹底的に検索することにより、「定数による最適な乗算」を計算するコンパイラを作成しました。次に、後で再利用するためにその回答をキャッシュします]。