LoginSignup
5
3

Visual Studio + ARM64 でNEONをビルドしようとしたら思ったより穴が深かった件

Last updated at Posted at 2023-12-22

はじめに

  • すごい小ネタですが、完走のために投稿します。
  • 前回の修正分をOpenCVにPRとして投げてみた。
  • その上で、AVX命令を呼んでるところを修正しようと思ったら思った以上に泥沼な作業が発生した話

先駆者がいた話

  • 前回、Windows + ARM64 + Visual Studioでビルドする際に参考にしたスクリプトは mshabuninさんのものだったが、どうやらその前にPRshibayanさん(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_tfloat32x4_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 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.harm_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. : 1つ目がF32からF16に変換する命令、2つ目がF164つ分のベクトルをメモリに書き出す命令です

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3