1. ホーム
  2. assembly

[解決済み] mul/divでcdqを拡張して署名するのはいつ、なぜか?

2022-03-09 04:02:13

質問

今日のテストで、ダブルワードをクアッドワードに変換する問題だけがわからなかった。

そこで考えたのですが、乗算や除算で符号を伸ばすのはなぜか、いつなのか?また、cdqのような命令はいつ使うのでしょうか?

解き方は?

使用方法 cdq / idiv 符号付き32ビット/32ビット=> 32ビット除算の場合。
xor edx,edx / div を無記名にします。

EAXで配当を開始し、DIVまたはIDIVにオペランドとして除数を指定した場合。

   mov  eax, 1234
   mov  ecx, 17
   cdq                   ; EDX = signbit(EAX)
   idiv  ecx             ; EAX = 1234/17     EDX = 1234%17

EDX/RDX をゼロにしてから EDX:EAX に符号拡張した場合。 idiv , のように、-5 / 2 に対して大きな正の結果を得ることができます。 .

32ビット除算は、64/32ビットのフルパワーで実行できますが、商がオーバーフローしないように除数が十分に大きいことが分かっていなければ、安全ではありません。 (つまり、一般的には (a*b) / c のみで mul / div と、EDX:EAXに64bitのテンポラリーがあります)。

除算は商のオーバーフローで例外(#DE)を発生させる。 Unix/Linuxの場合。 カーネルはSIGFPEを送出します。 除算エラーを含む算術例外の場合。 通常の符号またはゼロ拡張除算では、オーバーフローは以下の場合にのみ発生します。 idivINT_MIN / -1 (すなわち、最も負の数の2の補数の特殊な場合)。


insn refのマニュアルを見ればわかるように( x86 タグのwiki)をご覧ください。

  • ワンオペ mul / imul : edx:eax = eax * src
  • ツーオペランド imul : dst *= src . 例 imul ecx, esi は eax や edx の読み書きをしません。

  • div / idiv 分割 edx:eax のsrc.商で計算されます。 eax の余り edx . の形はありません。 div / idiv を無視した edx を入力します。
  • cdq サインエクステンド eaxedx:eax の符号ビットをブロードキャストする。 eax の各ビットに edx . と混同しないように cdqe をよりコンパクトにした64ビット命令です。 movsxd rax, eax .

    元々(8086)は、ちょうど cbw ( ax = sign_extend(al) ) と cwd ( dx:ax = sign_extend(ax) ). x86 の 32bit と 64bit への拡張により、ニーモニックが若干曖昧になっています (しかし、覚えておいてほしいのは、他の cbw で終わり、eax 内のバージョンは常に e Extendの場合)。 8bitのmulとdivは特殊なので、dl=sign_bit(al)命令はありません。 ax の代わりに dl:al .


への入力は [i]mul はシングルレジスタであるため、何もする必要がありません。 edx 乗算の前に

入力が符号付きの場合、符号拡張して乗算の入力として使用するレジスタを満たします。 movsx または cwde ( eax = sign_extend(ax) ). 入力が符号なしであれば、ゼロ拡張します。 (ただし、例えば、乗算結果の下位16ビットだけが必要な場合は例外です。 のどちらか、あるいは両方の入力の上位16ビットがゴミであっても問題ではありません。 .)


除算の場合、常にeaxをゼロにするか、符号拡張してedxに入れる必要があります。 ゼロ拡張は、無条件にedxをゼロにするのと同じなので、特別な命令はありません。 ただ xor edx,edx .

cdq よりもずっと短いので、存在するのです。 mov edx, eax / sar edx, 31 で、eaxの符号ビットをedxの各ビットにブロードキャストします。 また、即時カウント > 1 のシフトは 186 年まで存在せず、まだカウントごとに 1 サイクルだったので、8086 ではさらに悪いことをしなければなりませんでした(分岐するか、符号ビットを一番下に回転して + を分離するなど)。 neg ということです。) そこで cwd 8086では、必要なときに多くの時間やスペースを節約することができました。


64bitモードでは、32bitの値を64bitに拡張する符号と0が一般的です。 ABIは32bitの値を保持する64bitレジスタの上位32bitにゴミを入れることを許可しているため、もし関数が edi を使用することはできません。 [array + rdi] を使用して配列のインデックスを作成します。

そのため、多くの movsx rdi, edi (符号拡張)、または mov eax, edi (ゼロ拡張。Intel mov-eliminationが動作しないため、別のターゲットレジスタを使用する方が効率的です。 mov same,same )