ぐだぐだ低レベルプログラミング(155)ARM64(AArach64)SIMD FCVTxy

Joseph Halfmoon

前回は浮動小数を浮動小数フォーマットのまま整数に丸める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を同じ入力値に対して適用してみました。こんな感じ。FCVTan

 

    • FCVTZy、FCVTMy、FCVTPy

ゼロ方向のZ、マイナス方向のM、プラス方向のPです。FCVTzpm

丸めモードと符号付き、符号無での挙動の違い、全部分かった。ホントか?

ぐだぐだ低レベルプログラミング(155)ARM64(AArach64)SIMD FRINTx へ戻る

ぐだぐだ低レベルプログラミング(156)ARM64(AArach64)SIMD MUL へ進む