bamboo’s blog

Bambooの気まぐれブログ

ARMショートコーディング(コード短縮)技法

はじめに

 ショートコーディングとは、ソースコードをいかに短くできるかというものである。今回はARMアセンブリにおいて、どのようにすればコードを短くできるか、色々と考えてみる。

条件実行の短縮

mov r0, #0     @ r0 = 0
cmp r0, #0     @ if(r0 == 0)
beq <label>    @ goto <label>

 上記のようなコードがあったとする。「条件実行はcmp命令(論理演算であればtst命令)を使えばよい」と無条件に考える人は多いが、必ずしもそうではない。もちろん間違いではないが、ショートコーディングにおいては仇となる場合がある。

 もう少し詳しく見てみよう。cmp命令は内部的には以下のようなことを行っている。

1. 第1オペランドの値から第2オペランドの値を引く。
2. 演算結果をcpsr(フラグやプロセッサモードの情報が格納されているレジスタ)に反映する。

 例えば上記の例だとこうなる。

1. r0 - #0を計算。(r0には既に0が代入されているため、演算結果は0)
2. 演算結果が0になるため、Zフラグが設定される。
3. `eq`の実行はZフラグが設定されていることが条件であるため、Zフラグが設定されているとき`beq <label>`ではlabelへと分岐する。

 ここで本題へ戻る。上記の例は一見無駄がないように見えるかもしれない。ここで登場するのが、フラグフィールドを更新できる便利なサフィックスs。通常の命令の末尾にsをつけることで、演算結果をcpsrに反映できる。
 これを踏まえて上記のコードを書き直してみる。

movs r0, #0
beq <label>

 1行減らすことができた。movsを使用した時点で、movsの第一オペランドにある演算結果はcpsrに反映される。今回の演算結果は0であるため、わざわざcmp命令を使用しなくてもZフラグを設定することができる。
 今回の例ではたまたま演算結果が0であったため分岐に使用できたが、毎回そううまくいくとは限らない。また、cmp命令を省くとあるときに比べて読みづらくなる場合がある。あくまでショートコーディングの観点からすれば、こういう方法もある、ということである。

バレルシフタの活用

 バレルシフタとは、シフト処理を行うデジタル回路のことである。様々な命令に使用することができ、これを活用することでレジスタの値をシフト処理して使用したり、即値に指定できない値を指定したりすることができる。なお、バレルシフタが利用できる命令はムーブ命令(mov, mvn)、加算命令(add, adc)、減算命令(sub, sbc, rsb, rsc)、比較命令(cmp, cmn)、テスト命令(tst, teq)、論理演算命令(and, orr, eor, bic)である。

 例えば以下のコードは1命令で実行できる。

mov r0, #0x80000000

 「即値には8bitしか指定出来ないのでは?」と思った方がいるかもしれないが、これがバレルシフタのトリック。このコードはmov r0, #2, #2と同義で、「2を2bit右ローテートした結果(=0x80000000)をr0に代入」という意味になる。

 他にも、r1の4倍とr0を比較したいとき、

lsl r1, r1, #2    @r1を4倍
cmp r0, r1    @r0とr1を比較

と書くところを、

cmp r0, r1, lsl #2

とすれば1行減らすことができる。また、r1の値も書き換えずに済む。

アドレッシングモードの活用

 これはロード・ストア命令で使用することができる。以下にその種類を挙げる。

プリインデックス

 皆さんおなじみの記法。先に[]内を演算してメモリアドレスを算出し、そのメモリアドレスから値を取得する。

ldr r0, [r1, #4]    @r1+4の位置から取得した値をr0に格納

ライトバック付きプリインデックス

 ライトバック(!)を付けると、ベースアドレスを[]内の演算結果に書き換えることができる。

ldr r0, [r1, #4]!    @r1+4の位置から取得した値をr0に格納、r1にr1+4の値を格納

ポストインデックス

 こちらは先にベースアドレスをロードし、その後ベースアドレスを書き換える。

ldr r0, [r1], #4    @r1のメモリアドレスにある値をr0に格納、r1にr1+4の値を格納

 アドレッシングモードを活用すれば、通常ならadd命令などで加算しなければいけない部分を短縮することができる。

まとめ

 アセンブリで短いコードを書くことは、バイナリサイズの縮小や高速化、脆弱性のコード実証においても非常に重要である。日ごろから心がけるようにしよう。