背景
- レイトレや機械学習のコードで, どうも NaN とかが出ているようで, 計算結果(e.g. ピクセル値)がおかしい
- 原因を突き止めるため, aarch64 C/C++ プログラムで, NaN とかが発生したら SIGFPE とか seg fault してほしい
結論
少なくとも, x86 のように例外が起きたらプログラムが自動で seg fault するとかシグナル出すのは, aarch64 では無理(大部分の(?) HW が未実装)なことがわかりました.
もしかしたら iOS(arm64) ではいけるかもしれません.
怪しいコードのところに明示的に FPU 例外ステータスチェックを入れての対応になります.
clang undefined behavior sanitizer で, ソフトウェア的に divide-by-zero と overflow は検出できます.
環境
- aarch64 linux(Ubuntu 18.04 + Jetson) を想定する
- glibc 環境
- gcc 7.5.0(apt で入る)
- clang 10.0(llvm.org などから手に入る prebuilt or apt パッケージ)
fenv.h は
/usr/include/fenv.h
/usr/include/aarch64-linux-gnu/bits/fenv.h
にあります.
sNaN
aarch64 + gcc/clang では, NaN(sNaN) のシグナル検出はデフォルトでは有効にならない.
gcc/clang では -fsignaling-nans
で有効にする.
ただし, これらはコンパイラでの扱いを変えるだけで, FPU の動作自体には影響はない(コメントありがとうございます!)
(-fsignaling-nans
を指定しても aarch64 では NaN で浮動小数点数例外トラップは発生しない)
以下, 参考まで.
__SUPPORT_SNAN__
が定義される.
/* NaN support. */
# if (__GLIBC_USE (IEC_60559_BFP_EXT) \
&& defined FE_INVALID \
&& defined __SUPPORT_SNAN__)
# define FE_SNANS_ALWAYS_SIGNAL 1
# endif
FE_SNANS_ALWAYS_SIGNAL
はそのままでは定義されない.
_GNU_SOURCE
を定義することで有効になる.
# define _GNU_SOURCE
# include <fenv.h>
ソースコードで定義する場合は, 最初に _GNU_SOURCE
を定義する必要がある.
// NG
# include <signal.h>
# define _GNU_SOURCE
# include <fenv.h>
などとするとうまくいかない.
feenableexcept
例外トラップ指定 feenableexcept は aarch64 では利用できない. -1
を返してしまう.
arm compiler でも aarch64 は例外トラップはサポートしないとあるので, aarch64 環境では浮動小数点例外トラップが使えないと考えてよいでしょう.
Clang undefined behavior sanitizer
ゼロ除算は -fsanitize=float-divide-by-zero
で検出できる.
ただし, sqrtf(-1.0f)
のように NaN が生成されるのは検出できない.
builtin
__builtin_aarch64_set_fpcr で FP control register 指定.
例外ビットは定義されているが, 設定しても反映されない(コメントありがとうございます).
手動検出
C/C++ コードで, 怪しそうなところを feclearexcept と ftestexcept で囲い, 例外ビットが立っているか検出し, 自前で SIGFPE なり発行するしかない.
gdb で NaN 検出
watch でいけるが, ただし変数の場所がわかっている必要がある.