LoginSignup
4
0

More than 3 years have passed since last update.

store命令 -scatterを添えて-

Last updated at Posted at 2020-12-09

はじめに

store.png

$ grep ^vst[1234] /usr/lib/gcc/aarch64-linux-gnu/7.5.0/include/arm_neon.h  | cut -f 1 -d ' ' | sed -e 's/_[spfu][0-9]\+//g' | sort | uniq -c
     14 vst1
     14 vst1_lane
     14 vst1q
     14 vst1q_lane
     14 vst2
      1 vst2_lane_
     14 vst2q
      1 vst2q_lane_
     14 vst3
      1 vst3_lane_
     14 vst3q
      1 vst3q_lane_
     14 vst4
      1 vst4_lane_
     14 vst4q
      1 vst4q_lane_

vst1 (vstn)

  • load命令と対をなす。
  • vst1命令だとただメモリに保存する通常のstore命令
  • store命令はloadと同じくvst2vst3vst4まで存在する
  • loadではgatherを実現していたが、store命令ではscatterを実現する
st3q.cpp
        uint8_t data[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,};
        uint8_t dst[48] = { 255 };
        uint8x16x3_t vsrc;
        vsrc.val[0] = vld1q_u8(data     );
        vsrc.val[1] = vld1q_u8(data + 16);
        vsrc.val[2] = vld1q_u8(data + 32);
        vst3q_u8(dst , vsrc);
  • 演算結果
0       10      100     1       11      101     2       12      102     3       13      103     4       14      104     5
15      105     6       16      106     7       17      107     8       18      108     9       19      109     10      20
110     11      21      111     12      22      112     13      23      113     14      24      114     15      25      115
  • 見事に、vld3q命令の逆が行われ、010100から始まる数列がそれぞれ順番に並べられている様子が分かる
  • vld3q命令でロードしたベクトルに対して演算を行い、最後にvst3q命令で書き込めば、RGB画像において、各色独立の演算を施した上で同じフォーマットでメモリ上に書き戻せる
  • vld3qも神とたたえたが、このvst3q命令も神の如き尊さである。

vst1_lanevst1q_lane (vstn_lane)

  • このlaneがついた命令は、実際どう振る舞うのか
  • 動かして試してみよう
vst2q.cpp
        float data[] = { 1.0,  2.0,  3.0,  4.0, 10.0, 20.0, 30.0, 40.0};
        float res [] = { -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f};
        float32x4x2_t a;
        a.val[0] = vld1q_f32(data    );
        a.val[1] = vld1q_f32(data + 4);
        vst2q_lane_f32(res, a, 2);
  • 使い方はvld2q_lane命令同じで、vst2q_lane命令にはfloat32x4x2_t型のように、ベクトルを束ねた型を使う
    • 第3引数は0オリジンでレーン番号を指定する。
    • 当然範囲外を叩くとコンパイルエラーだし、数値はコンパイル時定数の必要がある。
  • 演算結果
3       30      -1      -1      -1      -1      -1      -1
  • 意外というかなんというか。
  • lane番号で指定した部分だけが書き出される。
  • なので、確保した配列res[8]のうち、後ろの6要素は触れられないままである

アライメントについて

  • SSEと違い、アライン(先頭アドレスが8 byteの倍数になっているか)を心配しなくてもHW側でよしなにやってくれる。1
  • ただし、Githubのissueにコメントしたが、32bit OS上でunalignedなアドレスから、u64s64のロードをすると、実行時エラーSIGBUSが起きる。
  • 解せないのは、同アドレスからvld1q_f32など、別の命令で適当にロードしても実行時エラーは起きず、ロードにvreinterpretq_xxで型を変えることが可能である
  • つまり「同じアドレス」から全く問題なくロードができる。
  • 解せないけれど、HWの仕様っぽいので割り切るしか無い。
チップの命令セット OSの32bit/64bit ベクトルレジスタの型 8byte alignment 結果
Armv8 64bit int32x4_t Y Success
Armv8 64bit int32x4_t N Success
Armv8 64bit int64x2_t Y Success
Armv8 64bit int64x2_t N Success
Armv8 32bit int32x4_t Y Success
Armv8 32bit int32x4_t N Success
Armv8 32bit int64x2_t Y Success
Armv8 32bit int64x2_t N Error (SIGBUS)
Armv7 32bit int32x4_t Y Success
Armv7 32bit int32x4_t N Success
Armv7 32bit int64x2_t Y Success
Armv7 32bit int64x2_t N Error (SIGBUS)
  • アセンブラレベルで見てみると、Arm v8 では当然違いは無い
  • Arm v7 のアセンブラを見てみると、
  • int64x2_tの場合(含むuint64x2_tint64x1_tuint64x1_t)
vld1.64 {d16-d17}, [r3:64]
  • それ以外の型の場合
vld1.32 {d16-d17}, [r3]
  • {d16-d17}は2本の64bitレジスタを指定している。
  • 命令のオペランドの[r3]は一般レジスタのr3に入ってるアドレスからロードする、という意味
  • 違いは
    • vld1.64vld1.32という末尾についてる要素の型
    • [r3]というアドレス指定が[r3:64]となっている
    • 多分、この:64が末尾についてることで8byte境界を期待しているのだと推測する
  • 何にしろそれ以外のロードに関してはアライメントの心配は要らない。

おわりに

  • store命令のvstとその派生形を紹介しました
  • 明日も手島の執筆の予定で、積和命令を紹介予定

  1. SSEでは16byte境界だが、NEONでは、前述の通りごく一部の命令に限って8byte境界の必要性がある 

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