さらっとニーモニックを一通り撫でるつもりでも命令多すぎArmです。アドレッシングモードはロード命令でやったし、データサイズは幅だけだからなどと言い訳を言って、大幅に手を抜きました、ハイ。今回はストア命令の「落穂ひろい」編であります。微妙にロードとは非対称だけれども大体はあるのだ、ストアにも。
※「ぐだぐだ低レベルプログラミング」投稿順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
毎月レビジョンヒストリが更新されているみたいです。とても追いかけきれませぬ。
今回練習のストア命令
今回は、アドレシングモードとデータサイズをばっさり割愛、ワード幅(Wレジスタ)だけの操作にしてしまいました。ニーモニック的には以下の3命令です。
-
- STUR
- STP
- STNP
1番のSTURのUは第93回をお読みいただいている方はお分かりのハズ。
Unscaled
です。unsigned ではないので念のため。ベース+オフセットのオフセットアドレスを「スケーリングしない」ための命令であります。アセンブラなんて何よ、という世間の冷たい目(冷たくもないか、眼中にないというべきか)の中でも、まさに重箱の隅的境遇の一品であります。
ただこれをあじあうには、scaledのケースと比較するしかありますまい。そしてエンコードのビット配置に突っ込むしかないのであります。
2番のSTPはロードでも出てきたペアレジスタ操作です。2個のレジスタをスポポンと連続アドレスにストアしてしまいます。
最後3番目のSTNPは、これまた重箱の隅的な一品。メモリ・サブシステム(キャッシュ)に対する 「non-temporal なヒント付」のペアレジスタのストアです。ヒントであるので、有効に活用されることもあれば、そうでないこともある、と。まあ、ハードウエアの気分(ステート)次第。これが有効なケースを無効なケースと識別できるプログラムを書くのはかなりムツカシイです。そのうちやるかもしれないですが、今は遠慮しておきますです。とりあえず表の機能が動いたというだけ。
実験に使ったアセンブリ言語ソース
例によって手抜き(関数プロローグもエピローグもない)1命令1関数スタイルです。ソースが以下に。
.globl strWSR, strWUR, stpW, stnpW .text .balign 4 strWSR: str w0, [x1, #4] ret strWUR: stur w0, [x1, #4] ret stpW: stp w0, w1, [x2, #8] ret stnpW: stnp w0, w1, [x2, #16] ret
さて、関数 strWSR内の str 命令が「スケールド」なオフセットを持つ命令で、関数strWUR内の stur 命令は「アンスケールド」なオフセットを持つ命令です。この様子を比べるためにまずは上記のアセンブリソースに対応するオブジェクトをディスアセンブルしてみましたぞ。該当部分はこんな感じ。
上記をみて、すんなりA64の命令エンコードが「見える」ほどには慣れておりませぬ。例によって、チマチマと解読してみたものが以下に。
たしかに stur 命令はスケーリングしないのね。あたりまえか。
実験に使用したC言語ソース
上記のアセンブリ言語関数を呼び出すテスト用のCソースが以下に。
#include <stdio.h> #include <stdint.h> #define MAXTGT (6) uint32_t tgt[MAXTGT]; uint32_t tgt2[2]; extern void strWSR(uint32_t, uint32_t *); extern void strWUR(uint32_t, uint32_t *); extern void stpW(uint32_t, uint32_t, uint32_t *); extern void stnpW(uint32_t, uint32_t, uint32_t *); void initTGT() { for (int i=0; i<MAXTGT; i++) { tgt[i] = 0; } } int main(void) { initTGT(); strWSR(0x12345678, tgt); //tgt[1] strWUR(0x22222222, tgt2); //tgt2[1] stpW(0xAAAA5555, 0x76543210, tgt); //tgt1[2], tgt1[3] stnpW(0x33345678, 0x1111FFFF, tgt); //tgt1[4], tgt1[5] printf ("tgt[1]: 0x%08x\n", tgt[1]); printf ("tgt2[1]: 0x%08x\n", tgt2[1]); printf ("tgt1[2]: 0x%08x\n", tgt[2]); printf ("tgt1[3]: 0x%08x\n", tgt[3]); printf ("tgt1[4]: 0x%08x\n", tgt[4]); printf ("tgt1[5]: 0x%08x\n", tgt[5]); return 0; }
ビルドして実行
ビルドして実行したところが以下に。
予定どおりの動作をしとりますがや。そろそろ「次の単元」に行きたいデス。