`testl` eax対eax?


118

いくつかのアセンブリを理解しようとしています。

次のようなアセンブリ、私はtestl行に興味があります:

000319df  8b4508        movl   0x08(%ebp), %eax  
000319e2  8b4004        movl   0x04(%eax), %eax  
000319e5  85c0          testl  %eax, %eax  
000319e7  7407          je     0x000319f0  

とのtestl間のそのポイントを理解しよう%eaxとしてい%eaxますか?このコードの詳細は重要ではないと思います。テストをそれ自体で理解しようとしているだけです。値は常に真ではありませんか?

回答:


91

eaxが0以上か、それとも以下かをテストします。この場合、ジャンプeaxは0の場合に行われます。


2
私は、この一般的な回答を「このTESTのすべては何であり、CMPとはどのように異なるのか」に対するより良い標準的な回答に変えるように編集しました。同義のJEとJZの意味上の意味に関するコメントについては、下の自分の回答を参照してください。私の編集はかなりメジャーであり、それはまだあなたの答えです。
Peter Cordes

@PeterCordes意図に感謝しますが、編集内容を元に戻します。1.あなたの「声」は私のものとは非常に異なりますが、今のところ、私のものよりもあなたの答えのほうがずっとよく読みます。2.さらに問題なのは、フラグがtestとの間でまったく同じように出力されるという大胆な主張ですcmp。はい、私はあなたのコーディへのコメントに基づくあなたの信念だと理解しています。しかし、それを私の投稿に入れるのは別の問題です。それがすべてのケースで同一どうかわからないという理由だけで、私が待機する意思のある主張ではありません
Chris Jester-Young

1
@PeterCordes暇な時間があったら、この答えをより正統的に表現したいと思います。私はそれを書くのと同じようにそれを書きます、そして私は物事を書く方法に非常にこだわります。:-)例えば、私が書きたいjejzcmp、とtest、とないJE、JZ、CMP、またはTEST。私はそのようなうるさいです。
Chris Jester-Young、

1
私は自分の答えを後押ししようとしていませんでした。実際に編集したときに自分でこの質問に答えたことを忘れてしまい、その後気づいただけです。誰かがぶつかった後、私はこれを見ただけで、小さな編集から始まったものが雪だるま式になりすぎました。それをロールバックしたいと思ったことは何の罪もありません。それは単なる提案であり、間違いなく私の作品のように読めます。私は書いたもののいくつかを取り、それを自分の答えに入れます。
Peter Cordes

2
うわー、この質問への私の回答を編集してあなたの質問に追加したものを含めたところ、6月に書いたほとんどの内容がほぼ正確に複製されていることに気付きました。おっとっと!私は、というのが私の主張をバックアップするために、より推論とそれを更新test a,aし、cmp $0,a同じようにフラグを設定します。それは重要な主張だと指摘してくれてありがとう。re:TEST vs.:test最近、Intelのマニュアルのようにすべて大文字を使い始めました。しかし、AT&TのニーモニックとIntelのニーモニックについて話すときはtestb、AT&Tのスタイルを使用します。IDKが読みやすくなる場合。
Peter Cordes

90

の意味はtest、引数をANDで結合し、結果がゼロかどうかを確認することです。したがって、このコードはEAXがゼロかどうかをテストします。jeゼロの場合はジャンプします。

ところで、これcmp eax, 0はコンパイラーが通常このように行う理由であるよりも小さい命令を生成します。


34

テスト命令は、オペランド間で論理AND演算を行いますが、結果をレジスタに書き戻しません。フラグのみが更新されます。

あなたの例では、テストeax、eaxは、eaxがゼロの場合はゼロフラグを設定し、最上位ビットが設定されている場合は符号フラグを設定し、他のいくつかのフラグも設定します。

ゼロフラグが設定されている場合、Jump if Equal(je)命令がジャンプします。

このコードを次のような読みやすいコードに変換できます。

cmp eax, 0
je  somewhere

これには同じ機能がありますが、数バイトのコードスペースが必要です。これが、コンパイラが比較ではなくテストを発行した理由です。


3
実際、cmpはそこで機能しない可能性があります。つまり、提示された特定のケースで機能しますが、cmpはandの代わりに内部サブルーチンであるため、テストとは異なる方法でフラグに影響します。覚えておくべきこと。
コーディBrocious

4
ゼロに対するテストの場合、それは完全に有効です。
Nils Pipenbrinck 2008

3
しかし、後で他のフラグを見ることはわかりません。フラグへの影響は非常に異なるため、これは問題になる可能性があり、非常に頻繁に起こります。
Cody Brocious

2
いいえ、別の/ method /によって設定されるフラグはキャリーとオーバーフローのみで、どちらも0に設定されています。他のフラグの/ values /は、cmpがsubとtestを使用するため、異なります。
Cody Brocious

2
@CodyBrocious:test eax, eaxcmp eax, 0の両方がすべてのフラグを設定して、同じ値に設定します。どちらの命令も、「結果に従って」すべてのフラグを設定します。減算0によってキャリーやオーバーフローが発生することはありません。あなたの引数が0以外の任意の即時の正しいではなく0です
ピーター・コルド

22

testandFLAGSのみを書き込み、両方の入力を変更せずに残すことを除いて、に似ています。2つの異なる入力を使用すると、一部のビットがすべてゼロであるか、少なくとも1つが設定されているかをテストするのに役立ちます。(たとえばtest al, 3、EAXが4の倍数である場合にZFを設定します(したがって、下位2ビットの両方がゼロに設定されます)。


test eax,eaxすべてのフラグにまったく同じように設定cmp eax, 0だろうが

  • CFとOFはクリアされます(AND / TESTは常にそれを実行します。ゼロを減算するとキャリーは生成されません)
  • EAXの値に応じたZF、SF、PF。(a = a&a = a-0)。
    (通常のPF は下位8ビットに従ってのみ設定されます

廃止されたAF(ASCII / BCD命令で使用される補助キャリーフラグ)を除きます。 TESTはそれを未定義のままにしますが、CMPは「結果に従って」それを設定します。ゼロを減算すると、4番目から5番目のビットからキャリーが生成されないため、CMPは常にAFをクリアする必要があります。


TESTはより小さく(即時ではなく)、場合によってはより高速です(CMPよりも多くの場合、より多くのCPUで比較および分岐uopにマクロ融合できます)。 これによりtest、レジスターをゼロと比較するための推奨イディオムになります。これcmp reg,0は、意味の意味に関係なく使用できるのぞき穴の最適化です。

CMPを即値0で使用する唯一の一般的な理由は、メモリオペランドと比較する場合です。たとえばcmpb $0, (%esi)、暗黙の長さのCスタイルの文字列の末尾にある終了ゼロバイトをチェックするには、


AVX512F addkortestw k1, k2とAVX512DQ / BW(Skylake-XではなくKNL)add は、整数または命令と同じ方法で、ktestb/w/d/q k1, k2AVX512マスクレジスタ(k0..k7)で動作しますtestが、通常のFLAGSのように設定します。(SSE4 またはSSE のようなソート:SIMDドメイン内の入力と整数FLAGSになります。)ORANDptestucomiss

kortestw k1,k1AVX512の比較結果に基づいて/ cmovcc / setccを分岐し、SSE / AVX2 (v)pmovmskb/ps/pd+ testまたはを置き換える慣用的な方法cmpです。


jzvsの使用はje混乱を招く可能性があります。

jzそしてje、文字通り同じ命令です。つまり、マシンコードの同じオペコードです。 それらは同じことを行いますが、人間にとっては意味的な意味が異なります。逆アセンブラー(および通常はコンパイラーからのasm出力)は1つしか使用しないため、意味上の違いは失われます。

cmpsub2つの入力が等しい場合(つまり、減算結果が0の場合)にZF を設定します。 je(等しい場合はジャンプ)は、意味的に関連する同義語です。

test %eax,%eax/ and %eax,%eaxは、結果がゼロのときにZFを設定しますが、「等価」テストはありません。テスト後のZFでは、2つのオペランドが等しいかどうかはわかりません。したがって、jz(ゼロの場合はジャンプ)は、意味的に関連する同義語です。


testビット単位のand操作であることに関する基本情報を追加することを検討します。アセンブリを学習しているだけの人には明らかではないかもしれません(そして60秒ごとに命令リファレンスガイドをチェックするのが面倒/気づいていません;):))。
Ped7g 2017

1
@ Ped7g:十分に公平です。この部分を他の答えに任せる代わりに、この答えにすべてを入れても害はないと思います。AVX512 kortest*を追加しましktest*た。
Peter Cordes

ちなみに、これは基本的に同じ質問の別のバージョンに対する私の回答と同じですが、たとえば、Nehalemのような古いP6ファミリCPUで同じ値でレジスタを書き換えることにより、レジスタ読み取りのストールを回避するなど、パフォーマンスについてもっと詳しく述べました。
Peter Cordes

@PeterCordesこれは受け入れられる答えであるはずです:網羅的かつ技術的。受け入れられた投稿とは異なり、これは知識への好奇心と渇きを和らげます。それを続けてください。
プログラマー

PFは下位8ビットのパリティ(この場合はAL)に設定されていることに注意してください。
ecm

5

このコードスニペットは、何かへのポインタ(おそらく何らかの構造体またはオブジェクト)が与えられたサブルーチンからのものです。2行目はそのポインターを逆参照し、そのポインターから値をフェッチします-おそらくそれ自体がポインター、または単にintで、2番目のメンバー(オフセット+4)として保存されます。3行目と4行目は、この値がゼロかどうか(ポインターの場合はNULL)をテストし、ゼロの場合は以下のいくつかの操作(図示せず)をスキップします。

ゼロのテストは、即時のリテラルゼロ値との比較としてコーディングされる場合がありますが、これを書いたコンパイラー(または人間?)は、パイプラインやレジスターなどのすべての最新のCPUを考慮して、testl opがより高速に実行されると考えているかもしれません名前の変更。それは、明白な、しかしおそらく遅いMOV EAX、#0(私は古い表記を使用しています)ではなく、XOR EAX、EAX(コロラドの誰かのナンバープレートで見ました!) )。

asmでは、perlと同様、TMTOWTDI。



0

一部のプログラムでは、バッファオーバーフローのチェックに使用できます。割り当てられたスペースの最上部に0が配置されます。スタックにデータを入力した後、割り当てられたスペースの先頭で0を探し、割り当てられたスペースがオーバーフローしないようにします。

それがエクスプロイト-演習のstack0演習で使用され、オーバーフローしたかどうか、またゼロがない場合に「再試行」と表示されるかどうかを確認するために使用されました。

0x080483f4 <main+0>:    push   ebp
0x080483f5 <main+1>:    mov    ebp,esp
0x080483f7 <main+3>:    and    esp,0xfffffff0
0x080483fa <main+6>:    sub    esp,0x60                     
0x080483fd <main+9>:    mov    DWORD PTR [esp+0x5c],0x0 ;puts a zero on stack
0x08048405 <main+17>:   lea    eax,[esp+0x1c]
0x08048409 <main+21>:   mov    DWORD PTR [esp],eax
0x0804840c <main+24>:   call   0x804830c <gets@plt>
0x08048411 <main+29>:   mov    eax,DWORD PTR [esp+0x5c] 
0x08048415 <main+33>:   test   eax,eax                  ; checks if its zero
0x08048417 <main+35>:   je     0x8048427 <main+51>
0x08048419 <main+37>:   mov    DWORD PTR [esp],0x8048500 
0x08048420 <main+44>:   call   0x804832c <puts@plt>
0x08048425 <main+49>:   jmp    0x8048433 <main+63>
0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:   call   0x804832c <puts@plt>
0x08048433 <main+63>:   leave
0x08048434 <main+64>:   ret

このQ&Aに、ゼロ以外のレジスターをチェックするこの特定のケースがどのように追加されるのかわかりません。特に、cmp DWORD PTR [esp+0x5c], 0/ jz 0x8048427 <main+51>が別のMOVロードとテストよりも効率的だった場合。これは、ゼロをチェックするための一般的なユースケースではありません。
Peter Cordes

-4

私たちは見ることができたJGJLEを した場合testl %edx,%edx. jle .L3、我々は簡単に見つけることができるJLEはスーツです(SF^OF)|ZF%のEDXがゼロの場合は、ZF = 1、しかし%のEDXがゼロでないと-1であれば、testl後、OF = 0、およびSF = 1、つまりフラグ= trueで、ジャンプを実装しています.sorry、私の英語は下手です

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.