
前回、前々回とA64のSIMD命令のうちソースオペランドを2つ取る演算命令の2タイプを練習しました。今回はソースオペランドを一つだけとる演算命令の代表選手?FSQRTです。前にもそんなこと書いた気がするな。デジャヴか記憶の混濁か?SIMD命令はとっても数が多いので「各パターン」せめて一種類くらいは練習しておこうと。
※「ぐだぐだ低レベルプログラミング」投稿順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
FSQRT(ベクトル)命令
今回練習してみるFSQRT命令の図が以下に。単精度浮動小数4個をベクトルレジスタv1 にロードして、それぞれの平方根をとって結果をベクトルレジスタv0に書き込みます。その後、v0をストア。
前回はデスティネーションオペランドもアキュムレータとして計算にとりこまれるパターンでしたが、今回はそのようなこともなく平和です。
勿論、FSQRT命令は以下の3種類のデータを計算できることになっているのですが、例によってARMv8p0は半精度には対応していないのでこれは練習できず。また、メンドイので単精度のみ実習してお茶を濁すと、いつものとおり。
- 
- 半精度浮動小数
- 単精度浮動小数
- 倍精度浮動小数
 
実験につかったアセンブリ言語記述の被テスト関数
例によって手抜きの関数プロローグ、エピローグ無の被テスト関数です。今回簡単だからパスしても、と思ったですけど、異なる演算(転送)パターン毎に1種類は練習すべしとの最低限のお約束。
.globl	fsqrtv
.text
.balign	4
fsqrtv:
    ld1  {v1.4S}, [x0], #16
    fsqrt v0.4S, v1.4S
    st1  {v0.4S}, [x0]
    ret
C言語記述のmain関数
上記のアセンブリ言語関数を呼び出すmain関数が以下に。FSQRTをやる前のメモリ初期値をダンプ、平方根とった後で再びダンプというお手軽コードです。そして平方根とった結果には、中学生でも暗記しているやつ(今はしないか?)「ひとよひとよにひとみごろ」と「ひとなみにおごれや」が微妙に(単精度浮動小数を10進表記しているので最後の方があやしい)含まれとります。
#include <stdio.h>
#include <stdint.h>
#define MAXMEM	(8)
float TargetMEM[MAXMEM];
extern void fsqrtv(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) {
    initTGT(1.000f);
    dumpTGT("Before FSQRTv");
    fsqrtv(TargetMEM);
    dumpTGT("After  FSQRTv");
    return 0;
}
実験結果
以下のようにしてビルドして実行しています。
$ gcc -g -O0 fsqrtv.c fsqrt.s $ ./a.out
標準出力に現れた結果が以下に。
平方根とれてるみたい。当たり前か。
