はじめに
- この記事はひとりNEONアドベントカレンダー2020 8日目の記事です
-
昨日は
div
命令を紹介した。 - 順序が逆転している気がするが、今日は
sub
命令を紹介する -
add
命令が多すぎるということは当然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_t
、float32x4_t
、float64x2_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_t
とint8x8_t
で戻り値はint16x8_t
、uint32x4_t
とuint16x4_t
で戻り値はuint32x4_t
など
- ex:
-
subw_high
命令では、第2引数に128bit幅レジスタを取るが、使われるのは上位64bit分だけ。 - このあたりの構成は
add
命令と全く同じである
その他
-
hsub
命令とsubhn
、rsubhn
命令は後日また解説する - 明日も手島が執筆の予定で、今の所
load
命令解説の予定です