一次元配列に含まれる最小値か最大値を見つけ、かつその位置を得るプログラムを紹介します。以降に処理の概要図を示します。
64ビット整数型
呼び出し側・符号付
以降に、呼び出し側のC++コードを示します。common.hをincludeしていますがSIMDで記述したアセンブリ言語の関数と等価な機能を持つC++言語で記述した共通関数のヘッダです。最下部にソースコードを示します。条件は関数名に含まれます。
#include "..\common.h" // TEMPLATES #define T long long extern "C" { T aminsq(const T*, const size_t, int*); T amaxsq(const T*, const size_t, int*); } // main int main(void) { const size_t ArrLen = 4096; static_assert(ArrLen % 16 == 0, "number of elements must be an integral multiple of 16."); T a[ArrLen]; int cminIndex, aminIndex, cmaxIndex, amaxIndex; init(a, ArrLen); T cmin = cMin (a, ArrLen, &cminIndex); T amin = aminsq(a, ArrLen, &aminIndex); T cmax = cMax (a, ArrLen, &cmaxIndex); T amax = amaxsq(a, ArrLen, &amaxIndex); cout << fixed << setprecision(2); cout << "cpp min = " << cmin << ", index = " << cminIndex << endl; cout << "asm min = " << amin << ", index = " << aminIndex << endl; cout << "cpp max = " << cmax << ", index = " << cmaxIndex << endl; cout << "asm max = " << amax << ", index = " << amaxIndex << endl; return 0; }
一次元配列にランダムな値を与え、先ほどの関数を呼び出し、配列内の最小値や最大値を表示します。
呼び出され側
64ビット整数型一次元配列に含まれる要素の、最小値や最大値を探し、そのインデックス値ます。以降に、マクロを使って開発したアセンブラーのコードを示します。最小値や最大値、そして符号ごとに関数を記述するのは面倒なのでマクロを使って記述します。本関数へ渡されるレジスターとデータの関係を以降の表に示します。
レジスタ | 内容 |
---|---|
rcx | 入力配列の先頭アドレス |
rdx | 配列の要素数 |
r8 | 探した値の位置を返すint型のアドレス |
mymacro macro MNAME, MINST, COND public MNAME align 16 MNAME proc vmovdqu64 zmm3, zmmword ptr index ; zmm3 = index vmovdqa64 zmm5, zmm3 ; zmm5 = index(min/max) mov r10, 8 vpbroadcastq zmm2, r10 ; zmm2 = 8,...,8 vmovdqu64 zmm0, qword ptr [rcx] ; zmm0 = initial value xor rax, rax ; clear index loop_f: vmovdqu64 zmm1, zmmword ptr [rcx+rax*8] ; load a[] MINST k1, zmm1, zmm0, COND vmovdqa64 zmm0{k1}, zmm1 ; zmm0 = {k1}a[] vmovdqa64 zmm5{k1}, zmm3 ; unpate index(min/max) vpaddq zmm3, zmm3, zmm2 ; next index add rax, 8 cmp rax, rdx jb short loop_f ; - reduce min/max, integer - vextracti64x4 ymm1, zmm0, 1 ; d0, d1, d2, d3 | d4, d5, d6, d7 MINST k1, ymm1, ymm0, COND vmovdqa64 ymm0{k1}, ymm1 vextracti64x2 xmm1, ymm0, 1 MINST k2, xmm1, xmm0, COND vmovdqa64 xmm0{k2}, xmm1 vpsrldq xmm1, xmm0, 8 MINST k3, xmm1, xmm0, COND vmovdqa64 xmm0{k3}, xmm1 vmovq rax, xmm0 ; rax = return value ; - reduce index, integer - vmovdqa64 zmm0, zmm5 ; load index, restore from zmm5 vextracti64x4 ymm1, zmm0, 1 ; d0, d1, d2, d3 | d4, d5, d6, d7 vmovdqa64 ymm0{k1}, ymm1 vextracti64x2 xmm1, ymm0, 1 vmovdqa64 xmm0{k2}, xmm1 vpsrldq xmm1, xmm0, 8 vmovdqa64 xmm0{k3}, xmm1 vmovd dword ptr [r8], xmm0 ; return index ret MNAME endp endm ;------------------------------------------------------------------- ;.code _TEXT segment mymacro aminsq, vpcmpq, _MM_CMPINT_LT ; < mymacro amaxsq, vpcmpq, _MM_CMPINT_GT ; > mymacro aminuq, vpcmpuq, _MM_CMPINT_LT ; < mymacro amaxuq, vpcmpuq, _MM_CMPINT_GT ; > _TEXT ends ;.data __DATA segment ;.data index dq 0, 1, 2, 3, 4, 5, 6, 7 __DATA ends end end
mymacro マクロを定義し、これを使ってaminsq、aminuq、amaxsq、amaxuq関数を作ります。各関数の機能を表で示します。
関数名 | 機能 |
---|---|
aminsq | 一次元符号付64ビット整数配列から最小値とその位置を取り出す。 |
aminuq | 一次元符号なし64ビット整数配列から最小値とその位置を取り出す。 |
amaxsq | 一次元符号付64ビット整数配列から最大値とその位置を取り出す。 |
amaxuq | 一次元符号なし64ビット整数配列から最大値とその位置を取り出す。 |
コード量は多くありませんが、若干面倒な処理を行っていますので、関数の内容を先頭から説明します。本プログラムはマクロで記述しているため、命令や条件が置き換えられます。このため一般的に説明すると面倒になるため、符号付64ビット整数型配列から最小値を求める場合を解説します。
vmovdqu64命令でzmm3レジスターへインデックスの初期値を設定します。zmm5レジスターへ同じ値を設定しますが、このレジスターは探した最小値のインデックスを保持します。vpbroadcastq命令で、zmm2レジスターへzmm3レジスターが保持するインデックスの増分値を設定します。本例では、64ビット整数を対象とするため一回で8要素を処理します。これから、インデックスの増分値は8 です。
ループ処理へ入る前に、vmovdqu64命令で、zmm0レジスターに配列の先頭の要素を読み込みます。これは、最小値を探すための初期値です。次にraxレジスターをクリアします。このraxレジスターは配列を参照するインデックスを保持します。最初の値は読み込み済みですので、次の処理対象のインデックスを設定するのが良いのでしょうが、ここでは単純にクリアします。
前準備が終わったのでループ処理に入ります。vmovdqu64命令で、処理対象の要素をzmm1レジスターへ読み込みます。読み込むアドレスは、先頭アドレスを保持しているrcxレジスターへ、インデックスを保持するraxレジスターへ8(64ビット整数型のバイト長)を乗じた値を加算することによって求めます。
次に、MINST を指定していますが、符号付64ビット整数型配列から最小値を求める場合はvpcmpq命令へ、そして第三引数の条件は_MM_CMPINT_LTへ置き換えられます。これでk1マスクレジスターへ更新する要素のビットが立ったマスク値が格納されます。
vmovdqa64命令へk1マスクレジスターと、読み込んだzmm1レジスターを与えることによって、最小値を保持するzmm0レジスターを更新します。これを繰り返すことによって、zmm0レジスターに8要素単位の最小値が求まります。先のプログラムは、vpminsq命令を使用しましたが、本プログラムは値だけでなくインデックスも返す必要もあるためvpcmpq命令を使用し、マスクレジスターへ値を設定します。
vmovdqa64命令へk1マスクレジスターと、読み込んだ配列のインデックスを保持するzmm3レジスターを与えることによって最小値のインデックスを保持するzmm5レジスターを更新します。これを繰り返すことによって、zmm3レジスターは最小値を保持するzmm0レジスターに対応したインデックス値を保持します。
読み込んだ配列のインデックスを保持するzmm3レジスターの値はループのたびに更新します。以降に図で示します。
インデックス値はint型ですが、データの値とインデックスの値を対応させるため、zmm3レジスターやzmm2レジスターの値は64ビット整数値とします。
これを全要素が終わるまで繰り返します。ループを繰り返す必要があるか判断するため、raxレジスターへ8(64ビット整数型のバイト長)を加算します。全要素の完了検査は、lengthが格納されているrdxレジスターと配列のインデックスを保持するraxレジスターを比較して判断します。全部の処理が終わっていなければループの先頭に戻り、そうでなければループを抜けます。
全要素の処理が完了するとzmm0レジスターへ8要素の最小値が格納され、zmm5レジスターへzmm0レジスターに格納された要素に対応するインデックス値が格納されます。この二つの8要素をreduce処理し、それぞれの最終の値を求めます。以降に、reduceのコードを示します。実際のコードはマクロで書かれていますが、ここでは符号付64ビット整数型の最小値を求めるように展開されたコードを示します。
vextracti64x4 ymm1, zmm0, 1 ; d0, d1, d2, d3 | d4, d5, d6, d7 vpcmpq k1, ymm1, ymm0, _MM_CMPINT_LT vmovdqa64 ymm0{k1}, ymm1 vextracti64x2 xmm1, ymm0, 1 vpcmpq k2, xmm1, xmm0, _MM_CMPINT_LT vmovdqa64 xmm0{k2}, xmm1 vpsrldq xmm1, xmm0, 8 vpcmpq k3, xmm1, xmm0, _MM_CMPINT_LT vmovdqa64 xmm0{k3}, xmm1 vmovq rax, xmm0 ; rax = return value
上記のコードを順次図を示しながら説明します。基本的にレジスターを半分に畳みながら、順次結果を得る方法を採用します。
まず、zmmレジスターに格納されている8個の要素を4個の要素にreduceしymmレジスターへ格納します。zmm0レジスターに格納されている上位4要素をymm1レジスターへ抽出します。下位4要素はzmm0レジスターの下位であるymm0レジスターへ残されます。この二つのレジスターをvpcmpq 命令へ与え、条件で与えた_MM_CMPINT_LT から小さな値を持つ要素に対応したビットがセットされ、k1マスクレジスターへ設定されます。
次に、このマスクレジスターをvmovdqa64命令に与え、ymm1レジスターの要素がymm0レジスターの要素より小さければ、ymm1レジスターからymm0レジスターへ移動します。これによって、8個の要素が4個の要素へreduceされます。このマスクレジスターは、インデックスを求めるときも利用しますので壊さないようにします。
8要素を4要素にreduceできましたので、今度は4要素を2要素にreduceします。
基本的な処理方法は同じです。使用するレジスターがymmレジスターからxmmレジスターへ変わり、マスクレジスターはk2を利用します。
4要素を2要素にreduceできましたので、最後に2要素を1要素にreduceします。
基本的な処理方法は同じです。使用するレジスターがxmmレジスターですが、上位は無視します。マスクレジスターはk3を利用します。
値のreduceができましたので、インデックス値をreduceします。全要素の処理が完了すると、zmm0レジスターに格納された要素に対応するインデックス値が、zmm5レジスターへ格納されます。zmm5レジスターの8要素をreduce処理し、最終のインデックス値を求めます。ただし、インデックス値に相関や規則性はないため、先の処理で作成したマスクレジスターk1、k2、k3を利用します。以降に、reduceのコードを示します。
vmovdqa64 zmm0, zmm5 ; load index, restore from zmm5 vextracti64x4 ymm1, zmm0, 1 ; d0, d1, d2, d3 | d4, d5, d6, d7 vmovdqa64 ymm0{k1}, ymm1 vextracti64x2 xmm1, ymm0, 1 vmovdqa64 xmm0{k2}, xmm1 vpsrldq xmm1, xmm0, 8 vmovdqa64 xmm0{k3}, xmm1 vmovd dword ptr [r8], xmm0 ; return index
先の最小値をreduceする処理から比較処理を抜き、作成済みのマスクレジスターk1、k2、k3を利用してインデックス値をreduceします。インデックス値が格納されているzmm5レジスターをzmm0レジスターへ代入し、以降は先の処理を簡略化して処理します。要素の値を処理したときに得た、マスクレジスターk1、k2、k3を利用するため比較は不要です。
common.h、共通関数
ヘッダーファイルの一部を示します。簡単な内容なので、説明は省きします。
: // min template <typename T> T cMin(const T* a, const size_t length, int* index) { T r = a[0]; for (size_t i = 0; i < length; i++) { if (r > a[i]) { r = a[i]; *index = i; } } return r; } // max template <typename T> T cMax(const T* a, const size_t length, int* index) { T r = a[0]; for (size_t i = 0; i < length; i++) { if (r < a[i]) { r = a[i]; *index = i; } } return r; } :
すべての型に対応させるため、テンプレート関数とします。cMin関数は一次元配列から最小値とインデックス値を探し、cMax 関数は最大値とインデックス値を探します。
このプログラムの実行例を示します。
実行結果(signed)
C:\> minmaxWidxQ
cpp min = -16362, index = 1880
asm min = -16362, index = 1880
cpp max = 16382, index = 2367
asm max = 16382, index = 2367
reduceのまとめ
zmmレジスターに格納されている64ビット整数をreduceする方法は解説しましたが、32ビット整数、16ビット整数、8ビット整数、64ビット浮動小数点、そして32ビット浮動小数点については説明していません。ここでは、それらについて説明しましょう。
32ビット整数をreduce
以降に、32ビット整数をreduceするコードと、それを図で示します。実際のコードはマクロで書かれていますが、ここでは符号付32ビット整数型の最小値を求めるように展開されたコードを示します。
vextracti32x8 ymm1, zmm0, 1 ; d0, d1, ... d7 | d8, d9, ... d15 vpcmpd k1, ymm1, ymm0, _MM_CMPINT_LT vmovdqa32 ymm0{k1}, ymm1 vextracti128 xmm1, ymm0, 1 vpcmpd k2, xmm1, xmm0, _MM_CMPINT_LT vmovdqa32 xmm0{k2}, xmm1 pshufd xmm1, xmm0, 14 ; 14 = _MM_SHUFFLE(0, 0, 3, 2), 0x0E = 14 vpcmpd k3, xmm1, xmm0, _MM_CMPINT_LT vmovdqa32 xmm0{k3}, xmm1 pshufd xmm1, xmm0, 1 ; 1 = _MM_SHUFFLE(0, 0, 0, 1), 0x01 = 1 vpcmpd k4, xmm1, xmm0, _MM_CMPINT_LT vmovdqa32 xmm0{k4}, xmm1 vmovd eax, xmm0 ; eax = return value
zmm0レジスターに格納されている16個の符号付32ビット整数から最小値を取り出す方法を解説します。先のプログラムは値だけを取り出しますが、本プログラムはインデックスを取り出すためvpcmpd命令とマスクレジスターを利用します。上記のコードを順次図を示しながら説明します。基本的にレジスターを半分に畳みながら、順次結果を得る方法を採用します。
まず、zmmレジスターに格納されている16個の要素を8個の要素にreduceしymmレジスターへ格納します。
zmm0レジスターに格納されている上位8要素をymm1レジスターへ抽出します。下位8要素はzmm0レジスターの下位であるymm0レジスターへ残されます。この二つのレジスターをvpcmpq 命令へ与え、条件で与えた_MM_CMPINT_LT から小さな値を持つ要素に対応したビットがセットされ、k1マスクレジスターへ設定されます。
次に、このマスクレジスターをvmovdqa32命令に与え、ymm1レジスターの要素がymm0レジスターの要素より小さければ、ymm1レジスターからymm0レジスターへ移動します。これによって、16個の要素が8個の要素へreduceされます。このマスクレジスターは、インデックスを求めるときも利用しますので壊さないようにします。
16要素を8要素にreduceできましたので、今度は8要素を4要素にreduceします。
ymm0レジスターに格納されている上位4要素をxmm1レジスターへ抽出します。下位4要素はymm0レジスターの下位であるxmm0レジスターへ残されます。この二つのレジスターをvpcmpq 命令へ与え、条件で与えた_MM_CMPINT_LT から小さな値を持つ要素に対応したビットがセットされ、k2マスクレジスターへ設定されます。
次に、このマスクレジスターをvmovdqa32命令に与え、xmm1レジスターの要素がxmm0レジスターの要素より小さければ、xmm1レジスターからxmm0レジスターへ移動します。これによって、8個の要素が4個の要素へreduceされます。このマスクレジスターは、インデックスを求めるときも利用しますので壊さないようにします。
8要素を4要素にreduceできましたので、今度は4要素を2要素にreduceします。
xmm0レジスターに格納されている上位2要素をxmm1レジスターへ抽出します。下位2要素はxmm0レジスターの下位へ残されます。この二つのレジスターをvpcmpq 命令へ与え、条件で与えた_MM_CMPINT_LT から小さな値を持つ要素に対応したビットがセットされ、k3マスクレジスターへ設定されます。
次に、このマスクレジスターをvmovdqa32命令に与え、xmm1レジスターの要素がxmm0レジスターの要素より小さければ、xmm1レジスターからxmm0レジスターへ移動します。これによって、4個の要素が2個の要素へreduceされます。処理自体はxmmレジスターの4要素を対象としますが、上位の2要素は意味がありません。このマスクレジスターは、インデックスを求めるときも利用しますので壊さないようにします。
4要素を2要素にreduceできましたので、今度は2要素を1要素にreduceします。
xmm0レジスターに格納されている下位2要素の内の上位要素をxmm1レジスターの最下位へ移動します。下位1要素はxmm0レジスターの最下位へ残されます。この二つのレジスターをvpcmpq 命令へ与え、条件で与えた_MM_CMPINT_LT から小さな値を持つ要素に対応したビットがセットされ、k4マスクレジスターへ設定されます。
次に、このマスクレジスターをvmovdqa32命令に与え、xmm1レジスターの要素がxmm0レジスターの要素より小さければ、xmm1レジスターからxmm0レジスターへ移動します。これによって、2個の要素が1個の要素へreduceされます。処理自体はxmmレジスターの4要素を対象としますが、上位の3要素は意味がありません。このマスクレジスターは、インデックスを求めるときも利用しますので壊さないようにします。
最後に、xmm0レジスターの最下位要素をeaxレジスターへ移動します。これは関数の戻り値として使用されます。
値のreduceができましたので、インデックス値をreduceします。全要素の処理が完了すると、zmm0レジスターに格納された要素に対応するインデックス値が、zmm5レジスターへ格納されます。zmm5レジスターの16要素をreduce処理し、最終のインデックス値を求めます。インデックス値に相関や規則性はないため、先の処理で作成したマスクレジスターk1、k2、k3、k4を利用します。以降に、reduceのコードを示します。
vmovdqa32 zmm0, zmm5 ; load index, restore from zmm5 vextracti32x8 ymm1, zmm0, 1 ; d0, d1, ... d7 | d8, d9, ... d15 vmovdqa32 ymm0{k1}, ymm1 vextracti128 xmm1, ymm0, 1 vmovdqa32 xmm0{k2}, xmm1 pshufd xmm1, xmm0, 14 ; 14 = _MM_SHUFFLE(0, 0, 3, 2), 0x0E = 14 vmovdqa32 xmm0{k3}, xmm1 pshufd xmm1, xmm0, 1 ; 1 = _MM_SHUFFLE(0, 0, 0, 1), 0x01 = 1 vmovdqa32 xmm0{k4}, xmm1 vmovd dword ptr [r8], xmm0 ; return index
先の最小値をreduceする処理から比較処理を抜き、作成済みのマスクレジスターk1、k2、k3、k4を利用してインデックス値をreduceします。インデックス値が格納されているzmm5レジスターをzmm0レジスターへ代入し、以降は先の処理を簡略化して処理します。要素の値を処理したときに得た、マスクレジスターk1、k2、k3、k4を利用するため比較は不要です。
64ビット浮動小数点をreduce
以降に、64ビット浮動小数点をreduceするコードを示します。64ビット整数型とほとんど同じ方法で処理しますが、使用する命令が整数型用から浮動小数点型へ変わります。実際のコードはマクロで書かれていますが、ここでは64ビット浮動小数点型の最小値を求めるように展開されたコードを示します。
; - reduce min/max, double - vextractf64x4 ymm1, zmm0, 1 ; d0, d1, d2, d3 | d4, d5, d6, d7 MINST k1, ymm1, ymm0 vmovapd ymm0{k1}, ymm1 vextractf64x2 xmm1, ymm0, 1 vcmpltpd k2, xmm1, xmm0 vmovapd xmm0{k2}, xmm1 vpermilpd xmm1, xmm0, 1 ; 1 = _MM_SHUFFLE(0, 0, 0, 1) vcmpltpd k3, xmm1, xmm0 vmovapd xmm0{k3}, xmm1 vmovapd xmm3, xmm0 ; save it ; - reduce index, integer - vmovdqa64 zmm0, zmm5 ; load index, restore from zmm5 vextracti64x4 ymm1, zmm0, 1 ; d0, d1, d2, d3 | d4, d5, d6, d7 vmovdqa64 ymm0{k1}, ymm1 vextracti64x2 xmm1, ymm0, 1 vmovdqa64 xmm0{k2}, xmm1 vpsrldq xmm1, xmm0, 8 vmovdqa64 xmm0{k3}, xmm1 vmovd dword ptr [r8], xmm0 ; return index
最初に説明した[64ビット整数型配列]のreduceとほとんど同じです。扱うデータが64ビット整数から、64ビット浮動小数点へ変わりますが、畳み込む要素数は同じです。データ型が異なるため、型に合った命令に変更するだけで、使用する命令もほとんど同じです。
32ビット浮動小数点をreduce
以降に、32ビット浮動小数点をreduceするコードを示します。実際のコードはマクロで書かれていますが、ここでは32ビット浮動小数点の最小値を求めるように展開されたコードを示します。まず、値のreduceのソースコードを示します。
vextractf32x8 ymm1, zmm0, 1 ; d0, d1, ... d7 | d8, d9, ... d15 vcmpltps k1, ymm1, ymm0 vmovaps ymm0{k1}, ymm1 vextractf128 xmm1, ymm0, 1 vcmpltps k2, xmm1, xmm0 vmovaps xmm0{k2}, xmm1 vpermilps xmm1, xmm0, 14 ; 14 = _MM_SHUFFLE(0, 0, 3, 2) vcmpltps k3, xmm1, xmm0 vmovaps xmm0{k3}, xmm1 vpermilps xmm1, xmm0, 1 ; 1 = _MM_SHUFFLE(0, 0, 0, 1) vcmpltps k4, xmm1, xmm0 vmovaps xmm0{k4}, xmm1 vmovaps xmm3, xmm0 ; save it
[32ビット整数をreduce]とほとんど同じです。扱うデータが32ビット整数から、32ビット浮動小数点へ変わりますが、畳み込む要素数は同じです。データ型が異なるため、型に合った命令に変更するだけで、使用する命令もほとんど同じです。
zmm0レジスターに格納されている16個の32ビット浮動小数点から最小値を取り出します。基本的にレジスターを半分に畳みながら、順次結果を得る方法を採用します。
zmm0レジスターに格納されている上位8要素をymm1レジスターへ抽出します。下位8要素はzmm0レジスターの下位であるymm0レジスターへ残されます。この二つのレジスターをvcmpltps命令へ与え小さな値を持つ要素に対応したビットがセットされ、k1マスクレジスターへ設定されます。
次に、このマスクレジスターをvmovaps命令に与え、ymm1レジスターの要素がymm0レジスターの要素より小さければ、ymm1レジスターからymm0レジスターへ移動します。これによって、16個の要素が8個の要素へreduceされます。このマスクレジスターは、インデックスを求めるときも利用しますので壊さないようにします。以降も、要素数が変わるだけで同様にreduceします。
今度は8要素を4要素にreduceします。
ymm0レジスターに格納されている上位4要素をxmm1レジスターへ抽出します。
次に、4要素を2要素にreduceします。
xmm0レジスターに格納されている下位2要素をxmm1レジスターの最下位要素へ抽出します。
今度は2要素を1要素にreduceします。
これでxmm1レジスターの最下位要素へ目的の値が抽出されます。処理中に作成したマスクレジスターk1、k2、k3、k4はインデックスのreduceで使用しますので壊してはなりません。
値のreduceができましたので、インデックス値をreduceします。全要素の処理が完了すると、zmm0レジスターに格納された要素に対応するインデックス値が、zmm5レジスターへ格納されます。zmm5レジスターの16要素をreduce処理し、最終のインデックス値を求めます。インデックス値に相関や規則性はないため、先の処理で作成したマスクレジスターk1、k2、k3、k4を利用します。以降に、reduceのコードを示します。
vmovdqa32 zmm0, zmm5 ; load index, restore from zmm5 vextracti32x8 ymm1, zmm0, 1 ; d0, d1, ... d7 | d8, d9, ... d15 vmovdqa32 ymm0{k1}, ymm1 vextracti128 xmm1, ymm0, 1 vmovdqa32 xmm0{k2}, xmm1 pshufd xmm1, xmm0, 14 ; 14 = _MM_SHUFFLE(0, 0, 3, 2), 0x0E = 14 vmovdqa32 xmm0{k3}, xmm1 pshufd xmm1, xmm0, 1 ; 1 = _MM_SHUFFLE(0, 0, 0, 1), 0x01 = 1 vmovdqa32 xmm0{k4}, xmm1 vmovd dword ptr [r8], xmm0 ; return index vmovaps xmm0, xmm3 ; xmm0 = return value
上記のコードを図で示します。
既に解説した方法なので説明は省きます。