ぐだぐだ低レベルプログラミング(124)ARM64(AArach64)ST1、ベクトルストア

Joseph Halfmoon

前回はLD1命令を練習。1ストラクチャが1要素の一番「シンプル」なベクトルロード命令です。今回はLD1と対になるST1命令をつかってベクトルストアを練習してみます。ベクトルロードしたものを即ストア。折角なので前回未使用だったポスト・インデックス・アドレシングも使用。「シンプル」といいながら1命令の動作がデッカイドー。

※「ぐだぐだ低レベルプログラミング」投稿順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

ベクトル・ストア命令と今回実験の関数

ベクトル・レジスタをオペランドにとるベクトル・ストア命令は、前回のベクトル・ロード命令同様、各要素、バイトからクワッドワードまでのビット幅に対応しています。全部練習するのはメンドイので前回同様に単精度浮動小数(ワード幅、32ビット)だけを練習してお茶を濁します。てへ。

LD1命令でメモリからロードしたベクトルをST1命令でメモリにストアするのですが、同じメモリアドレスに書き戻したのではなんだか分からないので、LD1命令のアドレシングモードにポスト・インデックスを採用し、ロード後のアドレス・レジスタの内容をロードしたベクトルの長さ分ずらしてみます。なお、ポストインデックス処理のアドレス確認のため fld1R4S4p という関数を設けてます。

また、LD1もST1もターゲットとなるレジスタは4本までとれるので、今回はフルに4本ロードして4本ストアすることにしてみます。

結果、「いつものように手抜きな」関数プロローグ無、エピローグ無の被テスト関数ですが、64バイト(単精度浮動小数16個)を一気にロード、および一気にロードしてストアするものが以下に。

.globl	fld1R4S4p, fst1R4S4
.text
.balign	4

fld1R4S4p:
    ld1 {v0.4S, v1.4S, v2.4S, v3.4S}, [x0], #64 
    ret

fst1R4S4:
    ld1 {v0.4S, v1.4S, v2.4S, v3.4S}, [x0], #64 
    st1 {v0.4S, v1.4S, v2.4S, v3.4S}, [x0] 
    ret
C言語記述のmain関数

前回、ベクトルレジスタの内容を直接 gdb で観察したのが、老眼の目に辛かったので、今回はメモリにストアしてprintfで表示してます。この方がお楽(かといって一致比較までソフトで完結してしまうと楽しくないし。)

最初に、ベクトルロード命令のポスト・インデックス・アドレシングでx0が更新されることを確認、つづいて「ロードしたものを即ストア」してみます。

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

#define MAXMEM	(32)
float TargetMEM[MAXMEM];

extern float * fld1R4S4p(float *);
extern void fst1R4S4(float *);

void initTGT(float c) {
    for (int i=0; i < MAXMEM; i++) {
        TargetMEM[i] = c * (i+1);
    }
}

void dumpTGT(const char *arg) {
    printf("%s\n", arg);
    for (int i=0; i < MAXMEM; i++) {
        printf("%d: %f\n", i, TargetMEM[i]);
    }
}

int main(void) {
    float * adr = NULL;
    initTGT(1.001f);

    printf("TargetMEM  ADR=0x%016lx\n", TargetMEM);
    adr = fld1R4S4p(TargetMEM);
    printf("POST-INDEX ADR=0x%016lx\n", adr);

    dumpTGT("Before STORE");
    fst1R4S4(TargetMEM);
    dumpTGT("After STORE");

    return 0;
}
ビルドして実行

コンパイルと実行の最初の部分が以下に。与えたメモリアドレスがPOST-INDEXで0x40バイト(64バイト)分ズレたのが分かりますかね。fld1st1result0

つづいて以下はロード、ストアのメモリ領域の初期値です。このうち先頭側の16個のデータをベクトルレジスタに1命令でロードし、その後、後ろの16個のデータに上書きする予定っす。まずは初期値を覚えて?おいてくだされ。fld1st1result1

ロードして即ストアの結果が以下に。後半の16個のデータが書き換わっているのがわかりますか。fld1st1result2

ロードしたものを即ストアしただけ、一気に64バイトだけれども。

ぐだぐだ低レベルプログラミング(123)ARM64(AArach64)LD1、ベクトルロード へ戻る

ぐだぐだ低レベルプログラミング(125)ARM64(AArach64)LD3、デインタリーブ へ進む