ぐだぐだ低レベルプログラミング(106)ARM64(AArach64)FPSR/FPCR

Joseph Halfmoon

何時までも見て見ぬふりもできないので今回はFPSRとFPCRを調べてみたいと思います。命令ニーモニックではありませぬ。フローティングポイントステータスレジスタとコントロールレジスタ、「メンドイ」浮動小数の例外やら非数(NaN)などの制御とそのステータスを保持するもの。今回はレジスタのビットの配置から。

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

FPCR/FPSR

浮動小数点演算命令(SIMD演算含む)については専用の制御レジスアFPCRと、ステータスレジスタFPCRが存在します。FPCRで例外発生時の処理の方法や、計算方法のオプションなどを指定し、FPSRで浮動小数点演算の結果のステータスを保持するという形です。2つのレジスタのビット配置が以下に。A64_FPCR_FPSR

ARM64のレジスタであるので、2つとも64ビット幅ということになっています。しかし実際にビットが存在するのは下の32ビット内だけです。上記図でRES0と書かれている黒い部分は予約ビット(0)です。

上記の2つのレジスタを眺めると、FPCRで使用しているビット部分はFPSRではRES0となっており、FPSRの使用ビットはFPCRでもRES0です。2つの値をORしても「今のままのビット配置なら」全ビット保持できるっと。

なお上記図の色分けですが、LSB側(右側)の濃い緑のそれぞれ6ビットは例外の制御とステータスです。またFPCRの紫部分は、AArach32(32ビットのARM)との互換性をとるための部分みたいです。AArach64(今回実習しているA64命令)では使ってないようです。また、FPCRの灰色のビットは、ARMv8.2以降の半精度浮動小数のサポートで使用するビットで、残念ながらターゲット機のArm Cortex A72では実装されてません(RES0扱い。)

残りの黄緑部分がFPCR、FPSRに独特なビットです。

今回実験のアセンブリ言語コード

今回は、このメンドイ感じがプンプンとするFPCR、FPSRを読み出してみるだけです。ただプログラム開始時の初期値を読んでも本当に読めているのかわからないので、1ビットだけFPSRのビットを立ててみたいと思います。対象は、

FPSRのビット1のDZC、泣く子も黙る「ゼロ割り算フラグ」

であります。ただ、対になるFPCRビット9のDZE、ゼロ割り算例外トラップのイネーブルフラグが立っているとどこかに連れていかれてしまいます。今回はDZEが0ということを確かめてから浮動小数点数のゼロ割り算に「取り組んで」みたいと思います。

例によって関数プロローグもエピローグもない、手抜きな被テストアセンブラ関数群が以下に。

.globl    readfpcr, readfpsr, trydiv0
.text
.balign    4

readfpcr:
    mrs x0, fpcr
    ret

readfpsr:
    mrs x0, fpsr
    ret

trydiv0:
    fmov d1, #1.75
    fsub d2, d1, d1
    fdiv d0, d1, d2
    mrs x0, fpsr
    ret
C言語記述のmain関数

上記のアセンブラ関数を呼び出してテストするためのものです。まずはプログラム起動時のFPCRとPFSRの初期値を読み出して表示します。その後、ゼロ割り算を意図的に起こす trydiv0 関数を呼び出し、その後PFSRがどのようになっているか再び読み出してます。

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

extern uint64_t readfpcr(uint64_t);
extern uint64_t readfpsr(uint64_t);
extern uint64_t trydiv0(uint64_t);


int main(void)
{
    uint64_t result;

    result = readfpcr(0);
    printf ("FPCR(init):    %016lx \n", result);
    result = readfpsr(0);
    printf ("FPSR(init):    %016lx \n", result);
    result = trydiv0(0);
    printf ("FPSR(trydiv0): %016lx \n", result);
 
    return 0;
}
ビルドして実行

ビルドして実行したところが以下に。FPCRもFPSRも初期値はオール0みたいです。オール0ということはFPCRのDZEも0なので、浮動小数のゼロ割り算をやっても trap されることはなく、FPSRのDZCビットに反映されるだけの筈。

ゼロ割り算後のFPSRを見てみるとビット1が立ってました。DZCビットですな。fscrResult

まあ、最初のビットは予想通り。割り算は分かりやすいからな。。。

ぐだぐだ低レベルプログラミング(105)ARM64(AArach64)FMOV へ戻る

ぐだぐだ低レベルプログラミング(107)ARM64(AArach64)FCVT へ進む