分岐命令 ()

CPU はメモリに格納されている32ビット (4バイト) の命令をメモリアドレスの 小さい方から大きい方に向かって順に実行します。 この命令の実行順を変更する 命令が分岐命令です。 実行中の命令のメモリアドレスはプログラムカウンタ (PC) が保持していますが、分岐命令はプログラムカウンタの値を書き換える命令と 考えることができます。 実行するメモリアドレスに無条件にジャンプする 「B」と「BR」、呼び出し元(ジャンプした次の命令)に戻る準備をして分岐する 「BL」と「BLR」、呼び出し元へジャンプして 戻る「RET」、条件フラグの値によって分岐したり、直後の命令を順に実行したりする 条件分岐命令(B.EQ、B.GTなど) があります。


無条件分岐命令

B

分岐命令の B は、プログラムカウンタの値にオフセットを加えたメモリアドレスに 分岐(ジャンプ)します。 分岐先の命令に付けたラベルを分岐先として指定しますが、 アセンブラが「B」命令の アドレスとラベルのアドレスから、プログラムカウンタ相対オフセットを計算します。 オフセットの値は26ビットの符号付整数ですが、プログラムカウンタの値が常に 4の倍数であることを利用するため、分岐先は ±128MB の範囲が可能です。

    B     label

BR

分岐命令の BR は、分岐先のメモリアドレスを汎用レジスタに格納して実行することで 、汎用レジスタが格納しているメモリアドレスへの無条件分岐を行います。

    BR    Xn

BL

リンク付分岐命令の BL は、プログラムカウンタの値にオフセットを加えた メモリアドレスに分岐(ジャンプ)します。 分岐先の命令に付けたラベルを分岐先として指定しますが、 アセンブラが「BL」命令のアドレスとラベルのアドレスから、 プログラムカウンタ相対オフセットを計算します。 オフセットの値は内部的には26ビットの符号付整数ですが、 プログラムカウンタの値は常に4の倍数であることを利用するため、 分岐先は ±128MB の範囲が可能です。

BL 命令はプログラムカウンタに4を加えた値(次の命令のアドレス)を、 汎用レジスタのX30 (リンクレジスタ) に設定した後に分岐します。 分岐先でRET命令を実行することで、BL命令の直後の命令に 戻ることができます

    BL    label

BLR

リンク付分岐命令の BLR は、分岐先のメモリアドレスを汎用レジスタに格納して 実行することで、汎用レジスタが格納しているメモリアドレスへの無条件分岐を行います。 リンクレジスタ X30 にプログラムカウンタに4を加えた値(次の命令のアドレス)を 設定します。 分岐先でRET命令を実行することで、BLR命令の 直後の命令に戻ることができます

    BLR   Xn

RET

RET (Return) 命令は、指定したレジスタのアドレスに無条件分岐します。 レジスタを指定しない場合はリンクレジスタ(X30) を指定したことになります。 サブルーチンから戻る命令として使われます。

    RET   { Xn }

サブルーチン呼び出しの例

サブルーチン呼び出しの典型的なコードです。呼ばれた先 (サブルーチン側) で リンクレジスタ (X30) の値を書き換えると戻り先が分からなくなります。 サブルーチン内で、もう一度サブルーチン呼び出しのために BL 命令を使うと X30 が 書き変わることに注意してください。呼び出された直後に「stp x0, x30, [sp, #-16]!」で リンクレジスタ (X30) をスタックに保存し、戻る直前に「ldp x0, x30, [sp], #16」で 戻すようにします。STR/LDR ではなく STP/LDP を使っている理由は、スタックポインタは 16で割り切れる値にしておく必要があるためです。 STP/LDP はレジスタ 2つ、 16バイトを単位とした読み書きになるために使っています。以下の例では X30 とともに X0、X1、X2 を保存していますが、ローカル変数に使う場合の退避/復帰の例です。

    // 呼び出し元
    Main:

        

        bl      Sub                   // サブルーチン呼び出し
        mov     x0, xzr               // X30に格納される戻り先

        

    // サブルーチン
    Sub:                              // このラベルにジャンプ
        stp     x0, x30, [sp, #-16]!  // リンクレジスタ(X30)の退避
        stp     x1, x2,  [sp, #-16]!  // レジスタの PUSH の例

        // 何か処理

        ldp     x1, x2,  [sp], #16    // レジスタの POP の例
        ldp     x0, x30, [sp], #16    // リンクレジスタ(X30)の復帰
        ret                           // 呼び出し元の次の命令に戻る

命令エンコード

命令 313029 282726 252423 222120 191817 161514 131211 100908 070605 040302 0100
B 0 0 0 1 0 1 imm26
BL 1 0 0 1 0 1 imm26
BR 1 1 0 1 0 1 1 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 Rn 0 0 0 0 0
BLR 1 1 0 1 0 1 1 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 Rn 0 0 0 0 0
RET 1 1 0 1 0 1 1 0 0 1 0 1 1 1 1 1 0 0 0 0 0 0 Rn 0 0 0 0 0



条件分岐命令

条件によって処理の流れを変えたり、処理の繰り返しに使う命令です。 条件フラグの状態によって分岐が決まる条件分岐命令(B.条件)は、条件を指定する サフィックスを付けるため 16 種類になります。 B.EQ、B.NE、B.GEなどのように使います。 分岐先のアドレスは4で割り切れる (word-aligned) 必要があります。 また、汎用レジスタの値がゼロ/非ゼロ、汎用レジスタの特定のビットが ゼロ/非ゼロで分岐する条件分岐命令 (CBZ / CBNZ / TBZ / TBNZ)もあります。

分岐の条件は次のサフィックスの1つで指定します。 サフィックスは条件フラグ (NZCV レジスタ) の値の組み合わせで次のように決まっています。

cond サフィックス 意味 フラグ
0000 EQ 等しい Z = 1
0001 NE 等しくない Z = 0
0010 CS or HS キャリーセット C = 1
0011 CC or LO キャリークリア C = 0
0100 MI N = 1
0101 PL ゼロまたは正 N = 0
0110 VS オーバーフロー V = 1
0111 VC オーバーフローなし V = 0
1000 HI > 大きい (符号無し) (C = 1) and (Z = 0)
1001 LS ≦ 小さいか等しい (符号無し) (C = 0) or (Z =1)
1010 GE ≧ 大きいか等しい (符号付) N = V
1011 LT < 小さい (符号付) N <> V
1100 GT > 大きい (符号付) (Z = 0) and (N = V)
1101 LE ≦ 小さいか等しい (符号付) (Z = 1) or (N <> V)
1110 AL Always Any
1111 NV Always Any

B.条件

条件分岐命令は 「B.条件指定のサフィックス」という形式です。 分岐先の命令に付けたラベルを分岐先として指定しますが、 アセンブラがこの命令のアドレス(PC)からのオフセットに変換します。 オフセットは ±1 MB の範囲となります。

    B.EQ    label
    B.NE    label
    B.CS    label         // B.HS も同じ
    B.CC    label         // B.LO も同じ
    B.MI    label
    B.PL    label
    B.VS    label
    B.VC    label
    B.HI    label
    B.LS    label
    B.GE    label
    B.LT    label
    B.GT    label
    B.LE    label

CBZ

汎用レジスタ(Rt)の値が 0 ならばラベルに分岐します。 分岐先へのオフセットは ±1 MB の範囲となります。 条件フラグの内容は変化しません。

    CBZ     Wt, label
    CBZ     Xt, label

CBNZ

汎用レジスタ(Rt)の値が 0 でなければ、ラベルに分岐します。 分岐先へのオフセットは ±1 MB の範囲となります。 条件フラグの内容は変化しません。

    CBNZ    Wt, label
    CBNZ    Xt, label

TBZ

TBZ(Test bit and Branch if Zero)命令は汎用レジスタ(Rt)の imm で指定した ビット(0..63) が 0 ならばラベルに分岐します。 分岐先へのオフセットは ±32KB の範囲となります。 条件フラグの内容は変化しません。

    TBZ     Wt, #imm, label
    TBZ     Xt, #imm, label

TBNZ

TBNZ (Test bit and Branch if Nonero) 命令は汎用レジスタ(Rt)の imm で指定した ビット(0..63) が 1 ならばラベルに分岐します。 分岐先へのオフセットは ±32KB の範囲となります。 条件フラグの内容は変化しません。

    TBNZ    Wt, #imm, label
    TBNZ    Xt, #imm, label

命令エンコード

命令 313029 282726 252423 222120 191817 161514 131211 100908 070605 040302 0100
B.cond 0 1 0 1 0 1 0 0 imm19 (4倍されて, ±1MB) 0 cond
CBZ z 0 1 1 0 1 0 0 imm19 (4倍されて, ±1MB) Rt
CBNZ z 0 1 1 0 1 0 1 imm19 (4倍されて, ±1MB) Rt
TBZ b5 0 1 1 0 1 1 0 b40 imm14 (4倍して±32KB) Rt
TBNZ b5 0 1 1 0 1 1 1 b40 imm14 (4倍して±32KB) Rt


条件実行命令

ほとんどの命令が条件によって実行/スキップできる ARM32 と異なり、 ARM64では代入と比較の一部の機能だけ条件実行できます。

条件代入

CSEL

条件が真ならば Rd に Rn を返します。条件が偽ならば Rd に Rm を返します。 条件はサフィックスで指定します。

    CSEL   Wd, Wn, Wm, 条件
    CSEL   Xd, Xn, Xm, 条件

CSET

条件が真ならば Rd に 1 を返し、偽ならば Rd に 0 を返します。 条件はサフィックスで指定します。

    CSET   Wd, 条件          //  CSINC Wd, WZR, WZR, 逆条件
    CSET   Xd, 条件          //  CSINC Xd, XZR, XZR, 逆条件

CSINC

条件が真ならば Xd に Rn を返します。 条件が偽ならば Rm に1を加えた値を Rd に返します。 条件はサフィックスで指定します。

    CSINC  Wd, Wn, Wm, 条件
    CSINC  Xd, Xn, Xm, 条件

CSINV

条件が真ならば Rd に Rn を返します。 条件が偽ならば Rm のビット毎の反転(1の補数)を Rd に返します。 条件はサフィックスで指定します。

    CSINV  Wd, Wn, Wm, 条件
    CSINV  Xd, Xn, Xm, 条件

CSNEG

条件が真ならば Rd に Rn を返します。 条件が偽ならば Rm が符号付整数として2の補数(正負反転)を Rd に返します。 条件はサフィックスで指定します。

    CSINV  Wd, Wn, Wm, 条件
    CSINV  Xd, Xn, Xm, 条件

命令エンコード

命令 313029 282726 252423 222120 191817 161514 131211 100908 070605 040302 0100
CSEL z 0 0 1 1 0 1 0 1 0 0 Rm cond 0 0 Rn Rd
CSINC z 0 0 1 1 0 1 0 1 0 0 Rm cond 0 1 Rn Rd
CSINV z 1 0 1 1 0 1 0 1 0 0 Rm cond 0 0 Rn Rd
CSNEG z 1 0 1 1 0 1 0 1 0 0 Rm cond 0 1 Rn Rd

条件比較

CCMP (イミディエート)

指定した条件(サフィックス)が真の場合は、 汎用レジスタと5ビットの符号なし整数 (0..31) を比較した結果で 条件フラグを設定し、指定した条件(サフィックス)が 偽の場合は、4ビットのNZCV定数(0..15)を条件フラグに設定します。 4ビットのNZCV定数(0..15)は、ビット3 が Nフラグ、ビット 2 が Z フラグ、 ビット 1 が C フラグ、ビット 0 が V フラグとなります。

    // NZCV = if cond then CMP(Wn, uimm) else nzcv.

    CCMP Wn, #imm, #nzcv, cond
    CCMP Xn, #imm, #nzcv, cond

CCMP (レジスタ)

指定した条件(サフィックス)が真の場合は、 汎用レジスタ Rn と Rm を比較した結果で条件フラグを設定し、 指定した条件(サフィックス)が偽の場合は、 4ビットのNZCV定数(0..15)を条件フラグに設定します。 4ビットのNZCV定数(0..15)は、ビット3 が Nフラグ、ビット 2 が Z フラグ、 ビット 1 が C フラグ、ビット 0 が V フラグとなります。

    // NZCV = if cond then CMP(Rn,Rm) else uimm4.

    CCMP    Wn, Wm, #nzcv, cond
    CCMP    Xn, Xm, #nzcv, cond

CCMN (イミディエート)

指定した条件(サフィックス)が真の場合は、 汎用レジスタと、5ビットの符号なし整数 (0..31) に -1 を乗じた数を比較した結果で 条件フラグを設定し、指定した条件(サフィックス)が 偽の場合は、4ビットのNZCV定数(0..15)を条件フラグに設定します。 4ビットのNZCV定数(0..15)は、ビット3 が Nフラグ、ビット 2 が Z フラグ、 ビット 1 が C フラグ、ビット 0 が V フラグとなります。

    // NZCV = if cond then CMP(Xn,-uimm5) else uimm4.

    CCMN   Wn, #imm, #nzcv, cond
    CCMN   Xn, #imm, #nzcv, cond

CCMN (レジスタ)

指定した条件(サフィックス)が真の場合は、 汎用レジスタ Rn と、Rm に-1を乗じた数を比較した結果で条件フラグを設定し、 指定した条件(サフィックス)が偽の場合は、 4ビットのNZCV定数(0..15)を条件フラグに設定します。 4ビットのNZCV定数(0..15)は、ビット3 が Nフラグ、ビット 2 が Z フラグ、 ビット 1 が C フラグ、ビット 0 が V フラグとなります。

    // NZCV = if cond then CMP(Rn,-Rm) else uimm4.

    CCMN   Wn, Wm, #nzcv, cond
    CCMN   Xn, Xm, #nzcv, cond

命令エンコード

命令 313029 282726 252423 222120 191817 161514 131211 100908 070605 040302 0100
CCMN (immediate) z 0 1 1 1 0 1 0 0 1 0 imm5 cond 1 0 Rn 0 nzcv
CCMN (register) z 0 1 1 1 0 1 0 0 1 0 Rm cond 0 0 Rn 0 nzcv
CCMP (immediate) z 1 1 1 1 0 1 0 0 1 0 imm5 cond 1 0 Rn 0 nzcv
CCMP (register) z 1 1 1 1 0 1 0 0 1 0 Rm cond 0 0 Rn 0 nzcv

その他の命令

ビット操作

REV

レジスタ(Rn) の内容のバイト順序を逆転して、指定したレジスタ (Rd) に格納します。

  REV Wd, Wn
  REV Xd, Xn

REV16

レジスタ(Rn) の内容の16ビットハーフワードのバイト順序を逆転して、指定したレジスタ (Rd) に格納します。

  REV16  Wd, Wn
  REV16  Xd, Xn

REV32

レジスタ(Xn) の内容の32ビットワードのバイト順序を逆転して、指定したレジスタ (Xd) に格納します。

  REV16  Xd, Xn

RBIT

レジスタ(Rn) の内容のビット順序を逆転して、指定したレジスタ (Rd) に格納します。

  RBIT Wd, Wn
  RBIT Xd, Xn

CLS

先行符号ビットカウント、Rn レジスタの最上位ビットから連続する 1 と なっているビット数を Rd レジスタに返します。

  CLS   Wd, Wn
  CLS   Xd, Xn

CLZ

先行ゼロカウント、Rn レジスタの最上位ビットから連続する 0 と なっているビット数を Rd レジスタに返します。

  CLZ   Wd, Wn
  CLZ   Xd, Xn

命令エンコード

命令 313029 282726 252423 222120 191817 161514 131211 100908 070605 040302 0100
REV (32bit) 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 Rn Rd
REV (64bit) 1 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 Rn Rd
REV16 z 1 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 Rn Rd
REV32 1 1 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 Rn Rd
RBIT z 1 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 Rn Rd
CLS z 1 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 Rn Rd
CLZ z 1 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 Rn Rd

ステータスレジスタの操作

MRSおよびMSR命令はシステムレジスタ汎用レジスタ間の読み書きを行う命令です。

MRS

MRS命令はシステムレジスタの内容を汎用レジスタへ読み出します。NZCV (フラグレジスタ) を汎用レジスタへ読み出すには次のように記述します。

    MRS    Xt,   NZCV

MSR

MSR 命令は汎用レジスタの内容または定数をシステムレジスタへ書き込みます。

    MSR    NZCV, Xt
    MSR    NZCV, #imm

命令エンコード

命令 313029 282726 252423 222120 191817 161514 131211 100908 070605 040302 0100
MRS 1 1 0 1 0 1 0 1 0 0 1 1 o op1 CRn CRm op2 Rt
MSR 1 1 0 1 0 1 0 1 0 0 0 1 o op1 CRn CRm op2 Rt
MSR (imm) 1 1 0 1 0 1 0 1 0 0 0 0 0 op1 0 1 0 0 CRm op2 1 1 1 1 1

システムコール

SVC (Supervisor Call) 命令はシステムコール用の命令です。

SVC

Linux では x8 レジスタにシステムコール番号、x0 から x5 にシステムコールの 引数を設定して SVC 命令を実行します。 システムコールの結果は x0 レジスタに返ります。

    SVC     #0

ARM64 Linux のシステムコールを直接使った「hello、world」です。

.text
        .global _start
_start:
        mov     x2,  #13    // x2  length
        adr     x1,  msg    // x1  string address
        mov     x0,  #1     // x0  stdout
        mov     x8,  #64    // sys_write
        svc     #0
        mov     x0,  xzr
        mov     x8,  #93    // sys_exit
        svc     #0
msg:
        .asciz  "hello, world\n"

ARM64 Linux のシステムコール番号の一覧は こちらのファイル(github) を見てください。1000番以降は使用できません。例えば、ARM64 ではファイルをオープンする場合に使う open システムコールは使えなくなっていて、openat システムコールを使う必要があります。

命令エンコード

命令 313029 282726 252423 222120 191817 161514 131211 100908 070605 040302 0100
SVC 1 1 0 1 0 1 0 0 0 0 0 imm16 0 0 0 0 1


これまでで整数型の命令は一通り終わりました。 次回からは浮動小数点演算を予定しています。

浮動小数点演算 に続く...




このページの目次