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

Joseph Halfmoon

さらっとニーモニックを一通り撫でるつもりでも命令多すぎ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命令です。

    1. STUR
    2. STP
    3. 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 命令は「アンスケールド」なオフセットを持つ命令です。この様子を比べるためにまずは上記のアセンブリソースに対応するオブジェクトをディスアセンブルしてみましたぞ。該当部分はこんな感じ。

str2DISASM

上記をみて、すんなりA64の命令エンコードが「見える」ほどには慣れておりませぬ。例によって、チマチマと解読してみたものが以下に。str2URencode

たしかに 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;
}
ビルドして実行

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

str2Results

予定どおりの動作をしとりますがや。そろそろ「次の単元」に行きたいデス。

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

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