Thumb命令とは(ARM)
はじめに
Thumb命令とは、ARMプロセッサに組み込まれている16bit長の命令である。命令に制限はあるものの、ARM命令の半分の長さで実行できるため、ARM命令と組み合わせることで効率のよいコードを作成することができる。
Thumb関数の書き方
.thumb .thumb_func sample: add r0, r0, r1 bx lr
必要な疑似命令は2つ。.thumb
は以降Thumbコードを生成しろという命令で、.thumb_func
は次の関数がThumbで書かれていることを示す宣言である。試しにこのコードを逆アセンブルしてみると以下のようになる。
00000000 <sample>: 0: 1840 adds r0, r0, r1 2: 4770 bx lr
正しく16bitのThumb命令が生成されていることが分かる。ARM命令に戻す場合は、疑似命令.arm
を使用する。
切り替え方法
ARMモードとThumbモードを切り替えるには、bx
命令、またはblx
命令を使用する。
blx命令
こちらが一般的な切り替え方法。プログラムカウンタの値をリンクレジスタに保存した後、指定したラベルに分岐する。ラベル(相対アドレス)指定の場合、モードの切り替えは自動的に行われる。レジスタを使用して絶対アドレスで分岐する場合(blx ip
など)は、自動切り替えは行われないため注意。
.text .global main .thumb .thumb_func calc: add r0, r0, r1 bx lr .arm main: push {lr} mov r0, #1 mov r1, #2 blx calc mov r1, r0 ldr r0, =str bl printf pop {pc} .data str: .asciz "%d\n"
上記の例では、引数2つを足して返す関数calc
をThumbでコード生成するように指定している。リンクレジスタは分岐する際に上書きされるため、あらかじめスタックに退避しておく。
bx命令
blx命令と同じく命令モードを切り替えられるが、いくつか異なる点がある。
以上の点に注意。Thumbモードに切り替えるには、分岐先の絶対アドレスに1足した値を使用するレジスタにロードする。(最下位bitが1の時はThumbモードに、0の時はARMモードに遷移するという仕様である。)
.text .global main .thumb .thumb_func calc: add r0, r0, r1 bx lr .arm main: push {lr} mov r0, #1 mov r1, #2 ldr ip, =calc mov lr, pc bx ip mov r1, r0 ldr r0, =str bl printf pop {pc} .data str: .asciz "%d\n"
上記の例で「ラベルに+1しなくていいの?」と不思議に思った方がいるかもしれないが、心配ない。.thumb_func
を宣言することで、でcalc関数がThumbの関数であるとアセンブラが解釈してくれるため、自動で+1される。ラベルならこれで大丈夫だが、絶対アドレスを直接ロードする際は+1することを忘れないようにしよう(例えば0x100000に分岐したい場合は0x100001をロードする)。
まとめ
Thumb命令を積極的に使用することで、容量の削減や性能向上が期待できる。ぜひ活用してみよう。