ぐだぐだ低レベルプログラミング(97)ARM64(AArach64)、条件分岐B.cond

Joseph Halfmoon

前回までロードストア命令を実習してきました。まだまだロードストアあるのですが別件実習してからの方がよさそうな奴らばかりなので一たんお休み。今回から分岐命令に入りたいと思います。最初は条件分岐命令です。どのプロセッサも「似たようなもん」ですが、条件の組み合わせを全部通すのはメンドイです。

※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら

※実機確認には以下を使用しております。

    • Raspberry Pi 4 model B、Cortex-A72コア(ARMv8-A)
    • Raspberry Pi OS (64bit) bullseye
    • gcc (Debian 10.2.1-6) 10.2.1 20210110

ARMv8もいろいろレベルがあり、Arm Cortex-A72はARMv8の中でもベーシックな(命令数の少ない)ARMv8p0です。

※A64の最新のマニュアルは以下でダウンロード可能です。毎月レビジョンヒストリが更新されているみたいっす。

Arm Architecture Reference Manual for A-profile architecture

条件付き分岐命令

条件付き分岐命令にはコードすべきことは2つしかありません。

B.cond label

condのところに分岐する条件2文字を記するのと、飛び先ラベルを記することです。条件2文字はこんな感じ。

条件シンボル いい加減な説明
EQ 等しい
NE 等しくない
CS キャリーセット
CC キャリークリア
MI マイナス
PL プラス
VS オーバフローセット
VC オーバーフロークリア
HI ハイ(符合無)
LS ハイでない(符合無)
GE 大きいか等しい
LT 小さい
GT 大きい
LE 小さいか等しい
AL いつでも

とび先ラベルはエンコード的には19ビットですが、ワード境界アライメントで、2ビット下駄を履かせるので21ビット相当の範囲(±1Mバイト)まで指定することができます。

実験に使ったアセンブリ言語ソース

いつものように手抜き(関数プロローグもエピローグもない)なものですが、条件フラグを操作しないことには何もできないので、

    1. msr NZCV, x0命令で第一引数の値をNZCVに突っ込む
    2. それを受けて条件分岐
    3. 条件成立すれば1を返す
    4. 不成立なら0を返す

というスタイルです。その上、例えば、EQとNEという「対」になる条件の場合、EQのみをあからさまに判定し、NEはEQでないとき、ということであからさまには書いてないっす。それでも数が多いです。ソースが以下に。

.globl	bEQ_NE, bCS_CC, bMI_PL, bVS_VC, bHI_LS, bGE_LT, bGT_LE, bAL
.text
.balign	4

bEQ_NE:
    msr NZCV, x0
    b.EQ lblEQ
    mov x0, 0
    ret
lblEQ:
    mov x0, 1
    ret

bCS_CC:
    msr NZCV, x0
    b.CS lblCS
    mov x0, 0
    ret
lblCS:
    mov x0, 1
    ret

bMI_PL:
    msr NZCV, x0
    b.MI lblMI
    mov x0, 0
    ret
lblMI:
    mov x0, 1
    ret

bVS_VC:
    msr NZCV, x0
    b.VS lblVS
    mov x0, 0
    ret
lblVS:
    mov x0, 1
    ret

bHI_LS:
    msr NZCV, x0
    b.HI lblHI
    mov x0, 0
    ret
lblHI:
    mov x0, 1
    ret

bGE_LT:
    msr NZCV, x0
    b.GE lblGE
    mov x0, 0
    ret
lblGE:
    mov x0, 1
    ret

bGT_LE:
    msr NZCV, x0
    b.GT lblGT
    mov x0, 0
    ret
lblGT:
    mov x0, 1
    ret

bAL:
    msr NZCV, x0
    b.AL lblAL
# Never!
    mov x0, 0
    ret
lblAL:
    mov x0, 1
    ret
実験に使用したC言語ソース

上記のアセンブリ言語関数を呼び出すテスト用のCソースが以下に。

#include <stdio.h>
#include <stdint.h>

#define NF	(0x80000000)
#define ZF	(0x40000000)
#define CF	(0x20000000)
#define VF	(0x10000000)

extern uint32_t bEQ_NE(uint64_t);
extern uint32_t bCS_CC(uint64_t);
extern uint32_t bMI_PL(uint64_t);
extern uint32_t bVS_VC(uint64_t);
extern uint32_t bHI_LS(uint64_t);
extern uint32_t bGE_LT(uint64_t);
extern uint32_t bGT_LE(uint64_t);
extern uint32_t bAL   (uint64_t);

int main(void)
{
    uint32_t result;

    result = bEQ_NE(ZF);
    printf ("EQ: %d\n", result);
    result = bEQ_NE(0);
    printf ("NE: %d\n", result);
    result = bCS_CC(CF);
    printf ("CS: %d\n", result);
    result = bCS_CC(0);
    printf ("CC: %d\n", result);
    result = bMI_PL(NF);
    printf ("MI: %d\n", result);
    result = bMI_PL(0);
    printf ("PL: %d\n", result);
    result = bVS_VC(VF);
    printf ("VS: %d\n", result);
    result = bVS_VC(0);
    printf ("VC: %d\n", result);
    result = bHI_LS(CF);
    printf ("HI: %d\n", result);
    result = bHI_LS(CF | ZF);
    printf ("LS: %d\n", result);
    result = bGE_LT(NF | VF);
    printf ("GE: %d\n", result);
    result = bGE_LT(0);
    printf ("GE: %d\n", result);
    result = bGE_LT(NF);
    printf ("LT: %d\n", result);
    result = bGE_LT(VF);
    printf ("LT: %d\n", result);
    result = bGT_LE(NF | VF);
    printf ("GT: %d\n", result);
    result = bGT_LE(NF | VF | ZF);
    printf ("LE: %d\n", result);
    result = bAL(0);
    printf ("AL: %d\n", result);

    return 0;
}
ビルドして実行

ビルドして実行したところが以下に。bcondResult

予め代入しておいた条件が、その通りだと判定しているので、動くよなあ。普通。

ぐだぐだ低レベルプログラミング(96)ARM64(AArach64)、ロードストア命令その7 へ戻る

ぐだぐだ低レベルプログラミング(97)ARM64(AArach64)、条件分岐CBNZ族 へ進む