EDX:EAX
レジスタペアには、の64ビットのの整数が格納されています。 どうすればいいですか?ネゲート?32ビットレジスタペアに格納されている64ビット整数をどのように否定するのですか?
たとえば、123456789123
→ -123456789123
です。
EDX:EAX
レジスタペアには、の64ビットのの整数が格納されています。 どうすればいいですか?ネゲート?32ビットレジスタペアに格納されている64ビット整数をどのように否定するのですか?
たとえば、123456789123
→ -123456789123
です。
コンパイラにアイディアを依頼してください:int64_t neg(int64_t a) { return -a; }
を32ビットモードでコンパイルしてください。もちろん、コンパイラに尋ねるさまざまな方法は、メモリ、コンパイラのレジスタの選択、またはEDX:EAXの中で開始値を持つことになります。 gcc、clang、およびMSVC(別名CL)からのasm出力を持つ3つの方法on the Godbolt compiler explorerすべてを参照してください。
もちろん、これを達成する方法はたくさんありますが、可能なシーケンスはある時点で低から高への何らかのキャリーを必要とするため、SBBやADCを回避する効率的な方法はありません。
値はメモリで起動します。または、レジスタの元の値を保持したい、xor-aがゼロ先とSUB/SBBを使用している場合。 SysV x86-32 ABIはargsをスタックに渡し、EDX:EAXの64ビット整数を返します。自分自身を、CFの設定 - あなたはレジスタの値を持っており、インプレース結果を必要としない場合は、0にレジスタを設定するNEGを使用することができます
; optimal for data coming from memory: just subtract from zero
xor eax, eax
xor edx, edx
sub eax, dword ptr [esp + 4]
sbb edx, dword ptr [esp + 8]
:これはneg_value_from_mem
について何clang3.9.1 -m32 -O3
does、あります入力が非ゼロの場合すなわちSUBと同じ方法である。 xor-zeroing is cheapであり、レイテンシクリティカルパスの一部ではないことに注意してください。これはgccの3命令シーケンス(下記)よりもはるかに優れています。
;; partially in-place: input in ecx:eax
xor edx, edx
neg eax ; eax = 0-eax, setting flags appropriately
sbb edx, ecx ;; result in edx:eax
クランはそれが余分mov ecx,edx
のコストにもかかわらず、でもインプレースケースのためにこれを行います。これは、ゼロレイテンシのmov reg、reg(Intel IvB +およびAMD Zen)を持つが、融合ドメインのuop(フロントエンドスループット)またはコードサイズの数ではない最新のCPUのレイテンシに最適です。
gccのシーケンスは興味深く、全く明らかではありません。それはインプレースの場合のための命令対clangを保存しますが、それ以外の場合は悪化します。
; gcc's in-place sequence, only good for in-place use
neg eax
adc edx, 0
neg edx
; disadvantage: higher latency for the upper half than subtract-from-zero
; advantage: result in edx:eax with no extra registers used
残念ながら、gccとMSVCの両方が常にxor-aがゼロ+サブ/ SBBが良いだろう場合でも、これを使用しています。コンパイラは何をすべきかのより完全な画像については
#include <stdint.h>
int64_t neg_value_from_mem(int64_t a) {
return -a;
}
int64_t neg_value_in_regs(int64_t a) {
// The OR makes the compiler load+OR first
// but it can choose regs to set up for the negate
int64_t reg = a | 0x1111111111LL;
// clang chooses mov reg,mem /or reg,imm8 when possible,
// otherwise mov reg,imm32/or reg,mem. Nice :)
return -reg;
}
int64_t foo();
int64_t neg_value_in_place(int64_t a) {
// foo's return value will be in edx:eax
return -foo();
}
興味深いことに、gccだけでは、clangとiccはゼロからの減算を使用します。 – Jester
なぜ 'adc edx、0'が必要ですか? *演算子*が* 0の場合、 'neg'演算はキャリーフラグをセットするだけです。* –
@NagyRobi:NEGのフラグ設定が後方にあります。'edx = 0-edx-CF'の代わりに' edx = - (edx + CF) 'を実行しているので、SBBではなくADCを使用しています。 –
ため、それらの出力を見てみましょう。多くの方法があります。まず、 '-x = 0-x'なので、0から減算することができます。次に' -x = -1 * x'を実行することもできます。すべてのビットを反転して1を加えるという2の補数式を行うこともオプションです。 – Jester