LoginSignup
21
23

More than 5 years have passed since last update.

ARM NEON intrinsic を書く

Posted at

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_* で計算結果をストアします.

add.c
#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.32d16 レジスタにロードして vadd.i32 で加算, vst1.32 でストアしていることが確認できます.だいたい NEON の命令には v の接頭辞がついているので分かりやすいです.(必ず全てかは確認していない) 2
d16 というのは NEON レジスタで,通常のレジスタの2つ分の長さ (=64bit) を持っています.また,4つ分の長さ (=128bit) レジスタは q8 といった表現になります. 3

add.s
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

参考資料

ちゃんとググると沢山出るとは思いますが...


  1. ARMv6にも SIMD 命令セットがあるらしいですが,こちらは NEON とは言いません. 

  2. ちなみに ARMv8 の SIMD 命令はその接頭辞が消えます 

  3. ただし手元で実行したら {d16-d17} といった表記になったんですけどこれはアセンブラの表記の揺れ? 

21
23
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
21
23