2016-12-10 5 views
1

EDX:EAXレジスタペアには、の64ビットのの整数が格納されています。 どうすればいいですか?ネゲート32ビットレジスタペアに格納されている64ビット整数をどのように否定するのですか?

たとえば、123456789123-123456789123です。

+5

ため、それらの出力を見てみましょう。多くの方法があります。まず、 '-x = 0-x'なので、0から減算することができます。次に' -x = -1 * x'を実行することもできます。すべてのビットを反転して1を加えるという2の補数式を行うこともオプションです。 – Jester

答えて

10

コンパイラにアイディアを依頼してください: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が良いだろう場合でも、これを使用しています。コンパイラは何をすべきかのより完全な画像については


は、あなたがしようとしたか、と考えるかを示すのを忘れて、これらの関数(on godbolt

#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(); 
} 
+3

興味深いことに、gccだけでは、clangとiccはゼロからの減算を使用します。 – Jester

+0

なぜ 'adc edx、0'が必要ですか? *演算子*が* 0の場合、 'neg'演算はキャリーフラグをセットするだけです。* –

+0

@NagyRobi:NEGのフラグ設定が後方にあります。'edx = 0-edx-CF'の代わりに' edx = - (edx + CF) 'を実行しているので、SBBではなくADCを使用しています。 –

関連する問題