AltiVec と SSE2, Cell SPU, SPARC64 VIIIfx(Venus) で SIMD プログラミングから引退し, スカラーコードと楽しく穏便に暮らしていた矢先, embree-aarch64 に iOS NEON 改善 PR がありました.
Pchang0414 i os opt
https://github.com/lighttransport/embree-aarch64/pull/15
この PR では, iOS 向け clang ではコンパイルが通るが, gcc でクロスコンパイルすると SIMD 型キャストでエラーがでました. 調べてみると conversion-free cast(implicit casting) を制御する -flax-verctor-conversion
に原因があるのがわかりました.
ちなみに lax は略語ではなくて, ゆるふわな, とか, 厳格ではない, の意味です.
conversion-free cast
SIMD おじさんの回顧録です.
SIMD 命令では, 内部的なビット配列は変えずに, uint32x4 から float32x4 にキャスト(C でいう union でのキャストや, reinterpret_cast でのキャスト)したいときがよくあります(e.g. 値をマスクしてから, 浮動小数点にしたいとか).
本来はビット配列を保持した型キャスト(intrinsics 命令 or コンパイラの builtin 関数)を利用するのが推奨ですが,
clang ですと, implicit にキャスト(ビット配列を保持してキャスト)してくれるようです.
たとえば,
uint32x4 a = 0x3f80000 x 4;
float32x4 b = i;
のようなコードは, 1065353216.0f x 4
ではなく, b = 1.0f x 4
になります.
私は AltiVec プログラミング時に, このようなキャストは conversion-free cast や bit-preserving cast と習った記憶があります. 最近では lax-vector-conversion など, 呼び方が変わっているようですね.
ARM NEON
明示的な conversion-free casting をする intrinsics があります.
vreinterpret_***
iOS clang
iOS 向けのシステムの arm_neon.h ヘッダがすでに -fno-lax-vector-conversion
をつけるとコンパイルできない(lax-vector-conversion 前提で記述がされている)ため, そもそも -fno-lax-vector-conversion
を有効にすることができません.
理想は, gcc + Linux などでコンパイルして, 必要なところへ vreinterpret_***
を入れていく必要があります.
lax-vector-conversion コンパイルオプション
新しい(いつからの?) gcc: none がデフォルト
clang: all がデフォルト
gcc では, -flax-vector-conversion
は既存コードのために用意され, 新規のコードでは利用はするべきではないとあります.
clang では lax-vector-conversion により, いろいろバグの原因になったりとの報告があります.
今後の動き
暗のキャストはバグの原因になりやすいので, 最近では arm clang で -fno-lax-vector-conversion
をデフォルトにしようという動きがあります.
clang 自体も gcc と同じように none をデフォルトにしたり, lax のパターンを増やしたりなどしようとしていますが, revert されたりしています.
現状 XCode clang で -fno-lax-vector-conversion
でコンパイルエラーになるわけで, none への移行の道は遠いかもしれません...
まとめ
ARM NEON プログラミングをするときは, aarch64 gcc + linux で vreinterpret_***
で明示的にキャストしましょう.