ぐだぐだ低レベルプログラミング(119)ARM64(AArach64)Floatのロード#2

Joseph Halfmoon

前回より浮動小数点レジスタおよびSIMDレジスタのスカラーに関するメモリからのロード命令の練習を始めました。何度も書いているようにA64の命令も多すぎなら、アドレシング・モードも多すぎ(個人の感想です。)下手をするとオペランドのビット幅のバリエーションが多い分、整数ロード命令より組み合わせ多いんでないの。

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

A64のアドレシング・モードについて

整数ロード/ストア命令で使えるアドレシング・モードは皆使えるみたいです。アドレシングモードについては以下の回で「図を描きながら」一通りおさらいしてみています。

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

整数ロード/ストア命令はオペランドのビット幅と符号拡張を指示するのにやたらニーモニックが増えていたことにくらべると、浮動小数点/SIMDのロード/ストア命令はニーモニックの種類が少ないのでありがたいデス。しかし整数型よりも、扱えるオペランドのビット幅のバリエーションが増えとります。ビット幅によりアドレシングモードのオフセット計算も影響受けるので組み合わせが増えている気がしないでもないです。

今回実験のアセンブリ言語関数

「いつものように手抜きな」関数プロローグもエピローグもない被テスト関数が以下に。今回は以下の5つのアドレシングモードを練習してみます。組み合わせがメンドイので例によってfloat型(単精度浮動小数)のみです。

    • ベースレジスタ+64ビット幅インデックスレジスタ(シフト無)
    • ベースレジスタ+64ビット幅インデックスレジスタ(シフト有)
    • ベースレジスタ+32ビット幅インデックスレジスタ(符合なし拡張)
    • ベースレジスタ+32ビット幅インデックスレジスタ(符合なし拡張、シフト有)
    • PC相対
.globl	fldrSRegLSL0, fldrSRegLSL2, fldrSRegUXTW0, fldrSRegUXTW2, fldrSPC 
.text
.balign	4

fldrSRegLSL0:
    ldr s0, [x0, x1]
    ret

fldrSRegLSL2:
    ldr s0, [x0, x1, LSL #2]
    ret

fldrSRegUXTW0:
    ldr s0, [x0, w1, UXTW]
    ret

fldrSRegUXTW2:
    ldr s0, [x0, w1, UXTW #2]
    ret

fldrSPC:
    ldr s0, datalabel
    ret

.balign 4
datalabel:
    .single 1.2345e3
C言語記述のmain関数

いつも通りの「通り一遍さわるだけの手抜きな」C言語記述のテスト駆動部です。float型を構造体メモリからロードしてます。インデックスレジスタを使う4種関数では全て同じメモリを指してそこからロードしています。今回「割愛」してますが実はインデックスの符合付拡張もありなので、とても全部やりきれないっと。

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

typedef struct {
    float s0;
    float s1;
    float s2;
} TargetMEM;

TargetMEM tgt;

extern float    fldrSRegLSL0(TargetMEM *, uint64_t);
extern float    fldrSRegLSL2(TargetMEM *, uint64_t);
extern float    fldrSRegUXTW0(TargetMEM *, uint32_t);
extern float    fldrSRegUXTW2(TargetMEM *, uint32_t);
extern float	fldrSPC();

void initTGT() {
    tgt.s0 = 1.2345f;
    tgt.s1 = 2.0001f;
    tgt.s2 = 3.1415f;
}

int main(void) {
    float resultS;

    initTGT();

    resultS =fldrSRegLSL0(&tgt, 4);
    printf ("fldrSRegLSL0: %f\n", resultS);

    resultS =fldrSRegLSL2(&tgt, 1);
    printf ("fldrSRegLSL2: %f\n", resultS);

    resultS =fldrSRegUXTW0(&tgt, 4);
    printf ("fldrSRegUXTW0: %f\n", resultS);

    resultS =fldrSRegUXTW2(&tgt, 1);
    printf ("fldrSRegUXTW2: %f\n", resultS);

    resultS =fldrSPC();
    printf ("fldrSPC: %e\n", resultS);

    return 0;
}
ビルドして実行

ビルドして実行した結果が以下に。fldrRegResults

ベース・レジスタ+インデックス・レジスタの4種は、いろいろアドレシングモード使って、結局同じデータを読んでますな。予定通り。PC相対の方はアセンブラソース中のデータをロードできてます。

ぐだぐだ低レベルプログラミング(118)ARM64(AArach64)Floatのロード#1

ぐだぐだ低レベルプログラミング(120)ARM64(AArach64)Floatのストア#1