ぐだぐだ低レベルプログラミング(74) ARM64(AArch64)、BFM命令、別名不在

Joseph Halfmoon

今回は、前々回調べたビットフィールドMOV命令の実習です。最初から躓きました。BFM命令の別名(エイリアス)のBFC命令、アセンブラに拒否られました。実体命令であるBFMは存在しているんですがね。そのくせディスアセンブラは頼みもしないのにBFM命令をBFC命令にディスアセンブルしてくれます。いろいろあるのね、きっと。

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

※今回からARM64のアセンブリ言語命令をエクササイズするために使用する機材を変更いたしました(ラズパイ4の64ビット化の件はこちら。)

    • 今回より Raspberry Pi 4 model B、Cortex-A72コア ARMv8-A
    • 前回まで「普及価格帯のAndroidスマホ」Cortex-A73/Cortex-A53 ARMv8-A

またこれに伴いビルド環境も変わりました。

    • 今回より Raspberry Pi OS(64bit) 上の gccツールチェーン
    • 前回まで  Android上のTermux環境、clang/llvm ツールチェーン

環境は変わったのですが、以下の「別名問題」にはなすすべなし。。。

「別名(エイリアス)」BFCが不在な件

今回、BFM命令(ビットフィールド転送)とその仲間たち(エイリアス)を動かしてみようということでコードを書いたらアセンブラ(gas)が以下のエラーメッセージを発してきました。AssemblerError

ムムム、bfc命令(ビットフィールドクリア)命令はこのプロセッサ(Cortex-A72)ではサポートされておらん、とな。しかし、bfm命令(ビットフィールド転送)命令は問題なくアセンブルされとります。

bfc命令の実体命令はbfm命令(bfcはbfmのエイリアス)

つまり実体あるのに表現できないだけ。慌てて Arm Architecture Reference Manual 調べてみたらBFC命令のところに「小さく」書いてありました。

ARMv8.2

今回から使用している Cortex-A72も、前回まで使用していた Cortex-A73も

ARMv8

です。新しいコアでは「エイリアスが使える」のだけれど、古いA72では許されんと(実体命令あるから所望の操作はできるのに)

再び慌ててBFM、SBFM、UBFMのBFM3兄弟命令のエイリアス群(冒頭のアイキャッチ画像をご参照ください)についてバージョン調査してみました。上記の断り書きがあるのは

BFC命令のみ

でした。最初から何か大人の事情のあるところを踏んずけてしまったのね。トホホ。

そのくせ、後でお見せいたしますが、ディスアセンブラ(objdump -d)はBFMで表現したBFC相当の命令を「ちゃんと」BFC命令に逆アセンブルしてくれるのですよ。なんだかな~。

今回練習するBFM命令とそのエイリアス

ビットフィールド転送命令、BFM、わかったような分からぬような命令なので、前々回に作製した説明図を再掲させていただきます。図でみると分かった気になるのだけれど。。。BFM

さて、ほぼ1命令=1関数形式のアセンブリ言語ソースが以下に。bfmWとbfmX関数は、本当はBFC命令のテストにするつもりのもの。BFC命令表記した方がその意味が分かり易いです。

残りの4関数はエイリアスが使えるので、殊更に同じ命令を2通りの表記で並べてあります。

    • 上に本体命令であるBFMでの表記
    • 下にエイリアス表記

上下の表記は異なるのですが、同じことをしています。

.globl	bfmW, bfmX, bfiW, bfiX, bfxilW, bfxilX
.text
.balign	4

bfmW:
    bfm		w0, wzr, #16, #3
    ret

bfmX:
    bfm		x0, xzr, #48, #3
    ret

bfiW:
    bfm		w0, w1, #24, #3
    bfi         w0, w1, #8, #4
    ret

bfiX:
    bfm		x0, x1, #56, #3
    bfi         x0, x1, #8, #4
    ret

bfxilW:
    bfm		w0, w1, #8, #11
    bfxil       w0, w1, #8, #4
    ret

bfxilX:
    bfm		x0, x1, #8, #11
    bfxil       x0, x1, #8, #4
    ret

ちなみにエイリアスbfcが使えなかった bfmWとbfmXをディスアセンブルしてみると、以下のようにbfcで返ってきます。ちょっと意地悪だな。

DISASMbfc

また、ことさらにBFM表記とエイリアス表記を並べた残りの4関数は、全てエイリアス表記でディスアセンブルされます。BFMの即値の記載より、エイリアス表記の即値表記の方がナンボか分かり易いデス。DISASM

アセンブラ関数を呼び出してテストするCのコード

通り一遍、さらっと触るだけのCのmain()関数が以下に。前回までの clang/llvm環境では <stdint.h>不要だったのですが、今回からはインクルードしています。

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

#define TSTV  (0xFFFFFFFE)

extern uint32_t bfmW(int32_t);
extern uint64_t bfmX(int64_t);
extern uint32_t bfiW(int32_t, uint32_t);
extern uint64_t bfiX(int64_t, uint64_t);
extern uint32_t bfxilW(int32_t, uint32_t);
extern uint64_t bfxilX(int64_t, uint64_t);

int main(void)
{
    uint32_t result;
    uint64_t resultX;

    result = bfmW(-1);
    printf ("bfmW: %08x\n", result);
    resultX = bfmX(-1);
    printf ("bfmX: %lx\n", resultX);
    result = bfiW(-1, 0x12345678);
    printf ("bfiW: %08x\n", result);
    resultX = bfiX(-1, 0x1122334455667788);
    printf ("bfiX: %lx\n", resultX);
    result = bfxilW(-1, 0x12345678);
    printf ("bfxilW: %08x\n", result);
    resultX = bfxilX(-1, 0x1122334455667788);
    printf ("bfxilX: %lx\n", resultX);

    return 0;
}
ビルドして実行

今回からのgcc環境では以下です。

$ gcc -g -O0 -o bfm bfm.c bfm.s

実行した結果が以下に。

Result

多分大丈夫だよね。いい加減だな。

ぐだぐだ低レベルプログラミング(73) ARM64(AArch64)、MOV命令の実習 へ戻る

ぐだぐだ低レベルプログラミング(75) ARM64(AArch64)、SBFM命令、変幻自在 へ進む