
前回は浮動小数を浮動小数フォーマットのまま整数に丸めるFRINTx命令でした。今回は浮動小数を丸めて「ホンモノの」整数表現に変換してしまうFCVTxy命令です。前回は丸めモードフラグに影響される命令が2個ありましたが、今回は相当する命令はありません。命令減ったの?とみれば増えとります。変換先が2種類あるから。
※「ぐだぐだ低レベルプログラミング」投稿順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
FCVTxy命令
前回の浮動小数フォーマットのまま整数相当の値に丸めるFRINTx命令では丸めモードをニーモニックに含む命令が5種、丸めモードはFPCRレジスタの丸めモードフラグに従うもの2種の合計7種がありました。今回のFCVTxy命令では丸めモードフラグに従う命令は無いので丸めモードは全てニーモニックに含まれ5種です。これがFCVTxyのxに入ります。しかし、変換後の整数値が符号付き(s)か符号無(u)かでyに2種類入るので合計は10種です。いつものことだけれどもA64の命令多過ぎ(充実?)
| Mnemonic | Source | Destination | Rounding mode |
|---|---|---|---|
| FCVTAS | FP | 符号付整数 | nearest with ties to away |
| FCVTAU | FP | 符号無整数 | nearest with ties to away |
| FCVTMS | FP | 符号付整数 | toward minus infinity |
| FCVTMU | FP | 符号無整数 | toward minus infinity |
| FCVTNS | FP | 符号付整数 | nearest with ties to even |
| FCVTNU | FP | 符号無整数 | nearest with ties to even |
| FCVTPS | FP | 符号付整数 | toward positive infinity |
| FCVTPU | FP | 符号無整数 | toward positive infinity |
| FCVTZS | FP | 符号付整数 | toward zero |
| FCVTZU | FP | 符号無整数 | toward zero |
実験につかったアセンブリ言語記述の被テスト関数
例によって手抜きの関数プロローグ、エピローグ無の被テスト関数のソースが以下に。ターゲット機は例によって半精度浮動小数のサポートがなく、単精度か倍精度なので単精度だけ練習してます。手抜きだな、自分。
.globl fcvtas4V, fcvtau4V, fcvtms4V, fcvtmu4V, fcvtns4V, fcvtnu4V, fcvtps4V, fcvtpu4V, fcvtzs4V, fcvtzu4V
.text
.balign 4
fcvtas4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtas v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtau4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtau v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtms4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtms v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtmu4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtmu v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtns4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtns v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtnu4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtnu v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtps4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtps v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtpu4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtpu v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtzs4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtzs v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
fcvtzu4V:
ld1 {v0.4S, v1.4S}, [x0]
fcvtzu v0.4S, v1.4S
st1 {v0.4S}, [x0]
ret
C言語記述のmain関数
上記のアセンブリ言語関数を呼び出すmain関数が以下に。
#include <stdio.h>
#include <stdint.h>
#include <float.h>
#define MAXMEM (8)
typedef union {
float f;
uint32_t u;
int32_t s;
} t32;
t32 TargetMEM[MAXMEM];
extern void fcvtas4V(t32 *);
extern void fcvtau4V(t32 *);
extern void fcvtms4V(t32 *);
extern void fcvtmu4V(t32 *);
extern void fcvtns4V(t32 *);
extern void fcvtnu4V(t32 *);
extern void fcvtps4V(t32 *);
extern void fcvtpu4V(t32 *);
extern void fcvtzs4V(t32 *);
extern void fcvtzu4V(t32 *);
void initTGT() {
TargetMEM[0].f = 0.0f;
TargetMEM[1].f = 0.0f;
TargetMEM[2].f = 0.0f;
TargetMEM[3].f = 0.0f;
TargetMEM[4].f = 2.5f;
TargetMEM[5].f = 2.51f;
TargetMEM[6].f = -2.5f;
TargetMEM[7].f = -2.51f;
}
void initTGT2() {
TargetMEM[0].f = 0.0f;
TargetMEM[1].f = 0.0f;
TargetMEM[2].f = 0.0f;
TargetMEM[3].f = 0.0f;
TargetMEM[4].f = 1.5f;
TargetMEM[5].f = 1.51f;
TargetMEM[6].f = -1.5f;
TargetMEM[7].f = -1.51f;
}
void dumpTGTs(const char *arg) {
printf("%s\n", arg);
for (int i=0; i<4; i++) {
printf("%02d: %f -(%s)-> %d\n", i, TargetMEM[i+4].f, arg, TargetMEM[i].s);
}
}
void dumpTGTu(const char *arg) {
printf("%s\n", arg);
for (int i=0; i<4; i++) {
printf("%02d: %f -(%s)-> %d\n", i, TargetMEM[i+4].f, arg, TargetMEM[i].u);
}
}
int main(void) {
initTGT();
fcvtns4V(TargetMEM);
dumpTGTs("fcvtns");
initTGT();
fcvtnu4V(TargetMEM);
dumpTGTu("fcvtnu");
initTGT();
fcvtas4V(TargetMEM);
dumpTGTs("fcvtas");
initTGT();
fcvtau4V(TargetMEM);
dumpTGTu("fcvtau");
initTGT2();
fcvtzs4V(TargetMEM);
dumpTGTs("fcvtzs");
initTGT2();
fcvtzu4V(TargetMEM);
dumpTGTu("fcvtzu");
initTGT2();
fcvtms4V(TargetMEM);
dumpTGTs("fcvtms");
initTGT2();
fcvtmu4V(TargetMEM);
dumpTGTu("fcvtmu");
initTGT2();
fcvtps4V(TargetMEM);
dumpTGTs("fcvtps");
initTGT2();
fcvtpu4V(TargetMEM);
dumpTGTu("fcvtpu");
return 0;
}
実機実行結果の確認
以下のようにしてビルドして実行しています。
$ gcc -g -O0 simdFCVTxy.c simdFCVTxy.s $ ./a.out
-
- FCVTAy、FCVTNy
AwayのAとNearestのyを同じ入力値に対して適用してみました。こんな感じ。
-
- FCVTZy、FCVTMy、FCVTPy
丸めモードと符号付き、符号無での挙動の違い、全部分かった。ホントか?

