2009年09月10日

gccでNEONのSIMD命令を生成させる方法

この記事ではgcc 4.4.0を使用しています。もっと新しいgccの場合はこちら。

2013年06月27日 ARMのNEONのSIMD命令をgccのオートベクタライズの最適化で使う方法

AndroidのSDKのgoldfishのCPUをcortex-A8に置き換えてNEONのSIMD命令を試す(その2) のときにはarm_neon.hに定義されているintrinsicsを使ってNEONのSIMD命令を生成させました。この方法だとNEONの命令について詳細を知っていなければなりませんし、なによりもそのプログラムがNEONに依存したものになってしまいます。

今回はコンパイラの最適化の機能を使ってNEONのSIMD命令を生成させるコツを紹介します。



以前にSIMD演算のサンプルとしてこんなプログラムを書きました。

void vmla(float* a, float* x, float* y, float* z)
{
    a[0] = x[0] * y[0] + z[0];
    a[1] = x[1] * y[1] + z[1];
    a[2] = x[2] * y[2] + z[2];
    a[3] = x[3] * y[3] + z[3];
}

これだと実際にはNEONのSIMD命令は生成されませんが、これをちょっと書き直すとNEONのSIMD命令が生成されるようになります。

ポイントは

  • 配列をループで使うこと。
  • その配列のポインタに __restrict の属性がついていること。

のようです。

__restrict はそのポインタが他の変数と重なっていないことをコンパイラに知らせるためのものです。

これらを修正したのが以下のプログラムです。

void vmla2(float* __restrict a, float* __restrict x, float* __restrict y, float* __restrict z)
{
    int i;
    for (i=0; i< 4; i++) {
        a[i] = x[i] * y[i] + z[i];
    }
}

これをneon0.c というファイルに格納します。

今回コンパイラはgcc 4.4.0を使っています。

コンパイルオプションは以下のものをつけます。

arm-eabi-gcc -O2 -march=armv7-a -ftree-vectorize -mhard-float -mfloat-abi=softfp -mfpu=neon -mvectorize-with-neon-quad -S neon0.c 

生成されたコードは以下のとおり。

	.align	2
	.global	vmla2
	.type	vmla2, %function
vmla2:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	orr	ip, r1, r0
	orr	ip, r2, ip
	orr	ip, r3, ip
	tst	ip, #15
	str	r4, [sp, #-4]!
	bne	.L4
	vldmia	r1, {d18-d19}
	vldmia	r2, {d16-d17}
	vmul.f32	q8, q9, q8
	vldmia	r3, {d18-d19}
	vadd.f32	q8, q8, q9
	vstmia	r0, {d16-d17}
.L7:
	ldmfd	sp!, {r4}
	bx	lr
.L4:
	mov	ip, #0
.L6:
	add	r4, r1, ip
	flds	s13, [r4, #0]
	add	r4, r2, ip
	flds	s14, [r4, #0]
	add	r4, r3, ip
	flds	s15, [r4, #0]
	fmacs	s15, s13, s14
	fmrs	r4, s15
	str	r4, [r0, ip]	@ float
	add	ip, ip, #4
	cmp	ip, #16
	bne	.L6
	b	.L7
	.size	vmla2, .-vmla2

NEONのSIMD命令が生成されましたが、積和命令でなく、乗算と加算に分かれました。このプログラムでは直前にメモリからロードしているので、そのロードの遅延を考慮するとこの方が速いのかもしれません。また、3つのオペランドのアライメントをチェックしていて3つとも16バイト境界にあるときのみNEON命令を使うようになっています。興味深いです。

このサイトを参考にさせていただきました。



トラックバックURL

トラックバック一覧

1. NetWalkerのgccでNEONしてみる  [ Android Zaurusの日記 ]   2009年09月28日 17:22
NetWalkerはgccがプリインストールされてて、セルフビルドが出来て幸せ。i.MX51はNEONを積んでて浮動点小数演算を加速したりできるらしいので、KMCさんのブログにある方法*1でベクトル演算命令を吐くかどうか試してみた。結論。吐いた。 普通にgcc*2: __aeabi_fmul, __aeabi

コメント一覧

1. Posted by kotak   2009年09月10日 15:59
こんにちは。
いつも興味深く拝見させていただいています。

メインの話題とずれていて恐縮ですが
> -mhard-float -mfloat-abi=softfp
の "-mhard-float" の指定は不要ではないでしょうか?

gcc-4.3のmanを参照すると、
> -mfloat-abi=name
> Specifies which ABI to use for floating point values. Permissible values are: soft, softfp and hard.
>
> soft and hard are equivalent to -msoft-float and -mhard-float respectively.

と書いてあるので、"-mhard-float" は "-mfloat-abi=hard" と同じだというように理解しています。
2. Posted by koba   2009年09月10日 17:43
kotakさん

-mhard-floatは (Androidでのデフォルトである) -msoft-floatとの対比の意味で明示的に指定しています。

ついでの話をすると、
-mhard-floatをつけると暗黙のうちに -mfloat-abi=hard になりますが、現状ARMのコンパイラではfloatのABIでhardはサポートされていません。なので、必ず -mhard-float を指定したら -mfloat-abi=softfp も指定する必要があります。
次にでてくるgcc 4.5 ではARMでの-mfloat-api=hardがサポートされるようです。
3. Posted by kotak   2009年09月10日 19:50
> -mhard-floatをつけると暗黙のうちに -mfloat-abi=hard になりますが、現状ARMのコンパイラではfloatのABIでhardはサポートされていません。
というのは、以下のように "-mhard-float" をつけてコンパイルするとエラーになることからも、分かります。
$ arm-linux-gnueabi-gcc -mhard-float sample.c
sample.c:1: sorry, unimplemented: -mfloat-abi=hard and VFP

> なので、必ず -mhard-float を指定したら -mfloat-abi=softfp も指定する必要があります。
というのが、分からないです。

以下のように、"-mfloat-abi=softfp" と "-mhard-float" の順番を入れ替えて、"-mhard-float" を後ろに持ってくると、"-mhard-float"だけを指定したときと同じエラーになります。
$ arm-linux-gnueabi-gcc -mhard-float -mfloat-abi=softfp sample.c
(エラーなし)

$ arm-linux-gnueabi-gcc -mfloat-abi=softfp -mhard-float sample.c
sample.c:1: sorry, unimplemented: -mfloat-abi=hard and VFP

単に、後ろに書いたオプションに上書きされているだけだと思っていたのですが。。。


-mhard-floatと-msoft-floatのそれぞれに、(floatのABIとして)-mfloat-abi=softfpか否かが存在する

のではなく、

-float-abi=hard or soft or softfp の三者択一

だと理解しているのですが、何か盛大に勘違いしているでしょうか?
4. Posted by koba   2009年09月10日 22:54
難しく考える必要はないのではないでしょうか?

ソースを追って見ればはっきりすることですが、gccのアーキテクチャ共通の部分の処理として -mhard-floatのオプションを解釈したときに内部的なfloat-abiのフラグもhardにセットしていて、ARMアーキテクチャ固有の部分の処理で-mhard-floatとfloat-abi=hardの組み合わせをエラーにしているだけかと。そのエラーチェックのところでは明示的に-mfloat-abi=のオプションで指定されたのか暗黙的に設定されたのかは見ていない(またはその時点では情報が残っていない)のでしょう。


PowerPCやMIPSでは-mhard-floatの指定で連動してfloat-abi=hardになるほうが都合がよいし、それで問題がないのですが、ARMだけが特殊でややこしいことになっています。

コメントする

名前
URL
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード