bamboo’s blog

Bambooの気まぐれブログ

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命令を積極的に使用することで、容量の削減や性能向上が期待できる。ぜひ活用してみよう。