X86アセンブリでは、符号付き整数のadd
またはsub
演算がオーバーフローするとオーバーフローフラグが設定され、符号なし整数の演算がオーバーフローするとキャリーフラグが設定されます。
ただし、inc
命令とdec
命令に関しては、状況が多少異なるようです。これによると website 、inc
命令はキャリーフラグにまったく影響しません。
しかし、inc
とdec
がオーバーフローフラグにどのように影響するかについての情報はありません。
整数オーバーフローが発生した場合、inc
またはdec
はオーバーフローフラグを設定しますか?そして、この動作は符号付き整数と符号なし整数の両方で同じですか?
=============================[〜#〜]編集[〜#〜]=============================
さて、ここでの基本的なコンセンサスは、INCとDECは、キャリーフラグを除いて、フラグの設定に関してADDとSUBと同じように動作する必要があるということです。これは、Intelのマニュアルにも記載されています。
問題は、符号なし整数に関しては、実際にはこの動作を再現できないことです。
次のアセンブリコードを検討してください(結果の印刷を容易にするためにGCCインラインアセンブリを使用します)。
int8_t ovf = 0;
__asm__
(
"movb $-128, %%bh;"
"decb %%bh;"
"seto %b0;"
: "=g"(ovf)
:
: "%bh"
);
printf("Overflow flag: %d\n", ovf);
ここでは、符号付き8ビット値-128をデクリメントします。 -128は可能な最小値であるため、オーバーフローは避けられません。予想どおり、これは次のように出力されます。Overflow flag: 1
しかし、unsigned値で同じことを行うと、動作は私が期待するとおりではありません。
int8_t ovf = 0;
__asm__
(
"movb $255, %%bh;"
"incb %%bh;"
"seto %b0;"
: "=g"(ovf)
:
: "%bh"
);
printf("Overflow flag: %d\n", ovf);
ここでは、255の符号なし8ビット値をインクリメントします。255は可能な最大値であるため、オーバーフローは避けられません。ただし、これは次のように出力されます。Overflow flag: 0
。
え?この場合、なぜオーバーフローフラグを設定しなかったのですか?
オーバーフローフラグは、操作によって符号が変更される場合に設定されます。あなたのコードは非常に近いです。次の(VC++)コードでOFフラグを設定できました。
char ovf = 0;
_asm {
mov bh, 127
inc bh
seto ovf
}
cout << "ovf: " << int(ovf) << endl;
BHがインクリメントされると、MSBが0から1に変化し、OFが設定されます。
これにより、OFも設定されます。
char ovf = 0;
_asm {
mov bh, 128
dec bh
seto ovf
}
cout << "ovf: " << int(ovf) << endl;
プロセッサは符号付きの数値と符号なしの数値を区別しないことに注意してください。 2の補数演算を使用する場合、両方を処理する1セットの命令を持つことができます。符号なしオーバーフローをテストする場合は、キャリーフラグを使用する必要があります。 INC/DECはキャリーフラグに影響を与えないため、その場合はADD/SUBを使用する必要があります。
他の多くの回答が指摘しているように、INC
とDEC
はCF
に影響を与えませんが、ADD
とSUB
は影響を及ぼします。
しかし、まだ言われていないことは、これがパフォーマンスの違いを生むかもしれないということです。ルーチンから地獄を最適化しようとしない限り、通常はそれに悩まされるわけではありませんが、本質的にCF
を設定しないということは、INC
/DEC
のみを意味しますフラグレジスタの一部に書き込むと、部分的なフラグレジスタのストールが発生する可能性があります。 Intel64およびIA-32アーキテクチャ最適化リファレンスマニュアルを参照) または Agner Fogの最適化マニュアル 。
インテル®64およびIA-32アーキテクチャーソフトウェア開発者マニュアル
適切なマニュアルを見てください 命令セットリファレンス、A-M 。すべての指示は正確に文書化されています。
影響を受けるフラグに関するINCセクションは次のとおりです。
CFフラグは影響を受けません。結果に応じて、OF、SZ、ZF、AZ、PFのフラグが設定されます。
テストを変更して、ハードコーディングするのではなく番号を渡すようにしてから、256個の番号すべてを試行して、フラグに影響する番号がある場合はそれを見つけるループを作成します。または、asmにループを実行させ、フラグに到達したとき、または開始した番号にラップアラウンドしたときに終了します(0x00、0x7f、0x80、または0xFF以外で開始します)。
編集
。globlinc inc: mov $ 33、%eax top: inc%al jo done jmp top done: ret 。globldec dec: mov $ 33、%eax topx: dec%al jo donex jmp topx donex: ret
Incは、0x7Fから0x80に移行するとオーバーフローします。 0x80から0x7Fになると、decがオーバーフローします。問題は、インラインアセンブラの使用方法にあると思われます。
キャリーフラグを除いて、incはオペランド1を追加するのと同じ方法でフラグを設定します。
Incがキャリーフラグに影響を与えないという事実は非常に重要です。
http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html#HEADING2-117
プロセッサが行うことは、これらの命令の結果(add、adc、dec、inc、sbb、sub)に対して、符号付きと符号なしの両方の場合に適切なフラグを設定することです。つまり、すべての操作に対して2つの異なるフラグ結果を設定します。別の方法は、2セットの命令を持ち、一方が符号付き関連フラグを設定し、もう一方が符号なし関連フラグを設定することです。発行コンパイラが操作で符号なし変数を使用している場合は、キャリーとゼロ(jc、jnc、jb、jbeなど)をテストし、符号付きの場合は、オーバーフロー、符号とゼロ(jo、jno、jg、jng、jl、jleなど)をテストします。 )。
CPU/ALUは、符号なし2進数のみを処理でき、OF、CF、AF、SF、ZFなどを使用して、符号付き数値(OF)、符号なし数値として使用するかどうかを決定できます。 (CF)またはBCD番号(AF)。
問題については、2進数自体を符号なしと見なすことを忘れないでください。
**また、オーバーフローとOFには、入力番号、算術演算で使用する2番目の番号、および結果番号の3つの数値が必要です。
オーバーフローは、最初と2番目の数値の符号ビット(最上位ビット)の値が同じで、結果の符号が異なる場合にのみアクティブになります。 のように、2つの負の数を追加すると正の数になり、2つの正の数を追加すると負の数になります:
if( (Sign_Num1==Sign_Num2) && (Sign_Result!=Sign_Num1) ) OF=1;
else OF=0;
最初の問題では、最初の番号として-128
を使用しています。 2番目の数値は暗黙的に-1
であり、DEC命令によって使用されます。つまり、実際には2進数0x80
と0xFF
があります。どちらも符号ビットが1に設定されています。結果は0x7F
です。これは、符号ビットが0に設定された数値です。同じ符号の2つの初期数値と、異なる符号の結果が得られたためです。オーバーフローを示します。 -128-1
の結果は127
であるため、オーバーフローフラグが設定され、誤った署名結果を示します。
2番目の問題では、最初の番号として255
を使用しています。 2番目の数値は暗黙的に1
であり、INC命令によって使用されます。つまり、実際には2進数0xFF
と0x01
があります。どちらも符号ビットが異なるため、オーバーフローすることはありません(基本的に同じ符号の2つの数値を加算した場合にのみオーバーフローする可能性がありますが、異なる符号の2つの数値でオーバーフローすることはできません。可能な符号付きの値を超えることは決してありません)。 結果は0x00
であり、255+1
、より正確には-1+1
は0を与えるため、オーバーフローフラグは設定されません。これは、符号付き算術に対して明らかに正しいです。
オーバーフローフラグを設定するには、加算/減算される2つの数値に同じ値の符号ビットが必要であり、結果にはそれらとは異なる値の符号ビットが必要であることに注意してください。