分岐命令 ()
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 // 呼び出し元の次の命令に戻る
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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, 条件
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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 システムコールを使う必要があります。
命令エンコード
命令 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
SVC | 1 1 0 1 0 1 0 0 | 0 0 0 | imm16 | 0 0 0 | 0 1 |
これまでで整数型の命令は一通り終わりました。 次回からは浮動小数点演算を予定しています。