ぐだぐだ低レベルプログラミング(102)ARM64(AArach64)FADDと半精度

Joseph Halfmoon

前回、浮動小数点レジスタのお名前一覧など図にしておったので半精度浮動小数が使える、と頭から信じておりました。しかし今回気づきました。これがArmv8でもv8.2からの拡張であることを。つまり手元のラズパイ4のCortex-A72(Armv8p0)では使えないようです。トホホ。ということで倍精度、単精度のみの練習とな。

※「ぐだぐだ低レベルプログラミング」投稿順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

半精度浮動小数点型

今回からそろそろと浮動小数点演算命令のエクササイズに入るに際し、まずは浮動小数点の足し算命令、FADD命令をやってみることにいたしました。ありがち。

浮動小数点命令特有なのが、同じFADD命令でも異なる精度に対応しておることです。倍精度、単精度、そして半精度と。ただし、C言語レベルでは半精度の浮動小数点数はアーキテクチャ固有の拡張の扱いだと思います。手元のビルド環境では、サポートされているのだろうね?以下のヘッダファイルが存在することを確かめてみました。

arm_fp16.h

ちゃんとありましたぞ。上記のヘッダの中で、以下の半精度浮動小数点型がtypedefされとりました(実体は__fp16型)

float16_t

上記のヘッダを取り込めば、とりあえず変数の定義は可能っと。

しかし、喜びいさんで半精度浮動小数点型使ったコードを書いてみたら冒頭のアイキャッチ画像のようにつれないお言葉。もしやオプションが不足している?ということで

-mfp16-format=ieee

とかも試みましたがダメでした。そこでArmv8のマニュアルを参照してようやく気付きました。

ARMv8.2-FP16, Half-precision floating-point data processing

半精度浮動小数点型はArmv8でもv8p2からの拡張だったのね。残念ながらラズパイ4 model B上では使えないみたいっす。トホホ。

浮動小数点レジスタに関するABIの規定

つづいて、アセンブリ言語で記述した関数をC言語ソースから呼び出すためのABIの確認です。今まで整数レジスタ規定のみ参照してきましたが。今回からは浮動小数点/SIMDレジスタの規定を見ないとなりません。でも簡単です。ホントか?

    • V0、引数の最初兼戻り値
    • V1-V7、引数
    • V8‐V15、呼ばれる側(caller)が使いたいときは保存する必要がある。ただし本当に保存しなければならないのは下の64ビット分だけ。
    • V16-V31、呼ぶ側(caller)が使っていた場合は、呼び出す場合に保存。

Arm御本家の規定が以下に

Parameters in NEON and floating-point registers

しかし、上記よりも、以下のマイクロソフト社のページの方がまとまっている感じがします(日本語翻訳済だし。)

ARM64 ABI 規則の概要

今回実験のアセンブリ言語記述の関数

例によって、関数プロローグ、エピローグ無の手抜き、ほぼほぼ1命令1関数スタイルの実験コードが以下に。

.globl	faddd, fadds
.text
.balign	4

faddd:
    fadd d0, d1, d2
    ret

fadds:
    fadd s0, s1, s2
    ret

簡単?

C言語記述のメイン関数

上記のアセンブラ関数を呼び出すmainが以下に。

#include <stdio.h>
#include <stdint.h>
#include <arm_fp16.h>

extern double faddd(double, double, double);
extern float fadds(float, float, float);

int main(void)
{
    double resultD;
    float resultS;
    float16_t resultH;

    resultD = faddd(0.0, 1.23456789e-100, 2.0e-100);
    printf ("faddd(0.0, 1.23456789e-100, 2.0e-100): %.16e\n", resultD);
    resultS = fadds(0.0f, 1.234567f, 2.0f);
    printf ("fadds(0.0f, 1.234567f, 2.0f): %f\n", resultS);

    return 0;
}

arm_fp16.hが残骸のように残ってますが、今回使っておりませぬ。

ビルドして実行

ビルドして実行したところが以下に。faddResults

念のため、該当関数の逆アセンブルリストが以下に。faddDump

まあ、1命令動けば、後は以下同文?ホントか。

ぐだぐだ低レベルプログラミング(101)ARM64(AArach64)SIMD&FPレジスタ へ戻る

ぐだぐだ低レベルプログラミング(103)ARM64(AArach64)FSUB、FMUL他 へ進む