LoginSignup
3
0

More than 3 years have passed since last update.

はじめに

vmul.png

$ egrep ^v.*mul /usr/lib/gcc/aarch64-linux-gnu/7.5.0/include/arm_neon.h  | cut -f 1 -d _  | sort | uniq -c
     31 vmul
      2 vmuld
      2 vmulh
     24 vmull
     31 vmulq
      2 vmuls
     10 vmulx
      3 vmulxd
      2 vmulxh
     10 vmulxq
      3 vmulxs
      8 vqdmulh
      3 vqdmulhh
      8 vqdmulhq
      3 vqdmulhs
     16 vqdmull
      3 vqdmullh
      3 vqdmulls
      8 vqrdmulh
      3 vqrdmulhh
      8 vqrdmulhq
      3 vqrdmulhs

  • 本日はvmul命令、vmulx命令、vmull命令を紹介する

vmul命令

  • 基本的な乗算命令
  • 型も、s8からf64まで、9種類ちゃんと揃えてある
    • 繰り返しになるが、vmul_f16vmulq_f16、及びそれらの派生系命令はArm v8.2 拡張命令である
  • 派生型、と記載したけれど、vmul_lane命令、vmul_n命令が該当する
  • vmul_lane命令は、引数を3つとる。1つ目が64bit/128bit幅レジスタ、2つ目が64bit/128bit幅レジスタ、3つ目が指定する要素の位置である
    • ex1: vmul_lane_f32 (float32x2_t __a, float32x2_t __b, const int __lane)
    • ex2: vmul_laneq_f32 (float32x2_t __a, float32x4_t __b, const int __lane)
    • ex3: vmulq_lane_f32 (float32x4_t __a, float32x2_t __b, const int __lane)
    • ex4: vmulq_laneq_f32 (float32x4_t __a, float32x4_t __b, const int __lane)
    • 非常に似た関数シグネチャだが、qが付く場所と、引数レジスタの幅に注目
    • 第3引数のlaneはコンパイル時定数の必要があり、0オリジンで第2引数の要素を指定する
    • 当然第2引数の範囲外を指定するコンパイルエラーになる
    • 第1引数の各要素に、第2引数と第3引数で指定した数値を掛ける。
  • vmul_n命令はvmul_lane命令みたいに面倒なことをしないで、直接乗算する数値を引数にわたす
    • ex: vmulq_n_s16 (int16x8_t __a, int16_t __b)
    • 画像処理屋からすると、フィルタの係数を全画素に乗算する場合とかに使える
mul.cpp
    float src0[] = { 1.f/0.f,        0.f,   -1.f/0.f, 84800.38f,}; // 0番目の要素は正の無限大、2番目の要素は負の無限大
    float src1[] = { 0.000f,  -3.141592f, 24717.005f, 87145.40f,};
    float32x4_t  vsrc0 = vld1q_f32(src0);
    float32x4_t  vsrc1 = vld1q_f32(src1);
    float32x4_t vdst  = vmulq_f32(vsrc0, vsrc1);
  • 演算結果
0:nan
1:-0
2:-inf
3:7.38996e+09

vmulx命令

  • 調べたのだが、公式のドキュメント もイマイチ的を得ない
  • 個人的に調べた結果を述べると、$0 \times \infty $ が発生した場合、演算結果が 2になるという謎命令
mulx.cpp
    float src0[] = { 1.f/0.f,        0.f,   -1.f/0.f, 84800.38f,}; // 0番目の要素は正の無限大、2番目の要素は負の無限大
    float src1[] = { 0.000f,  -3.141592f, 24717.005f, 87145.40f,};
    float32x4_t  vsrc0 = vld1q_f32(src0);
    float32x4_t  vsrc1 = vld1q_f32(src1);
    float32x4_t vdst  = vmulxq_f32(vsrc0, vsrc1);
  • 前節のサンプルコードと全く同じデータに対して、vmulx命令を使用してみた
  • 演算結果
0:2
1:-0
2:-inf
3:7.38996e+09
  • 最初の演算結果が2になっている
  • それ以外の結果はvmul(通常の乗算)と同じ

vmull命令

  • おなじみ、末尾にl(エル)が付いた派生系
  • 引数は64bit幅レジスタが2つ。戻り値は128bitレジスタとなる
    • ex: int32x4_t vmull_s16 (int16x4_t a, int16x4_t b)
    • オーバーフローに配慮した優しい実装となっております
  • vmull命令には、vmull_high命令、vmull_n命令の派生系もあり、なんとその組み合わせのvmull_high_n命令もある
    • 仕組みはそれぞれ単純で、vmull_high命令では第2引数が128bit幅のレジスタになるが、使われるのは上位64bitだけ
    • vmull_n命令では、第2引数がベクトルでなく、単体の数値になる。同じ値を第1引数の各要素に掛け合わせる

おわりに

  • mul命令も多すぎである
  • 明日も私の予定で、執筆時点では比較命令系を紹介する予定である
3
0
1

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
3
0