決定とループ
デフォルトでは、CPUは命令を上から下へと現れる順に実行します。その順序だけでは、選択を行ったり作業を繰り返したりすることはできません。分岐とは、プログラムがその一直線の道から外れる方法です。条件をテストし、条件が成立したとき、CPUを次の行ではなく別の場所から続行するように導きます。すべての決定とすべてのループは、この一つの仕組みから生まれます。
if や for はありません。それらはラベル(名前付きの位置)と分岐(条件が成立したときに位置へジャンプする)から構築します。ラベルは名前の後に : が続くものです。
分岐がどのように実行されるかを理解するには、プログラムカウンタ(PC)について知る必要があります。PCは、コードからは見えない特別なレジスタで、実行中の命令のメモリアドレスを保持します。分岐命令はPCに新しい値を書き込むため、CPUは次の命令をコードの次の行ではなく、ターゲットラベルからフェッチします。
分岐ファミリー
すべての分岐は2つのレジスタを比較し、テストが成立したときだけラベルにジャンプします。テストが失敗したときは、実行は次の行に続きます。6つのテストが核心を形成します:
beq a, b, label # a == bなら分岐bne a, b, label # a != bなら分岐blt a, b, label # a < bなら分岐 (符号あり)bge a, b, label # a >= bなら分岐 (符号あり)bltu a, b, label # a < bなら分岐 (符号なし)bgeu a, b, label # a >= bなら分岐 (符号なし)
と は等価性をテストし、符号を無視します。 と は順序のペア、より小さいとより大きいか等しいを形成し、レジスタを符号付き数として比較します。 と は同じ比較を行いますが、値を符号なしとして扱います。これは負にならないアドレスやカウントに適しています。
符号付きと符号なしとはどういう意味でしょうか?レジスタは32ビットを保持し、同じビットを2通りに読むことができます。符号なしとして読むと、0から約40億までの単純な整数を形成します。符号付きとして読むと、最上位ビットがマイナス記号として数えられるため、ビットパターンの半分は負の数を表します。ビット自体は変わりません。変わるのは読み方だけです。だからこそ2組の比較があるのです。負になる可能性のある通常の数には符号付き(、)を使い、メモリアドレスのように決して負にならない値には符号なし(、)を使います。
直接の「より大きいなら分岐」や「より小さいか等しいなら分岐」はありません。それらは2つのレジスタを交換することで得られ、アセンブラは交換を代行する疑似分岐として と を提供します。したがって は になります。さらに2つのショートカット: と は1つのレジスタをゼロと比較し、 は条件なしでジャンプします。
li t0, 0 # 合計 = 0li t1, 1 # i = 1li t2, 6 # 上限 = 6 (i < 6の間ループ)loop:bge t1, t2, done # i >= 6でループを抜けるadd t0, t0, t1 # 合計 += iaddi t1, t1, 1 # i++j loop # 再びチェックしに戻るdone:mv a0, t0 # 合計を出力 (15になるはず)li a7, 1ecallli a7, 10ecall
追跡してみましょう。t0 は実行中の合計を保持し(0から開始)、t1 はカウンタ i を保持し(1から開始)、t2 は上限6を保持します。loop を通過するたびに: は i が6に達するとループを抜けます。そうでなければ が i を合計に加え、 が i を1増やし、 が再びテストするためにジャンプします。ループは1、2、3、4、5を加え、done が合計15を出力します。
bge は t1 と t2 を比較して、ループを継続するかどうかを決定します。
t1 が6になると、 は本体を再び実行させるのではなく、制御を done に移します。そのテストが、繰り返すパスと停止するパスを分けるものです。t1 が増加し、プログラムカウンタ(pc)が loop にジャンプして戻ります。pc が loop に戻るたびに、分岐条件は偽だったので、ループは再び実行されます。