ぐだぐだ低レベルプログラミング(113)ARM64(AArach64)積和演算4種の違い

Joseph Halfmoon

前回、浮動小数の積和演算はfused演算だということで、fused演算とそうでない計算の微妙な差を味わいました。今回は残りの積和演算命令を一気にやってみます。というか4つ並べて実行してその差をみるとようやく4個ある意味が良く分かるから。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

浮動小数点(スカラー)の4種積和演算命令

FMADD、FMSUB、FNMADD、FNMSUBと浮動小数の積和演算命令のニーモニックを上げていくと何だかサッパリなのですが、以下の表にまとめたらスッキリ。ホントか?

命令 積算(和)レジスタ 乗算ソース1 乗算ソース2
FMADD
FMSUB NEG
FNMADD NEG NEG
FNMSUB NEG

積和演算命令には3つのソースレジスタ指定がありますが、上記のようにそのうち2つをネゲート(符合反転)するか否かの組み合わせなんであります。つまるところは。前回 fused演算で使ったFMADDはまったくネゲートしない「プレイン風味」だと。

勿論、4つすべて fused演算です。

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

いつものように手抜きな、関数プロローグもエピローグもない1命令1関数スタイルです。倍精度も単精度もおなじようなもんだと踏みつぶして、単精度だけで実験しています。ほんと手抜き。また丸めについてはfpcr指定の「成り行き」で適用されます。デフォルトでは最近接awayの筈。

.globl	fmaddS, fmsubS, fnmaddS, fnmsubS
.text
.balign	4

fmaddS:
    fmadd s0, s1, s2, s3
    ret

fmsubS:
    fmsub s0, s1, s2, s3
    ret

fnmaddS:
    fnmadd s0, s1, s2, s3
    ret

fnmsubS:
    fnmsub s0, s1, s2, s3
    ret
C言語記述のmain関数

これまた手抜きなC言語記述のテスト駆動部が以下に。前回のfused計算で使った数字の組み合わせをそのまま流用してます。そのせいで数字がなんか微妙。今回は4種命令に全て同じ引数を与えているので、その差が一目瞭然な筈。

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

extern float fmaddS(float, float, float, float);
extern float fmsubS(float, float, float, float);
extern float fnmaddS(float, float, float, float);
extern float fnmsubS(float, float, float, float);

int main(void)
{
    float fin;
    union {
        float s;
        uint32_t u;
    } u32a, u32b, u32c, u32d;


    fin = 0.0009992f;
    u32a.s = fmaddS  (0.0f, fin, 1.1f, 0.000003f);
    u32b.s = fmsubS  (0.0f, fin, 1.1f, 0.000003f);
    u32c.s = fnmaddS  (0.0f, fin, 1.1f, 0.000003f);
    u32d.s = fnmsubS  (0.0f, fin, 1.1f, 0.000003f);
    printf ("fmadd =%2.8f(%08x)\n", u32a.s, u32a.u);
    printf ("fmsub =%2.8f(%08x)\n", u32b.s, u32b.u);
    printf ("fnmadd=%2.8f(%08x)\n", u32c.s, u32c.u);
    printf ("fnmsub=%2.8f(%08x)\n", u32d.s, u32d.u);

    return 0;
}
実行結果

ビルドして実行したものが以下に。前回の流用コードなので%fでも無用に長めの10進浮動小数表示しているだけでなく、()内に16進表記も併記しております。

fmadd_results

上記結果の検算を行うために表計算ソフトにお願いしました。DST、一致しておりますぞ。testResults

念のため、表計算ソフトに入力した計算式が以下です。最初の表のとおりです。testFormulaC

積和算4種の差は分かった。ホントか?

ぐだぐだ低レベルプログラミング(112)ARM64(AArach64)積和は”fused” へ戻る

ぐだぐだ低レベルプログラミング(114)ARM64(AArach64)FABS他 へ進む