これは難しいです。これを実行できる命令は1つではありません。最良の解決策は、データがメモリにあるか、すでにレジスタにあるかによって異なります。
変換を行うには、少なくとも2つの操作が必要です。最初に、次のように引数を並べ替えるベクトルターンを実行します。
a = a1 a2
b = b1 b2
vtrn.32 a, b
a = a1 b1
b = a2 b2
次に、各操作の引数を交換する必要があります。各ベクトルをそれ自体で反転するか、2つのベクトルをクワッドベクトルとして扱い、長い反転を実行します。
temp = {a, b}
temp = a1 b1 a2 b2
vrev64.32 temp, temp
temp = b1 a1 b2 a2 <-- this is what you want.
メモリからデータをロードする場合、NEONはvld2.32命令を使用してデータをロードしている間にこれを実行できるため、最初のvtrn.32命令をスキップできます。これはまさにそれを行う小さなアセンブラ関数です:
.globl asmtest
asmtest:
vld2.32 {d0-d1}, [r0] # load two vectors and transose
vrev64.32 q0, q0 # reverse within d0 and d1
vst1.32 {d0-d1}, [r0] # store result
mov pc, lr # return from subroutine..
ところで、ちょっとした注意:vtrn.32、vzip.32、vuzp.32の命令は同じです(ただし、32ビットエンティティを使用している場合のみ)
そして、NEON組み込み関数を使用しますか?まあ-単にあなたがねじ込まれていると言った。すでに知っているように、あるタイプから別のタイプに直接キャストしたり、クワッドベクトルとダブルベクトルを直接混合したりすることはできません。
これは私が組み込み関数を使用して思いついた最高のものです(読みやすさのためにvld2.32トリックを使用していません):
int main (int argc, char **args)
{
const float32_t data[4] =
{
1, 2, 3, 4
};
float32_t output[4];
/* load test vectors */
float32x2_t a = vld1_f32 (data + 0);
float32x2_t b = vld1_f32 (data + 2);
/* transpose and convert to float32x4_t */
float32x2x2_t temp = vzip_f32 (b,a);
float32x4_t result = vcombine_f32 (temp.val[0], temp.val[1]);
/* store for printing */
vst1q_f32 (output, result);
/* print out the original and transposed result */
printf ("%f %f %f %f\n", data[0], data[1], data[2], data[3]);
printf ("%f %f %f %f\n", output[0], output[1], output[2], output[3]);
}
GCCを使用している場合、これは機能しますが、GCCによって生成されたコードはひどく遅くなります。NEONの本質的なサポートはまだ非常に若いです。ここでは、単純なCコードを使用するとパフォーマンスが向上する可能性があります。