sub命令


はじめに

$ grep ^v.*sub /usr/lib/gcc/aarch64-linux-gnu/7.5.0/include/arm_neon.h | cut -f 1 -d ' ' | sed -e 's/[usf][0-9]\+//g' | sort | uniq -c
      6 vhsub_
      6 vhsubq_
      8 vqsub_
      2 vqsubb_
      2 vqsubd_
      2 vqsubh_
      8 vqsubq_
      2 vqsubs_
      6 vrsubhn_
      6 vrsubhn_high_
     11 vsub_
      2 vsubd_
      6 vsubhn_
      6 vsubhn_high_
      6 vsubl_
      6 vsubl_high_
     11 vsubq_
      6 vsubw_
      6 vsubw_high_

sub命令

  • 従来と同じく、64bit幅の引数を2つ取るsub命令と128bit幅の引数を2つ取るsubq命令がある
  • sub命令はadd命令と同じく、計算結果がオーバーフローする場合はオーバーフローした分だけが保持される
  • 浮動小数点型(float16x8_tfloat32x4_tfloat64x2_t)で使える引き算命令はこいつしかない。
  • 演算内容は第1引数-第2引数
sub.cpp
        int16_t src0[] = {-3633, 30162, 14067, 10566, -3604, 15767, -15238, 12605,};
        int16_t src1[] = {30549, -14165, 26950, 12751, 12780, 32151, 1146, 28989,};
        int16_t dst[8] = {0};
        int16x8_t vsrc0 = vld1q_s16(src0);
        int16x8_t vsrc1 = vld1q_s16(src1);
        int16x8_t vdst  = vsubq_s16(vsrc0, vsrc1);
}
0:31354   // -3633 - 30549    = -34182 だけど演算結果が 31354 (=-34182+65536)
1:-21209  // 30162 - (-14165) =  44327 だけど演算結果が-21209 (= 44327-65536)
2:-12883
3:-2185
4:-16384
5:-16384
6:-16384
7:-16384

qsub命令

  • 末尾にqが付くのが64bit/128bitの違いだが、頭にqがつく場合は、飽和演算を意味する
  • 符号なし整数型の場合は最小値0、最大値255、符号あり整数型の場合は最小値-128、最大値127でクランプするので、演算結果がこの範囲を超えても、結果はこの範囲にクランプされる
    • いずれも8bit整数型の場合。レーンのbit幅に応じて最大値/最小値は変わる
sub.cpp
        int16_t src0[] = {-3633, 30162, 14067, 10566, -3604, 15767, -15238, 12605,};
        int16_t src1[] = {30549, -14165, 26950, 12751, 12780, 32151, 1146, 28989,};
        int16_t dst[8] = {0};
        int16x8_t vsrc0 = vld1q_s16(src0);
        int16x8_t vsrc1 = vld1q_s16(src1);
        int16x8_t vdst  = vqsubq_s16(vsrc0, vsrc1);
}
0:-32768   // int16_tの最小の値=-32768
1:32767    // int16_tの最大の値= 32767
2:-12883
3:-2185
4:-16384
5:-16384
6:-16384
7:-16384

subl命令

  • 演算結果をbit拡張しながら結果レジスタに書き込む
  • 引数がint8x8_t型ならば、結果はint16x8_t型。引数がint16x4_t型ならば、結果はint32x4_t型。
    • bit拡張されるので、引数として64bit幅のレジスタしか取れないことに注意
    • subl_high命令では、128bit幅レジスタを引数に取るが、使われるのは上位64bit分だけ。

subw命令

  • 演算結果がオーバーフローしないようにbit拡張されたレジスタから引く
  • 第1引数が128bit幅、第2引数が64bit幅、要素数は同じだけど各要素のbit幅が違う型のみ引数に取る
    • ex: int16x8_tint8x8_tで戻り値はint16x8_tuint32x4_tuint16x4_tで戻り値はuint32x4_tなど
  • subw_high命令では、第2引数に128bit幅レジスタを取るが、使われるのは上位64bit分だけ。
  • このあたりの構成はadd命令と全く同じである

その他

  • hsub命令とsubhnrsubhn命令は後日また解説する
  • 明日も手島が執筆の予定で、今の所load命令解説の予定です