ARM NEON の intrinsic を書くことはしばしばあるかもしれないのでまとめておきます.どちらかというと作業記録に近いかもしれない.
基本的な情報
NEON は ARMv7 の SIMD 命令セットです. 1
NEON に使用するレジスタは通常のレジスタとは別であり, 64bit のレジスタが32本 (=128bitのレジスタが16本) あります.また, VFP (浮動小数点コプロセッサ) とレジスタを共有しています.
浮動小数点計算は倍精度には非対応です.
基本的にコンパイラオプションを指定することにより自動的にベクタライズされ SIMD 命令が生成されますが,思うようにうまく行かず自分で記述したい場合は intrinsic を記述するかアセンブラを書くことになります.以下は intrinsic を記述した例です.
実行環境
- Raspbian (32bit)
- Raspberry Pi 3 (armv8)
- gcc 4.9.2
手頃な値段で 64bitのSIMD で遊べる勝つる!!と思って買ったのですが,結局まだ書けずじまいです.かなしい.
方法
intrinsic の関数一覧はここに載っている:
https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/ARM-NEON-Intrinsics.html#ARM-NEON-Intrinsics
arm_neon.h というヘッダファイルがあり,これをインクルードしてコンパイラオプションを指定すれば良い.
手元の環境では arm_neon.h はここにあった:
/usr/lib/gcc/arm-linux-gnueabihf/4.9.2/include/arm_neon.h
実験
32bitの符号無し整数x2を加算する例です.
NEON では uint32x2_t
というように <データ型>x<(レーン)数>_t
という形で表します.
vld1_*
で通常のレジスタから NEON レジスタにロードして演算, vst1_*
で計算結果をストアします.
#include <arm_neon.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
uint32x2_t vx, vy, vz;
uint32_t x[2] = {2, 4};
uint32_t y[2] = {4, 8};
uint32_t z[2];
vx = vld1_u32(x);
vy = vld1_u32(y);
vz = vadd_u32(vx, vy);
vst1_u32(z, vz);
printf("z: %u %u\n", z[0], z[1]);
}
実行結果
できた.オプションはもう少しいじった方が良いかもしれない
raspberrypi:~/tmp/add> gcc --version
gcc (Raspbian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
raspberrypi:~/tmp> gcc -S add.c -mfpu=neon -march=armv7-a -O3
raspberrypi:~/tmp> gcc -o add add.s
raspberrypi:~/tmp> ./add
z: 6 12
raspberrypi:~/tmp>
出力
上記のように生成したアセンブリを下記に示します.(結果は抜粋したもの)
vld1.32
で d16
レジスタにロードして vadd.i32
で加算, vst1.32
でストアしていることが確認できます.だいたい NEON の命令には v
の接頭辞がついているので分かりやすいです.(必ず全てかは確認していない) 2
d16
というのは NEON レジスタで,通常のレジスタの2つ分の長さ (=64bit) を持っています.また,4つ分の長さ (=128bit) レジスタは q8
といった表現になります. 3
main:
@ args = 0, pretend = 0, frame = 24
@ frame_needed = 0, uses_anonymous_args = 0
movw r3, #:lower16:.LANCHOR0
movt r3, #:upper16:.LANCHOR0
add ip, r3, #8
ldmia r3, {r0, r1}
str lr, [sp, #-4]!
sub sp, sp, #28
add r3, sp, #8
stmia sp, {r0, r1}
ldmia ip, {r0, r1}
vld1.32 {d16}, [sp:64]
stmia r3, {r0, r1}
movw r0, #:lower16:.LC2
vld1.32 {d17}, [r3:64]
add r3, sp, #16
vadd.i32 d16, d16, d17
movt r0, #:upper16:.LC2
vst1.32 {d16}, [r3:64]
ldr r1, [sp, #16]
ldr r2, [sp, #20]
bl printf
add sp, sp, #28
@ sp needed
ldr pc, [sp], #4
.size main, .-main
.section .rodata
.align 2
.LANCHOR0 = . + 0
.LC0:
.word 2
.word 4
.LC1:
.word 4
.word 8
.section .rodata.str1.4,"aMS",%progbits,1
.align 2
.LC2:
.ascii "z: %u %u\012\000"
.ident "GCC: (Raspbian 4.9.2-10) 4.9.2"
.section .note.GNU-stack,"",%progbits
調べたら追記したい
- clang/LLVMだと?
- コンパイラオプションについて
- 32bit/64bit
参考資料
ちゃんとググると沢山出るとは思いますが...
- https://ja.wikipedia.org/wiki/ARMアーキテクチャ#Advanced_SIMD_.28NEON.29
- https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/ARM-NEON-Intrinsics.html#ARM-NEON-Intrinsics
- http://www.slideshare.net/linaroorg/intrinsics-demo
- http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489ej/CJAJIIGG.html