今回は、前々回調べたビットフィールド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)が以下のエラーメッセージを発してきました。
ムムム、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、わかったような分からぬような命令なので、前々回に作製した説明図を再掲させていただきます。図でみると分かった気になるのだけれど。。。
さて、ほぼ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で返ってきます。ちょっと意地悪だな。
また、ことさらにBFM表記とエイリアス表記を並べた残りの4関数は、全てエイリアス表記でディスアセンブルされます。BFMの即値の記載より、エイリアス表記の即値表記の方がナンボか分かり易いデス。
アセンブラ関数を呼び出してテストする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
実行した結果が以下に。
多分大丈夫だよね。いい加減だな。