落書きノート

ふと自分が気になった事を書いてます

アセンブラの条件分岐 i386編

参考書から抜粋してます。以前投稿した記事も含めて、自分用にまとめて復習しています。

i386では「ブランチ」でなく「ジャンプ」と呼ぶようだ

以下のC言語ファイルがあります。

int condition(int a, int b)
{
  if (a == b)
    b = 1;
  return b + 1;
}

これを逆アセンブルするとどんな感じなのでしょうか。条件分岐ですね。

00fe153e <condition>:
  fe153e:    8b 44 24 08              mov    0x8(%esp),%eax
  fe1542:    39 44 24 04             cmp    %eax,0x4(%esp)
  fe1546:    75 05                 jne    fe154d <condition+0xf>
  fe1548:    b8 01 00 00 00          mov    $0x1,%eax
  fe154d:    40                     inc    %eax
  fe154e:    c3                       ret 

3行目に「cmp」という命令があります。名前からして、これが恐らく比較命令でしょう。

cmp命令のオペランドはEAXレジスタと「0x4(%esp)」です。「0x4(%esp)」は「ESP + 4」というメモリ上の位置を指しますから、これはスタック上の値をオペランドにして比較しているようです。

そしてEAXレジスタには、先頭のmov命令によりスタック上の「ESP + 8」という位置から値をロードしています。

i386は「スタックポインタ + 4」の位置に第1引数が、「+8」の位置に第2引数が格納されています。つまり3行目では、condition()の第1引数と第2引数をcmp命令で比較していることになります。

cmp命令の後に呼ばれているのは「jne」という命令です。H8とPowerPCでは条件分岐のために「bne」という命令が使われていましたが、i386では「branch」(ブランチ)でなく「jump」(ジャンプ)と呼ばれるようです。

jne命令の飛び先には、「fe154d」というアドレスが指定されています。そしてそこにはinc命令があります。つまり、「return b + 1」の加算処理にジャンプしているようです。この辺りはH8やPowerPCで見た動作と同じです。

実はi386も、ブラグレジスタを持っています。cmp命令の結果としてフラグレジスタのフラグが書き換わり、jne命令でそのビットを見て条件分岐しているようです。

この事を踏まえて自分で説明しますと、jne(jump not equal)命令は、%eax != 0x4(%esp)ならば、fe154dに飛べということですね。%eax == 0x4(%esp)ならば、EAXレジスタに1を代入して次に進めということです。(唯一、説明してる…。)

「分岐(ブランチ)」と「ジャンプ」

ここまでは「分岐」(branch)と「ジャンプ」(jump)という言葉をあまり厳密に区別せずに使ってきたのですが、これらにはどのような使い分けがあるのでしょうか。

一般に分岐というと、いわゆる「条件分岐」のことで、特定の条件が満たされた時のみ飛ぶような命令を指すように思います。

これに対してジャンプというと「無条件分岐」を指し、必ず飛び先にいくような命令を指すことが多いようです。

また飛び先の指定が相対アドレスの場合には「分岐」と呼び、絶対アドレスの場合は「ジャンプ」と呼んでいることも多いように思います。

そして分岐命令は「branch」として「bXX」のような名前の命令になっていて、ジャンプ命令は「jXX」とか「jmp」とかいった命令名になっていることが多いようです。また関数呼び出しは特別扱いとして、「call」のような名前の命令になっていることも多くあります。

ただこれらの呼び方はCPUによって異なっていて、共通規則のようなものはないようです。あるCPUでは「分岐」と「ジャンプ」で区別しているものが、他のCPUでは全て「分岐」と言ってしまっているようなこともあります。

なので、あまり深く考えず、どちらも同じくらいに思っているのが良いようですね。