
前回に続きSIMDの比較命令の練習です。今回は浮動小数型。条件一致すればオール1、不一致でオール0が結果です。いつもの通りA64の命令多すぎ、と書いておきます。前回の整数型であったビット比較が無くなって1個減ったと思ったら、絶対値比較が2個も増えている。かえって練習するパターン増だと。流石だな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
SIMD浮動小数点数の比較命令
前回整数型のSIMD比較命令の一覧表であった符合付、符号無の種別がなくなった分、シンプルにはなってます。みんな符合つきだもんね。しかし前回同様、対ゼロ比較は優遇されてます。また整数型ではなかった絶対値とって比較というニーモニックが追加されてます。
| 比較 | 符号付 |
|---|---|
| = | FCMEQ |
| =0 | FCMEQ |
| ≧ | FCMGE |
| ≧0 | FCMGE |
| > | FCMGT |
| >0 | FCMGT |
| ≦0 | FCMLE |
| <0 | FCMLT |
| |S1|≧|S2| | FACGE |
| |S1|>|S2| | FACGT |
例によって、浮動小数点数の要素は半精度、単精度、倍精度の引数をとることができるのですが、Arm v8.0には半精度のサポートはありません(v8.2以降。)メンドイので、いつもの通り単精度(float)しか練習しないけれども。
実験に使ったアセンブリ言語記述の被テスト関数
例によって手抜きの関数プロローグ、エピローグ無の被テスト関数のソースが以下です。一通り「なでる」だけなのだけれども多いっす。充実というべきか。
.globl fcmeq4V, fcmeqZ4V, fcmge4V, fcmgeZ4V, fcmgt4V, fcmgtZ4V, fcmleZ4V, fcmltZ4V, facge4V, facgt4V
.text
.balign 4
fcmeq4V:
ld1 {v1.4S, v2.4S}, [x0], #32
fcmeq v0.4S, v1.4S, v2.4S
st1 {v0.4S}, [x0]
ret
fcmeqZ4V:
ld1 {v1.4S, v2.4S}, [x0], #32
fcmeq v0.4S, v1.4S, #0.0
st1 {v0.4S}, [x0]
ret
fcmge4V:
ld1 {v1.4S, v2.4S}, [x0], #32
fcmge v0.4S, v1.4S, v2.4S
st1 {v0.4S}, [x0]
ret
fcmgeZ4V:
ld1 {v1.4S, v2.4S}, [x0], #32
fcmge v0.4S, v1.4S, #0.0
st1 {v0.4S}, [x0]
ret
fcmgt4V:
ld1 {v1.4S, v2.4S}, [x0], #32
fcmgt v0.4S, v1.4S, v2.4S
st1 {v0.4S}, [x0]
ret
fcmgtZ4V:
ld1 {v1.4S, v2.4S}, [x0], #32
fcmgt v0.4S, v1.4S, #0.0
st1 {v0.4S}, [x0]
ret
fcmleZ4V:
ld1 {v1.4S, v2.4S}, [x0], #32
fcmle v0.4S, v1.4S, #0.0
st1 {v0.4S}, [x0]
ret
fcmltZ4V:
ld1 {v1.4S, v2.4S}, [x0], #32
fcmlt v0.4S, v1.4S, #0.0
st1 {v0.4S}, [x0]
ret
facge4V:
ld1 {v1.4S, v2.4S}, [x0], #32
facge v0.4S, v1.4S, v2.4S
st1 {v0.4S}, [x0]
ret
facgt4V:
ld1 {v1.4S, v2.4S}, [x0], #32
facgt v0.4S, v1.4S, v2.4S
st1 {v0.4S}, [x0]
ret
C言語記述のmain関数
上記のアセンブリ言語関数を呼び出すmain関数が以下に。今回の浮動小数点数の比較命令が、何気にメンドイのは、Cからはfloat型の値を配列に与えているのに、SIMD演算の結果はオール0かオール1のビット列なので符合無整数で戻すのが自然ということです。このため、これまたいつもの通りでunion定義してますが、typedefメンドイです。
#include <stdio.h>
#include <stdint.h>
#define MAXMEM (12)
typedef union {
float s;
uint32_t u;
} un32;
un32 TargetMEM[MAXMEM];
extern void fcmeq4V(un32 *);
extern void fcmeqZ4V(un32 *);
extern void fcmge4V(un32 *);
extern void fcmgeZ4V(un32 *);
extern void fcmgt4V(un32 *);
extern void fcmgtZ4V(un32 *);
extern void fcmleZ4V(un32 *);
extern void fcmltZ4V(un32 *);
extern void facge4V(un32 *);
extern void facgt4V(un32 *);
void initTGT() {
TargetMEM[0].s = 0.0;
TargetMEM[1].s = 1.25;
TargetMEM[2].s = -1.25;
TargetMEM[3].s = -1.5;
TargetMEM[4].s = 1.25;
TargetMEM[5].s = 1.25;
TargetMEM[6].s = -1.25;
TargetMEM[7].s = -1.25;
}
void dumpTGT(const char *arg) {
printf("%s\n", arg);
for (int i=0; i < 4; i++) {
printf("%02d: %3.3f opr %3.3f -> 0x%08x\n", i, TargetMEM[i].s, TargetMEM[i+4].s, TargetMEM[i+8].u);
}
}
void dumpTGTZ(const char *arg) {
printf("%s\n", arg);
for (int i=0; i < 4; i++) {
printf("%02d: %3.3f opr 0.000 -> 0x%08x\n", i, TargetMEM[i].s, TargetMEM[i+8].u);
}
}
int main(void) {
initTGT();
fcmeq4V(TargetMEM);
dumpTGT("fcmeq");
fcmeqZ4V(TargetMEM);
dumpTGTZ("fcmeq zero");
fcmge4V(TargetMEM);
dumpTGT("fcmge");
fcmgeZ4V(TargetMEM);
dumpTGTZ("fcmge zero");
fcmgt4V(TargetMEM);
dumpTGT("fcmgt");
fcmgtZ4V(TargetMEM);
dumpTGTZ("fcmgt zero");
fcmleZ4V(TargetMEM);
dumpTGTZ("fcmle zero");
fcmltZ4V(TargetMEM);
dumpTGTZ("fcmlt zero");
facge4V(TargetMEM);
dumpTGT("facge");
facgt4V(TargetMEM);
dumpTGT("facgt");
return 0;
}
実機実行結果の確認
以下のようにしてビルドして実行しています。
$ gcc -g -O0 simdfcmp.c simdfcmp.s $ ./a.out
-
- イコール比較、レジスタ間と対ゼロ
-
- 大なりおよび大なりイコール比較、レジスタ間と対ゼロ
-
- 小なりおよび小なりイコール比較は対ゼロしかありませぬ
-
- 最後は絶対値とってからの大なりおよび大なりイコール比較




