条件実行とジャンプ命令
i386にはフラグレジスタがあり,その内容によって条件付きでジャンプする命令が用意されていた(第I部 第3章). 条件付きジャンプ命令を使うと例えばC言語のif文のようなことができる.
ARMにもi386のフラグレジスタに相当する条件フラグがあるが,条件付きジャンプ命令はもっと一般化された仕組みである条件実行の中のひとつとして提供されている. ARMでは,ほとんどの命令にその命令を実行する条件を付けることができる.条件付きの命令は,条件フラグの内容が条件を満たしているときに限り実行され,条件を満たしていなければ無視される.
プログラム例
条件実行の仕組みを使ってN
の絶対値を計算する例を示す.
.equ N, -5
.section .text
.global _start
_start:
movs r0, #N @ 即値 N を r0 にロードし,条件フラグを更新
rsbmi r0, r0, #0 @ Negative フラグがセットされているなら r0 = 0 - r0 を実行
@ EXIT システムコール
mov r7, #1
swi #0
RSBMI命令はRSB命令に条件属性MIが付いた命令で,条件フラグ中のNegativeフラグがセットされているときに限りRSB命令として動作する.
Negativeフラグがセットされていないときは単にRSBMI命令が無視される.RSB命令は第3オペランドから第2オペランドを引いて第1オペランドに格納する命令(SUB命令の引く数と引かれる数を入れ替えた命令)である.N
が負のときはMOVS命令によってNegativeフラグがセットされるので,RSBMI命令によりr0 = 0 - r0
を実行することで符号を反転する.
このように,命令の後ろに条件属性を付けることで,条件付き命令に変更できる.
条件フラグ
条件フラグには V, C, Z, N の4つがある.各フラグの意味は次の表の通り.
フラグ | フラグ名 | 意味 |
---|---|---|
V | oVerflow | オーバフローした |
C | Carry | キャリーが発生した |
Z | Zero | 結果が0になった |
N | Negative | 結果が負になった |
Cフラグは,加算の結果が32ビットに収まらなかったとき,及び減算の結果が0以上のときに1にセットされる.フレキシブル第2オペランドの指定によるシフトやローテートによってもセットされる.
(参考: 減算を行ったときのCフラグの値が i386 の逆になっている.i386では桁借りが起こったときにCFが1となるが,ARMでは桁借りが起こらなかったときにCフラグが1になる.)
条件フラグの更新
条件フラグは命令の最後にSを付けたときだけ更新される.上の例でMOV命令ではなくMOVS命令を使ったのはそのためである. どの条件フラグが変化するかは命令によって決まっている.例えばMOVS命令はコピーの対象となった値に応じてZフラグとNフラグが変化するがVフラグとCフラグは保存される. これまでに登場した命令では,次のような規則になっている.
- ADDS/SUBS/RSBS/ADCS/SBCS/RSCS命令はすべての条件フラグを更新する.
- UDIV/SDIV命令には条件フラグを更新するUDIVS/SDIVSのような命令は存在しない.
- LDR命令にも条件フラグを更新するLDRSのような命令は存在しない.
- それ以外の命令(MOV, 乗算, 論理演算)は,S付きの命令を実行すると,ZとNフラグのみを更新する.ただし,フレキシブル第2オペランドでシフトやローテートが指定された場合はCフラグも更新する.
CMP命令とTST命令
CMP命令は条件フラグの更新だけを目的にした命令である.CMP命令はSUBS命令と同じように減算を行うが,dstレジスタを取らず(つまり2オペランド命令), 減算の結果はどのレジスタにも格納しない.CMP命令は条件フラグの更新が目的の命令なので最後にSを付ける必要がない.
TST命令もCMP命令と同様に条件フラグの更新だけを目的としている.CMP命令は演算結果を捨てるSUBS命令だったのに対し,TST命令は演算結果を捨てるANDS命令である.
- CMP src1, src2*
- src1からsrc2を引いて結果は捨てる.条件フラグV, C, Z, Nを更新する.
- TST src1, src2*
- src1とsrc2の論理積を計算して結果は捨てる.条件フラグZ, Nを更新する.フレキシブル第2オペランドでシフトかローテートを指定したときはCフラグも更新する.
条件属性
命令の後に条件属性を追加することで,条件付きで実行する命令になる.条件付きで命令を実行し,さらに条件フラグの更新もしたいときは,RSBMISのように条件属性をSの前に付ける.
条件属性の一覧を示す.
接尾辞 | 意味(下記ならば実行) | 正確な条件 |
---|---|---|
MI | 負 (<0) | Nセット |
PL | 0または正 (>=0) | Nクリア |
EQ | 等しい (==) | Zセット |
NE | 等しくない (!=) | Zクリア |
HS または CS | (符号なしで)以上 (>=) | Cセット |
LO または CC | (符号なしで)未満 (<) | Cクリア |
HI | (符号なしで)より大きい (>) | CセットかつZクリア |
LS | (符号なしで)以下 (<=) | CクリアまたはZセット |
GE | (符号付きで)以上 (>=) | NセットかつVセット,またはNクリアかつVクリア |
LT | (符号付きで)未満 (<) | NセットかつVクリア,またはNクリアかつVセット |
GT | (符号付きで)より大きい (>) | ZクリアかつNセットかつVセット,またはZクリアかつNクリアかつVクリア |
LE | (符号付きで)以下 (<=) | Zセット,またはNセットかつVクリア,またはNクリアかつVセット |
VS | オーバフロー | Vセット |
VC | オーバフローなし | Vクリア |
ジャンプ命令と条件ジャンプ命令
ジャンプ命令を示す.Bはbranch(分岐)のB.
- B label
- labelの付いた命令に制御を移す.
条件ジャンプ命令は,ジャンプ命令に条件属性を付けることで実現できる.
if文とwhile文
高級言語のif文やwhile文は,第I部 3章で説明した方法で条件ジャンプを使って実現できる. しかし,if文のthen節が短い場合は,条件ジャンプを使うより,then節の中身を条件実行する方が,プログラムが短かくなる.
例えばNの絶対値を求めるプログラムをC言語風に書くと,
r0 = N;
if (r0 < 0) {
r0 = 0 - r0;
}
となる.これを条件ジャンプ命令を使ってアセンブリ言語で書くと,
.equ N, -5
.section .text
.global _start
_start:
movs r0, #N @ 即値 N を r0 にロードし,条件フラグを更新
bpl owari @ Negative フラグがクリアされているならラベル owari: にジャンプ
rsb r0, r0, #0 @ r0 = 0 - r0 を実行
owari:
@ EXIT システムコール
mov r7, #1
swi #0
となるが,条件実行を使うと最初に示したように,次のプログラムになる.
.equ N, -5
.section .text
.global _start
_start:
movs r0, #N @ 即値 N を r0 にロードし,条件フラグを更新
rsbmi r0, r0, #0 @ Negative フラグがセットされているなら r0 = 0 - r0 を実行
@ EXIT システムコール
mov r7, #1
swi #0
コードゴルフ (code golf)
第II部の演習課題は基本的に,第I部の演習課題と同じプログラムをARM用に作るものである. 従って,i386用プログラムを1命令ずつARMの命令に翻訳していくだけでも仕様を満たすものは作れるが,なるべくARM特有の機能を使って(例えば条件ジャンプを使わずに条件実行機能を使うなどして)プログラムを記述した方が,知識を深めるのに役立つであろう.
与えられた仕様を満たすプログラムをどれだけ短く記述できるか競う競技を「コードゴルフ」と言う. ARMの命令の諸機能を学び使う練習として,余力のあるチームはぜひ,各演習課題のプログラムをできるだけ短く記述することに挑戦してほしい.
(ここでは「機械語命令数の少なさ」を短さの尺度とする.ソースコードの文字数や行数,疑似命令の数,データ領域の大きさ等は関係ない.)
命令数を減らすヒント:
- 命令Aで得られる計算結果と0を比較するCMP命令は,命令AにSを付けてフラグを更新することで削れることが多い.
- n回の回数を数える場合,0からnまでカウントするのではなく,nから0までカウントすることで,前項のテクニックを使ってCMP命令を削れる場合がある.
- フラグを更新する命令とフラグを使う命令(条件属性が付いた命令)の間にはフラグを更新しない命令をはさんでもよい.if-then-elseも次のように条件ジャンプを使わずに書ける.
cmp r0, r1 ...pl ... @ then節 (r0 >= r1 のとき実行) ...pl ... @ then節 (r0 >= r1 のとき実行) ...mi ... @ else節 (r0 < r1 のとき実行) ...mi ... @ else節 (r0 < r1 のとき実行)
- SWI命令にも条件属性を付けられる.