はじめに
- この記事はひとりNEONアドベントカレンダー2020 19日目の記事です
- 昨日は固定小数点演算用命令を紹介した
- 今日は
mov
命令を紹介する
$ grep -e ^vmovn -e vmovun -e ^vmovl -e ^vqmov /usr/lib/gcc/aarch64-linux-gnu/7.5.0/include/arm_neon.h
vmovl_high_s8 (int8x16_t a)
vmovl_high_s16 (int16x8_t a)
vmovl_high_s32 (int32x4_t a)
vmovl_high_u8 (uint8x16_t a)
vmovl_high_u16 (uint16x8_t a)
vmovl_high_u32 (uint32x4_t a)
vmovl_s8 (int8x8_t a)
vmovl_s16 (int16x4_t a)
vmovl_s32 (int32x2_t a)
vmovl_u8 (uint8x8_t a)
vmovl_u16 (uint16x4_t a)
vmovl_u32 (uint32x2_t a)
vmovn_high_s16 (int8x8_t a, int16x8_t b)
vmovn_high_s32 (int16x4_t a, int32x4_t b)
vmovn_high_s64 (int32x2_t a, int64x2_t b)
vmovn_high_u16 (uint8x8_t a, uint16x8_t b)
vmovn_high_u32 (uint16x4_t a, uint32x4_t b)
vmovn_high_u64 (uint32x2_t a, uint64x2_t b)
vmovn_s16 (int16x8_t a)
vmovn_s32 (int32x4_t a)
vmovn_s64 (int64x2_t a)
vmovn_u16 (uint16x8_t a)
vmovn_u32 (uint32x4_t a)
vmovn_u64 (uint64x2_t a)
vqmovn_high_s16 (int8x8_t a, int16x8_t b)
vqmovn_high_s32 (int16x4_t a, int32x4_t b)
vqmovn_high_s64 (int32x2_t a, int64x2_t b)
vqmovn_high_u16 (uint8x8_t a, uint16x8_t b)
vqmovn_high_u32 (uint16x4_t a, uint32x4_t b)
vqmovn_high_u64 (uint32x2_t a, uint64x2_t b)
vqmovun_high_s16 (uint8x8_t a, int16x8_t b)
vqmovun_high_s32 (uint16x4_t a, int32x4_t b)
vqmovun_high_s64 (uint32x2_t a, int64x2_t b)
vqmovn_s16 (int16x8_t __a)
vqmovn_s32 (int32x4_t __a)
vqmovn_s64 (int64x2_t __a)
vqmovn_u16 (uint16x8_t __a)
vqmovn_u32 (uint32x4_t __a)
vqmovn_u64 (uint64x2_t __a)
vqmovnh_s16 (int16_t __a)
vqmovns_s32 (int32_t __a)
vqmovnd_s64 (int64_t __a)
vqmovnh_u16 (uint16_t __a)
vqmovns_u32 (uint32_t __a)
vqmovnd_u64 (uint64_t __a)
vqmovun_s16 (int16x8_t __a)
vqmovun_s32 (int32x4_t __a)
vqmovun_s64 (int64x2_t __a)
vqmovunh_s16 (int16_t __a)
vqmovuns_s32 (int32_t __a)
vqmovund_s64 (int64_t __a)
- 全般的に言えることなのだが、
mov
命令はx86系のmov
命令とは全然ちがう -
movn
とmovl
及びqmovn
とqmovun
がある - また、浮動小数点型用の命令は存在しない。全部整数型のみ
movn
命令
- ベクトルの各レーンのbit幅を、半分の幅に縮める
- 要素の型で言うと、
int32_t
をint16_t
に、int16_t
をint8_t
にする
int16_t src[] = {130,256,257,103,-132,-126,-125,-124,};
int16x8_t vsrc = vld1q_s16(src);
int8x8_t vdst0 = vmovn_s16(vsrc);
- 演算結果
0:-126
1:0
2:1
3:103
4:124
5:-126
6:-125
7:-124
- レーン数はそのまま、要素のbit幅だけ縮めるので、結果的に引数が単体の128bit幅ベクトル、戻り値が64bit幅ベクトルとなる
- bit幅を縮めてオーバーフローする場合は、
add
命令のように単純に出力幅分のbitだけ、LSBからマスキングして出力される- 例1:先程のベクトルの要素0番は
130
、16進数表記で0x0082
である。符号あり8bitの正の最大値127
を超えているが、結果は下位8bit0x82
を符号あり8bit整数型として扱って、-126
となっている - 例2:先程のベクトルの要素1番は
256
、16進数表記で0x0100
である。符号あり8bitの正の最大値127
を超えているが、結果は下位8bit0x00
を符号あり8bit整数型として扱って、0
となっている - 例3:先程のベクトルの要素4番は
-132
、16進数表記で0xff7c
である。符号あり8bitの負の最大値-128
を超えているが、結果は下位8bit0x7c
を符号あり8bit整数型として扱って、-124
となっている
- 例1:先程のベクトルの要素0番は
qmovn
命令
-
add
命令のときと同じく、命令の先頭にq
が付くと飽和演算になる - 先程の
movn
命令は単純に下位n
bitを抽出して狭い型の整数型として解釈していたが、qmovn
は入力値が結果のbit幅で結果がオーバーフローする際、最大値もしくは最小値でクランプする
qmovn.cpp
int16_t src[] = {130,256,257,103,-132,-126,-125,-124,};
int16x8_t vsrc = vld1q_s16(src);
int8x8_t vdst1 = vqmovn_s16(vsrc);
- 演算結果
0:127
1:127
2:127
3:103
4:-128
5:-126
6:-125
7:-124
- 要素の
0
番、1
番、2
番はそれぞれ127
に、4
番は-128
にクランプされている
movn_high
命令、qmovn_high
命令
- おなじみ、後ろに
high
が付く派生形 - 引数を2つ取り、第1引数は64bit幅ベクトル、第2引数が128bit幅ベクトル
- 第2引数を狭めて64bit幅ベクトルを生成し、第1引数と連結して128bit幅ベクトルを返す
- 縮める演算は
high
がついてない基本形と全く同じ
movl
命令
- 狭める命令があれば、広げる命令もある。それが
movl
命令 - 引数が64bit幅ベクトルで出力が128bit幅ベクトル
- 符号あり整数型を引数に取る場合は、負数のbit拡張をちゃんとやってくれる
- 8bitの
-1
(0xff
)を16bitの-1
(0xffff
)にしてくれる
- 8bitの
- 符号のありなしは引数と戻り値で共通
movl_high
命令
- 128bit幅ベクトルを取り、128bit幅ベクトルを返す
- ただし、演算に使われるのは、入力ベクトルの後半、上位64bit分だけ
qmovun
命令
-
qmovn
命令に近いが、符号あり整数型を、符号なし整数型に縮める-
int64x2_t
型をuint32x2_t
型に、int32x4_t
をuint16x4_t
型に、int16x8_t
型をuint8x8_t
型に変換する
-
- 引数は128bit幅ベクトル、戻り値は64bit幅ベクトル
qmovun.cpp
int16_t src[] = {130,256,257,103,-132,-126,-125,-124,};
int16x8_t vsrc = vld1q_s16(src);
int8x8_t vdst2 = vqmovun_s16(vsrc);
- 演算結果
0:130
1:255
2:255
3:103
4:0
5:0
6:0
7:0
- 負数は全て
0
に、256
以上の数字は255
に丸められている - なお、やはり
high
が末尾につく派生形もある
早見表
拡張/収縮 | 飽和演算 | 符号無し飽和演算 | 命令 |
---|---|---|---|
拡張 | - | - | movl |
収縮 | N |
- | movn |
収縮 | Y |
N |
qmovn |
収縮 | Y |
Y |
qmovun |
その他
- 図では省略したが、単体用の命令もある
- 単体の要素を取る
mov_n
、mov_n_high
命令は、調べた限りではdup_n
、dup_n_high
命令のエイリアス
おわりに
- 整数型のbit幅を変換する命令
movn
、movl
とその仲間たちを紹介した。 - 明日も手島の担当で、Arm v8.2の内積命令を紹介する予定