前回、前々回と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
標準出力に現れた結果が以下に。
平方根とれてるみたい。当たり前か。