今日テストを受けましたが、ダブルワードをクワッドワードに変換する問題だけが理解できませんでした。
なぜ/いつ乗算または除算の拡張に署名するのですか? また、cdq などの命令はいつ使用するのでしょうか。
今日テストを受けましたが、ダブルワードをクワッドワードに変換する問題だけが理解できませんでした。
なぜ/いつ乗算または除算の拡張に署名するのですか? また、cdq などの命令はいつ使用するのでしょうか。
cdq
符号付き 32 ビット/ idiv
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
idiv
の前にEDX:EAX に符号拡張する代わりに EDX/RDX をゼロにすると、たとえば のように -5 / 2 に対して大きな正の結果を得ることができます。
64 / 32ビット=> 32ビット除算の「フルパワー」を使用することは可能ですが、商がオーバーフローしないように除数が十分に大きいことがわかっていない限り安全ではありません。(つまり、EDX:EAX で/と 64 ビットのテンポラリ(a*b) / c
のみを使用して一般に実装することはできません。)mul
div
除算は、商のオーバーフロー時に例外 (#DE) を発生させます。Unix/Linux では、カーネルは除算エラーを含む算術例外に対してSIGFPE を提供します。通常の符号またはゼロ拡張除算では、オーバーフローは of でのみ可能ですidiv
(INT_MIN / -1
つまり、最も負の数の 2 の補数の特殊なケース)。
insn ref マニュアルからわかるように ( x86タグ wiki のリンク):
mul
/ imul
:edx:eax = eax * src
imul
: dst *= src
. たとえばimul ecx, esi
、eax や edx を読み書きしません。div
/ : src でidiv
割ります。edx:eax
の商eax
、残りのedx
。入力を無視するdiv
/の形式はありません。idiv
edx
cdq
に符号拡張eax
しますedx:eax
。つまり、 の符号ビットを のeax
すべてのビットにブロードキャストしedx
ます。cdqe
をよりコンパクトな形式にした 64 ビット命令であると混同しないでくださいmovsxd rax, eax
。
もともと (8086) はcbw
( ax = sign_extend(al)
) とcwd
( dx:ax = sign_extend(ax)
) だけでした。x86 から 32 ビットおよび 64 ビットへの拡張により、ニーモニックが少し曖昧になりました (ただし、cbw
eax 以外のバージョンは常にe
for Extend で終わることに注意してください)。8bit mul と div は特殊なので dl=sign_bit(al) 命令はax
なく、 の代わりに使用しdl:al
ます。
への入力は単一のレジスタであるため、乗算の前に[i]mul
何もする必要はありません。edx
入力が符号付きの場合は、それを符号拡張して、乗算への入力として使用しているレジスタを埋めます (例: movsx
or 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 カウントあたり 1 サイクルのままだったため、8086 ではさらに悪いことを行う必要がありました (分岐するか、符号ビットを下に回転させて +neg
それを分離するなど)。 )。したがってcwd
、8086 では、必要なときに多くの時間/スペースを節約できました。
64 ビット モードでは、32 ビット値を 64 ビットに拡張する符号とゼロが一般的です。ABI では、32 ビット値を保持する 64 ビット レジスタの上位 32 ビットのガベージが許可されているため、関数が の下位 32 ビットのみを参照することになっている場合は、配列のインデックス付けedi
だけを使用することはできません。[array + rdi]
したがって、多くのmovsx rdi, edi
(符号拡張) またはmov eax, edi
(ゼロ拡張、そしてインテルの mov-elimination は では機能しないため、別のターゲット レジスタを使用する方が効率的ですmov same,same
)