はじめに
先駆者がいた話
- 前回、
Windows + ARM64 + Visual Studio
でビルドする際に参考にしたスクリプトは mshabuninさんのものだったが、どうやらその前にPRで shibayanさん(Github上のアカウント)が言及していた。 - で、ここに不思議なマクロが現れる
# define _ARM64_DISTINCT_NEON_TYPES
-
なお、基準にしているOpenCVは4.8.0と4.8.1の間の開発版(git コミット
6ee71fee88d26df529eaad7209dbdcd3baf86577
)である -
このマクロはOpenCVの
cmake/checks/cpu_neon.cpp
で定義されている他、cmake/OpenCVCompilerOption.cmake
の中でVisual Studioにも渡されている -
このマクロを追加したのが前述のshiabayanさんのPRである。
-
ところで、このマクロは一体なんなのか?shibayanさんのPRでは
the blocking of NEON support in builds for Windows on ARM has been resolved.
- と書かれてある。ちょっと調べてみた
調べてみた
- 結論から述べると、Visual Studio 17.2より前だと、NEONのベクトルの型をただの128bitベクトルとみなす挙動になっていた模様である。
Microsoft 公式ブログ
- 前述のマクロを検索すると、まずMicrosoftのコミュニティフォーラムが検索に引っかかる
- そこでは前述のマクロに関して、Microsoftの公式ブログを引用している
- で公式ブログではVisual Studio 17.2 でARM64コンパイラについて以下のことが言及されている
- オプション
/Zc:arm64-aliased-neon-types-
が追加されたこと、 - このオプションにより「NEONの型を、GCCやClangと同じく区別された型とみなすようになる」と書かれている
- 従来の挙動がデフォルトで、その場合は
/Zc:arm64-aliased-neon-types
(末尾にマイナス無し)を指定したことと等価になる、とある。
- オプション
- では、公式の言う「NEONの型を区別された型とみなす」とはどういうことだろうか。
- PRやログメッセージを読み解くと、Visual Studio 17.2より前のARM64コンパイラは、NEON用ベクトル型は、幅でしか区別していなかったようである。つまり、
-
int32x4_t
やfloat32x4_t
は、ARMの定義する標準では「別の型」とみなされるが、Visual Studio ではまとめて"128bit幅のベクトル"としか扱っていなかった。 - 結果、OpenCVに実装されている、NEONのベクトルを受け取る関数が、全て重複したシグネチャとみなされる、という挙動となっていた。
-
- どういう経緯でこういう挙動になっているのかは不明だが、個人的には
SSE
では中身によらず__m128
/__m128i
という型だけでハンドリングしていた挙動を連想した。 - そして、どうも諸々のメッセージを読み解くと、
- Visual Studio に
/Zc:arm64-aliased-neon-types-
オプションを渡すと、 - Visual Studio の内部的に
_ARM64_DISTINCT_NEON_TYPES
マクロが定義される - これにより、各NEONの方が独立した型として扱われる
- という挙動になっているらしい
- Visual Studio に
Visual Studio 17.2 で区別するようにしたよ!(ただし全機能サポートするとは言ってない)
- ここまで読んだら、あとはFP16の部分だけ修正すれば良いや、と思い立ち、ポチポチとPRを作ってみたのですが、何故かこんなメッセージに見舞われます。
error C3861: 'vcvt_f16_f32': identifier not found
error C3861: 'vst1_f16': identifier not found
- なにやら
f16
関連の型、関数がことごとく見つかりません。 1
- 参考までに、以下のようなコードを試しにコンパイルしてみたときのエラーメッセージです。
const float src[] = { 0.0f, 0.0f, 0.0f, 0.0f };
__n16 dst[8]{};
float32x4_t v_src = *(float32x4_t*)src;
float16x4_t v_dst = vcvt_f16_f32(v_src);
vst1_f16(dst, v_dst);
- ドウシテコウナッタ。。。
個人的な結論
- どうやら現状Visual Studio では、
/Zc:arm64-aliased-neon-types-
を定義すると、FP16
関連命令、型が全て無効になる挙動のようです。 - 先程のフォーラムのコメントにもその旨書かれています。
There is, however, still a compiler issue here: When using /Zc:arm64-aliased-neon-types-, all of the half-precision intrinsics are missing: vcvt_f32_f16, vcvt_f16_f32, etc.
- また、実際にコンパイラ付属のヘッダファイルを覗いてみるとその旨確認できます。
- 以下、Visual Studio 付属の
arm64_neon.h
の内容を一部抜粋したものです - この
arm64_neon.h
はarm_neon.h
からincludeされ、ARM64用のNEONのヘッダファイルです。
#if !defined(_ARM64_DISTINCT_NEON_TYPES)
#define vget_lane_f16(Dm, lane) neon_dups16((Dm), (lane))
#define vcvt_f32_f16(src) __n128_to_float32x4_t(neon_fcvtl_32(__float16x4_t_to_n64(src)))
#define vcvt_f16_f32(src) __n64_to_float16x4_t(neon_fcvtn_32(__float32x4_t_to_n128(src)))
#endif /* !_ARM64_DISTINCT_NEON_TYPES */
- この
_ARM64_DISTINCT_NEON_TYPES
は/Zc:arm64-aliased-neon-types-
オプションを渡したときに定義されるマクロです。 - OpenCVをビルドするためにはこのオプションが必須であります。
- 結果マクロは定義されており、前述の
#if
から#endif
までまるっとプリプロセッサにより無効化されます - 念のため、
/Zc:arm64-aliased-neon-types-
オプションを外してビルドを試してみましたが、今度はやはり多重定義のエラーが多発し、ビルドが通りません。
後日談というか今回のオチ
- 前回の記事に書いた迂回策に則って、
FP16
命令だけ無効化する
$ cmake -G"Visual Studio 17 2022" \
-A ARM64 \
-DCMAKE_SYSTEM_NAME=Windows \
-DCMAKE_SYSTEM_PROCESSOR=ARM64 \
-DPYTHON2_EXECUTABLE= \
-DCPU_BASELINE=NEON \
..
-
これはVisual Studio + ARM64の組み合わせで発生している挙動が原因なので、コードでのFIXは現実的ではない
-
すごい小ネタですが、完走のために投稿します。
-
: 1つ目が
F32
からF16
に変換する命令、2つ目がF16
4つ分のベクトルをメモリに書き出す命令です ↩