ここには、AltiVecを扱ってて自分が気づいたことや、AppleのAltiVecページに挙げられている演算アルゴリズムを載せている…つもり。

ベクトル算術演算


絶対値
整数、浮動小数両方とも、大きい方の値を取り出す命令はあるので、それと減算命令を利用する。以下は、32bit符合つき整数の場合の演算。
vspltisw v2, 0	;v2 = 0
vsubuwm v0, v2, v1	;v0 = 0 - v1 = -v1
vmaxsw v0, v0, v1	;v0 = -v1 > v1 ? -v1 : v1

レジスタの内容をコピー
なんでこんな簡単な命令がないのか!でも、よく見てみると論理積命令や論理和命令があるじゃないか。xとxの論理積はxだし、xとxの論理和もxだ。
vor v0, v1, v1	;v0 = (v1 | v1) = v1

1byte整数の乗算
1byte同士の乗算命令は、奇数番目か偶数番目の要素を乗算したものを2byte整数にして出力する。そのため、2つの乗算結果を合成することで、乗算を実現する必要がある。
vmulesb v3, v1, v2	;v3 = v1 * v2(奇数番目の符合つき1byte整数乗算結果)
vmulosb v4, v1, v2	;v4 = v1 * v2(偶数番目の符合つき1byte整数乗算結果)
vpkuhum v3, v3, v3	;v3 = (signed char)v3(1byte整数に修正)
vpkuhum v4, v4, v4	;v4 = (signed char)v4(1byte整数に修正)
vmrglb v0, v3, v4	;乗算結果を合成
なお、ループ中で1byteの乗算を行うなら、あらかじめベクトルを用意しておいて撹拌命令を使った方が命令数が少ない。ループに限らず、乗算回数が多い場合はこちらの方が有効かもしれない。
;v5 = (0x1, 0x11, 0x3, 0x13, 0x5, 0x15, 0x7, 0x17, 0x9, 0x19, 0xb, 0x1b, 0xd, 0x1d, 0xf, 0x1f)
...
vmulesb v3, v1, v2	;v3 = v1 * v2(奇数番目の符合つき1byte整数乗算結果)
vmulosb v4, v1, v2	;v4 = v1 * v2(偶数番目の符合つき1byte整数乗算結果)
vperm v0, v3, v4, v5	;撹拌命令で乗算結果の下1byteのみを摘出して合成

2byte整数の乗算
2byte同士の乗算は1byte整数の乗算と同じような演算でも行えるが、2byte整数用の演算の中に、2byte同士の乗算後に2byte整数を加算した結果を2byte整数として出力する命令がある。速いかどうかは不明だが、これを使う方が命令数が少なくて済む。
vspltish v3, 0	;v3 = 0
vmladduhm v0, v1, v2, v3	;v0 = v1 * v2 + 0

4byte整数の乗算
AltiVecには2byte整数までの乗算はあるが、4byte整数の乗算はない。仕方がないので、2byte整数の乗算を使って4byte整数の乗算を行う。4byte整数の乗算結果に影響を及ぼすのは、上2byte×下2byteが2つと、下2byte×下2byteである。
vspltisw v3, -16	;4byteシフト命令は下5bitしか見ないので、負の数を生成しても問題ない
vslptisw v4, 0
vrlw v5, v2, v3	;v5 = (v2 << 16) | ( (unsigned)v2 >> 16)(回転シフト)
vmulouh v6, v1, v2	;v1 * v2の下16bitの乗算結果
vmsumuhm v0, v1, v5, v4	;v1とv5の上16bitの乗算結果×2の合計
vslw v0, v0, v3	;(unsigned long)v0 << 16
vadduwm v0, v0, v6	;乗算結果の上16bitと下16bitを合成

ベクトル論理演算


否定演算
AltiVecにはいくつか論理演算があるものの、否定を取るだけの演算が存在しない。ところで、vnor命令は論理和の否定をとる命令だ。ソースレジスタ2つが同じレジスタの場合、vnorによる演算結果は否定となる。
vnor v0, v1, v1	;v0 = ¬(v1 | v1) = ¬v1

符合反転
ある値の符合を反転しようとした場合、符合反転をした値を取り出す命令がないので、代わりに0との減算を行う必要がある。他にも、否定を取ってから1を足すという方法もあるが、そちらの方が命令数が多い。
vspltisw v2, 0		;v2 <- 0
vsubuwm v0, v2, v1	;v0 = 0 - v1 = -v1

ベクトル浮動小数演算


浮動小数即値のロード
整数の即値ロードは(使いにくいものの)AltiVecにあるが、浮動小数の即値ロードはない。が、整数を浮動小数に変換する命令はあるので、整数の即値ロード+浮動小数変換を使えば、浮動小数即値を生成できる。ちなみに、0は整数でも浮動小数でも全てのビットが0なので、浮動小数変換命令を通す必要がない。
vspltisw v1, 1	;v1 <- (int)1
vcfsx v0, v1, 0	;v0 = (float)1

浮動小数の乗算
AltiVecの浮動小数演算命令に乗算命令は存在しないが、乗算と加算を行う命令は存在する。その命令において、加算する値を0にすれば乗算命令になる。乗算と加算を1セットで演算することの方が乗算単独で使うことより多いから、こんな仕様になったのではないかな、と思う。
vspltisw v3, 0		;v3 <- 0.0
vmaddfp v0, v1, v2, v3	;v0 = (float)v1 * (float)v2 + 0.0

浮動小数の除算
なんと、AltiVecに除算命令は一つも存在しない。しかし、浮動小数に関しては、逆数を求める命令があるので、それと乗算加算命令を利用すれば、除算を行うことができる。(『ハッカーの楽しみ』に書いてある方法を使えば、整数と定数の除算もできると思う)
vrefp v3, v2		;v2 = (estimated) (1.0 / (float)v4)
vspltisw v5, 1		;v5 = 1
vcfsx v5, v5, 0		;v5 = (float)1
vnmsubfp v4, v3, v2, v5	;v4 = -v3 * v2 + 1.0
vmaddfp v4, v4, v3, v3	;v4 = v4 * v3 + v3(v2の逆数の計算終了)
vspltisw v6, 0		;v6 = 0.0
vmaddfp v0, v1, v4, v6	;v0 = v1 / v2 + 0.0
平方根
平方根の逆数を求めてから、乗算加算命令を使えば求められる。
vrsqrtefp v2, v1	;v2 = (estimated)(1.0 / sqrt(v1))
vspltisw v3, 0		;v3 = 0.0
vspltisw v5, 1		;v5 = 1
vcfsx v7, v5, 0		;v7 = 1.0
vcfsx v5, v5, 1		;v5 = 0.5
vmaddfp v4, v2, v2, v3
vmaddfp v6, v2, v3, v5
vnmsubfp v0, v1, v4, v7
vmaddfp v0, v0, v6, v2
vmaddfp v0, v1, v0, v3

その他


スレッド別ベクトルレジスタの管理方法
OSはあるスレッドをしばらく動かしてから、割り込みをかけて別スレッドに処理を移すようにしている。この時、割り込みをかけられる側のスレッドが保持しているレジスタの状態は全て保存されなければならない。
AltiVecを搭載したプロセッサは、当然ながらベクトルレジスタも退避しなければならない。しかし、ベクトルレジスタは32本もあるので、退避や復帰時にアクセスするメモリ量は32 * 16 = 512バイト。これを毎スレッド切り替えが起こった時にアクセスすると、それだけで膨大な時間がかかってしまう。
そこで、AltiVec搭載プロセッサでは、『AltiVecモード切り替えスイッチ』でAltiVecを使用している時だけ、ベクトルレジスタの内容を退避/復帰するようにしているらしい。このモード切り替えの処理は、具体的には
mfvscrを使ってベクトル状態レジスタの内容を取り出し、取り出した値と0xc0000000との論理和を計算し、mtvscrを使ってベクトル状態レジスタに値をセットしている部分のようだ。

戻る