MMXなんて実際に使ったことなんてないんだけど

GCCの内部もなーんとなくわかってきたところで、GCCMMX使うのもなんとなくわかってきた。

/* 
 * typedef ... hoge __attribute__ ((mode(V4SI)))でGCC拡張型でtypedefできる。
 * 下ではintってなってるけど、intは多分無視される。floatでもlongでも関係無い。
 *
 *
 * modeっていうのは GCCの内部での型の名前みたいなもの 
 * V4SI = Single Integer 4個 の Vector
 */
typedef int v4si __attribute__ ((mode(V4SI)));

/* Single Float 4個分 */
typedef int v4sf __attribute__ ((mode(V4SF)));

/* Double Float 2個分 */
typedef int v2df __attribute__ ((mode(V2DF)));

/* Quarter Integer 8個分 */
typedef int v8qi __attribute__ ((mode(V8QI)));

typedef unsigned char uchar;

void
vqadds( uchar *x, uchar *y, uchar *z, int elem_num )
{
	v8qi *vx = (v8qi*)x;
	v8qi *vy = (v8qi*)y;
	v8qi *vz = (v8qi*)z;
	
	int i;

	for ( i=0; i<(elem_num/8); i++ ) {
		vx[i] = vy[i] + vz[i];
	}
}

これで、8バイト同時加算ができるはず。おもいっきりGCC拡張なんだけど。
よーするに、「こいつはベクトル型ですよー」っていう型を作っといて、そいつにキャストする、と。unionでそーいうのを作っちゃってもいいか。で、ベクトル型同士で演算を行うと、それに対応するベクトル命令が生成される、という感じだ。


自分でベクトル指定しないといけないんだったら生asm書いたのとあんまり変わらないような気もするんだけど、生asmを使う場合よりもいいのは、

  1. うまくレジスタを割り当ててくれる(かもしれない)
  2. ベクタ命令が無い場合も適当に展開してくれる(-march=i386でも一応動くコードは吐いてくれる)

と、いう感じで、2番目は大きいんじゃないかと。


あと、機種依存しちゃうんだけど、__buitin_ia32_paddusbみたいなのを使えば、対応する命令を吐かせるのもできる。ia32命令については、gcc.infoのC Extensionのところ


Adasさんのところに-mfpmath=sseで4倍速くなるんじゃないかって書いてあるだけど、-msseは「sse使ってもいい」で-mfpmath=sseが「浮動小数点演算にsse命令を使う」っていう意味しか無くて、Vectorizeまでは面倒見てくれないので、オプション付けるだけではsse命令が出たとしても387で計算するのとあんまり変わらないかと。



と、思わせておいて、gcc-4.0ではアラインメントを保証してやれば自動でやってくれる。

typedef float afloat __attribute__((__aligned__(16)));


/*  __restrict__ は「この変数の中身は勝手に変わらない」ぐらいの意味でいいのか? よくわからん。
 * とりあえず必要…らしい */
void
add_fvec( afloat * __restrict__ x, afloat *__restrict__ y, afloat * __restrict__ z )
{
	int i;
	for ( i=0; i<256; i++ ) {
		x[i] = y[i] + z[i];
	}

}

で、

$ gcc -ftree-vectorize -O2 -S -mfpmath=sse -msse2 sse.c 

こんな感じで。まあ、まだちょっと、「GCCは偉いなぁ…」って感動するためだけの観賞用って感じもするけど。
参考: http://gcc.gnu.org/projects/tree-ssa/vectorization.html