はじめに
- この記事はひとりNEONアドベントカレンダー2020 16日目の記事です
-
昨日は
dup
命令という、単一の値を全レーンに設定する命令を紹介した - 今日の記事では、当初は
popcount
を紹介するつもりだったのですが、reinterpret
命令を紹介しないと話が複雑そうだったので、今日はreinterpret
命令を。
- いつも挿入している命令をグルーピングした図ですが、今回は特段芸術的な仕上がりになりました。12
- 例えば、
vreinterpretq_f32_s32
はint32x4_t
を引数として、float32x4_t
型を返す - 前述の図をよーく見ると、芸術的な網目の中に、水平に引かれている線は一本も無いことが分かる
- 同じ型を同じ位置に並べており、同じ型への変換命令は存在しないため、水平線が存在しない
reinterpret
命令
- 大前提として、この命令はアセンブラ上には現れない命令である。
- 事実公式の命令リファレンスページを見ても、
A64 Instruction
の項目は空欄である
- 事実公式の命令リファレンスページを見ても、
- CPUはレジスタにデータを読み書きする訳ですが、C/C++は「float」「int」みたいに気をつけてはいるもの、レジスタ上には「int用のレジスタ」「short用のレジスタ」と区別はしていません
- そのため、CPUがデータを読み出す際に「この命令はfloatとして扱う」というふうに、レジスタのデータをちゃんと解釈(interpret)する必要があります3
- C/C++のcastが近いですが、castは値の変換を含みます。
float f = 1.0f;
int a = (int)f;
- 前述のコードで、変数
f
には、16進数表記で0x3F800000
が含まれているが、変数a
には0x1
が代入される。- これは、浮動小数点数の$1.0$を整数型の1に変換している
- 一方で、本日紹介している
reinterpret
命令は以下の処理を行っている4
float f = 1.0f;
int a = *(int*)&f;
- ベクトル型は、前述のような変換を伴うcastは認めて無く、明示的に
reinterpret
命令で再解釈してやる必要がある - また、ベクトルの型の暗黙の型変換も認めておらず、明示的にエラーが上がります。5
note: use -flax-vector-conversions to permit conversions between vectors with differing element types or numbers of subparts
- 以上はGCC 7.5.0でのwarningメッセージ
- warningメッセージだが、エラーも出る
- オプションに
-flax-vector-conversions
があれば、コンパイラが暗黙の型変換を認めてくれるが、手間を考えると、逐一reinterpret
命令を挟んだほうがいいと思う。 - 実際にアセンブラには現れず、その代わり、レジスタを読む次の命令が、レジスタを整数なり浮動小数点数なり、再解釈した値で読んでくれる。
「変換」ではない
-
reinterpret
命令のポイントは「変換」ではないということです。 - 何でこんなのが必要になるかと言うと、明日の
popcount
命令への布石です。
おわりに
- 今日は
reinterpret
命令を紹介しました - 明日も手島の予定で
popcount
命令を紹介します。
-
実はこの図は厳密には正しくありません。図を愚直にたどると、
vreinterpret_xxx_p128
という命令が存在しうる形になっていますが、q
が付かないreinterpret
命令は64bit幅のレジスタを扱います。しかし、p128
が末尾に付くということは引数としてpoly128_t
型を取るということなので、レジスタbit幅の辻褄が合いません。しかし、そこを省いて描くと、ただのカオスな樹形図になってしまうので、そこは敢えて目をつぶって今回の図を描きました。 ↩ -
っていうか、
poly128_t
型なんてあるんですね。筆者もこの記事を書いてて初めて知りました。まあ、SI M D型ではないので、省いてもよかったかも? ↩ -
予防線を張っておくと、FPU専用レジスタが用意されてる場合は「
float
を保存する専用のレジスタ」が存在するわけですけれど、本記事の主題とは外れるのでそこは割愛します ↩ -
最近、C++ではこれがお行儀の宜しくない書き方だと知ったが、それはそれ ↩
-
GCCで確認。でも多分MSVCやClangでも同じだと思う。 ↩