More Related Content
Similar to ネイティブコードを語る (20)
More from Kenji Imasaki (6)
ネイティブコードを語る
- 10. 問題:NULLチェックはありますか。
Sample!Road::GetCategory:
mov eax,dword ptr [esp+4]
mov ecx,dword ptr [ecx+0ACh]
mov dword ptr [eax],ecx
mov al,1
ret 4
Sample!Road::GetCategory:
mov edx,dword ptr [esp+4]
test edx,edx
jne Sample!Road::GetCategory+0xd
Sample!Road::GetCategory+0x8:
xor al,al
ret 4
Sample!Road::GetCategory+0xd:
mov eax,dword ptr [ecx+0ACh]
mov dword ptr [edx],eax
mov al,1
ret 4
BEFORE
AFTER
- 11. 問題:NULLチェックはありますか。
Sample!Road::GetCategory:
mov edx,dword ptr [esp+4]
test edx,edx
jne Sample!Road::GetCategory+0xd
Sample!Road::GetCategory+0x8:
xor al,al
ret 4
Sample!Road::GetCategory+0xd:
mov eax,dword ptr [ecx+0ACh]
mov dword ptr [edx],eax
mov al,1
ret 4
if (!x)
{
...
}
if (!x)
{
return 0;
}
f(T arg1)
{
if (!arg1)
{
return 0;
}
}
NULLチェック、あります。
- 14. 問題:NULLチェックはありますか。
Sample!Road::GetCategory:
mov edx,dword ptr [esp+4]
test edx,edx
jne Sample!Road::GetCategory+0xd
Sample!Road::GetCategory+0x8:
xor al,al
ret 4
Sample!Road::GetCategory+0xd:
mov eax,dword ptr [ecx+0ACh]
mov dword ptr [edx],eax
mov al,1
ret 4
ポインタ[ESP+4]が指すDWORD値をEDXに転送。
EDXとEDXをANDし、ゼロならば ZF=1 をセット。
ZF!=0 ならば分岐。
AL (EAXの下位8bit) をゼロクリア。
return;
ポインタ[ECX+AC]が指すDWORD値をEAXに転送。
EAXの値をポインタ[EDX]が指すアドレスに転送。
return;
AL (EAXの下位8bit)に1をセット。
(マニア向け)
第1引数の取得
第1引数==0?
if (第1引数)
返り値 = 0;
戻る
メンバ変数の参照
*第1引数 = メンバ変数
返り値 = 1;
戻る
- 17. 問題:それは正しいメンバ変数ですか。
mov edx,dword ptr [esp+4]
test edx,edx
jne Sample!Road::GetCategory+0xd
xor al,al
ret 4
mov eax,dword ptr [ecx+0ACh]
mov dword ptr [edx],eax
mov al,1
ret 4
mov edx,dword ptr [esp+4]
test edx,edx
jne Sample!Road::GetCategory+0xd
xor al,al
ret 4
mov eax,dword ptr [ecx+0B0h]
mov dword ptr [edx],eax
mov al,1
ret 4
BEFORE AFTER
ポインタ[ECX+B0]
が指すDWORD値を
EAXに転送。
ECX = this
Roadクラスのメンバ変数レイアウト
0:000> dt Sample!Road
+0x000 __VFN_table : Ptr32
+0x004 isEnabled_ : Bool
+0x008 pointDepot_ : [18] Point
+0x098 numVerifiedPoints_ : Uint4B
+0x09c numPoints_ : Uint4B
+0x0a0 myDraftPoint_ : Point
+0x0a8 myBirthday_ : Uint4B
+0x0ac myCategory_ : Uint4B
+0x0b0 mySmoothingCategory_ : Uint4B
+0x0b4 myJunctions_ : [32] Uint4B
+0x134 mySystemClock_ : Ptr32 SystemClock
- 18. 問題:それは正しいメンバ変数ですか。
C++のメソッドでは、呼び出し規約
thiscall により、ECXレジスタに
は this ポインタが格納されている。
[ECX+オフセット] というオペラン
ドはメンバ変数を参照している。
結論:メンバ変数のオフセットを確
認すればよい。
デバッガでクラスのレイアウトをダ
ンプする。
0:000> dt Sample!Road
(......)
+0x0a8 myBirthday_ : Uint4B
+0x0ac myCategory_ : Uint4B
+0x0b0 mySmoothingCategory_ : Uint4B
+0x0b4 myJunctions_ : [32] Uint4B
+0x134 mySystemClock_ : Ptr32 SystemClock
mov edx,dword ptr [esp+4]
test edx,edx
jne Sample!Road::GetCategory+0xd
xor al,al
ret 4
mov eax,dword ptr [ecx+0B0h]
mov dword ptr [edx],eax
mov al,1
ret 4
- 19. ここまでのまとめ
if 文が増えているかどうか。
バイナリを見て、 test (比較) jne (分岐)のような構造があるかどう
かをチェックする。
正しいメンバ変数を参照しているかどうか。
バイナリを見て、thisポインタからのオフセットが正しいかどうかをチェッ
クする。
呼び出し規約…?
- 21. int Function1()
{
Function2(a, b, c, d);
}
int Function2(int a, int b, int c, int d)
{
Function3(e, f, g, h)
}
int Function3(int a, int b, int c, int d)
{
......
}
関数呼び出し
とスタックメモリ
ESP
引数4
引数2
引数1
戻りアドレス
元のEBP
引数3
引数2
引数1
戻りアドレス
元のEBP
ローカル変数
/ 作業用領域
引数4
引数3
EBP
Function1
の担当分
Function2
の担当分
Function3
の担当分
ローカル変数
/ 作業用領域
実行中
- 23. DEMO: 関数呼び出し規約
実行時のスタックメモリを観察してみよう
0:000> dd /c1 @esp
00d8fb44 44444407
00d8fb48 33333305
00d8fb4c 22222207
00d8fb50 11111105
00d8fb54 00d8fb7c
00d8fb58 010e2175
00d8fb5c 11110002
00d8fb60 22220003
00d8fb64 33330002
00d8fb68 44440003
00d8fb6c 11110002
00d8fb70 22220003
00d8fb74 33330002
00d8fb78 44440003
00d8fb7c 00d8fbac
00d8fb80 010e23ea
00d8fb84 11000001
00d8fb88 22000001
cdecl
int __cdecl Function1(int a, int b, int c, int d)
{
(......)
t = Function2(e, f, g, h);
(......)
}
int __cdecl Function2(int a, int b, int c, int d)
{
(......)
}
- 24. 関数呼び出し規約
cdecl と stdcall
スタック清掃係が移動している理由?
cdecl stdcall
対応する 対応しない可変長引数
呼び出し側 呼び出され側スタック清掃係
スタック(左→右) スタック(左→右)引数
C標準 Windows API用途
- 25. cdecl と stdcall
stack!SumCdecl:
00401130 8b442404 mov eax,dword ptr [esp+4]
00401134 03442408 add eax,dword ptr [esp+8]
00401138 0344240c add eax,dword ptr [esp+0Ch]
0040113c c3 ret
stack!TestCallerCdecl:
00401180 56 push esi
00401181 57 push edi
00401182 e864020000 call stack!rand (004013eb)
00401187 8bf8 mov edi,eax
00401189 e85d020000 call stack!rand (004013eb)
0040118e 8bf0 mov esi,eax
00401190 e856020000 call stack!rand (004013eb)
00401195 50 push eax
00401196 56 push esi
00401197 57 push edi
00401198 e86dfeffff call SumCdecl
0040119d 83c40c add esp,0Ch
004011a0 5f pop edi
004011a1 5e pop esi
004011a2 c3 ret
stack!SumStdcall:
00401140 8b442404 mov eax,dword ptr [esp+4]
00401144 03442408 add eax,dword ptr [esp+8]
00401148 0344240c add eax,dword ptr [esp+0Ch]
0040114c c20c00 ret 0Ch
stack!TestCallerStdcall:
00401160 56 push esi
00401161 57 push edi
00401162 e884020000 call stack!rand (004013eb)
00401167 8bf8 mov edi,eax
00401169 e87d020000 call stack!rand (004013eb)
0040116e 8bf0 mov esi,eax
00401170 e876020000 call stack!rand (004013eb)
00401175 50 push eax
00401176 56 push esi
00401177 57 push edi
00401178 e8bafeffff call SumStdcall
0040117d 5f pop edi
0040117e 5e pop esi
0040117f c3 ret
清掃作業
+3バイト
清掃不要
清掃作業
+2バイト
清掃不要
cdecl stdcall
- 28. stdcallとfastcall
stdcall
stack!SumFastcall:
00401150 8d0411 lea eax,[ecx+edx]
00401153 03442404 add eax,dword ptr [esp+4]
00401157 c20400 ret 4
stack!TestCallerFastcall:
004011b0 56 push esi
004011b1 57 push edi
004011b2 e834020000 call stack!rand (004013eb)
004011b7 8bf8 mov edi,eax
004011b9 e82d020000 call stack!rand (004013eb)
004011be 8bf0 mov esi,eax
004011c0 e826020000 call stack!rand (004013eb)
004011c5 50 push eax
004011c6 8bd6 mov edx,esi
004011c8 8bcf mov ecx,edi
004011ca e881feffff call SumFastCall
004011cf 5f pop edi
004011d0 5e pop esi
004011d1 c3 ret
清掃不要
fastcall
stack!SumStdcall:
00401140 8b442404 mov eax,dword ptr [esp+4]
00401144 03442408 add eax,dword ptr [esp+8]
00401148 0344240c add eax,dword ptr [esp+0Ch]
0040114c c20c00 ret 0Ch
stack!TestCallerStdcall:
00401160 56 push esi
00401161 57 push edi
00401162 e884020000 call stack!rand (004013eb)
00401167 8bf8 mov edi,eax
00401169 e87d020000 call stack!rand (004013eb)
0040116e 8bf0 mov esi,eax
00401170 e876020000 call stack!rand (004013eb)
00401175 50 push eax
00401176 56 push esi
00401177 57 push edi
00401178 e8bafeffff call SumStdcall
0040117d 5f pop edi
0040117e 5e pop esi
0040117f c3 ret
清掃不要
清掃作業
+2バイト
清掃作業
+2バイト
最初の2引数はレジスタ渡し
第3引数以降はスタック渡しLEA (Load Effective Address) :
オペランドを計算してレジスタに代入
する。メモリアクセスはしない。
引数をスタック
に積む 引数をレジスタ
に入れる
- 30. デバッグビルドとリリースビルド
詳細は MSDN: Compiler Options
https://msdn.microsoft.com/en-us/library/9s7c9wdw.aspx
オプション 意味 用途
/Od デバッグビルド
/O1
/Og /Os /Oy /Ob2
/Gs /GF /Gy
サイズ優先
/O2
/Og /Oi /Ot /Oy /Ob2
/Gs /GF /Gy
リリースビルド
/Ox /Og /Oi /Ot /Oy /Ob2
リリースビルド
(プレゼン用?)
- 32. デバッグビルドとリリースビルド
式の最適化
call Copy!rand (01042340)
mov dword ptr [ebp-4],eax
call Copy!rand (01042340)
mov dword ptr [ebp-8],eax
mov eax,dword ptr [ebp-4]
add eax,dword ptr [ebp-8]
mov dword ptr [ebp-0Ch],eax
mov ecx,dword ptr [ebp-4]
add ecx,dword ptr [ebp-8]
mov dword ptr [ebp-10h],ecx
mov edx,dword ptr [ebp-4]
add edx,dword ptr [ebp-8]
mov dword ptr [ebp-14h],edx
mov eax,dword ptr [ebp-0Ch]
add eax,dword ptr [ebp-10h]
add eax,dword ptr [ebp-14h]
x = a + b;
y = a + b;
z = a + b;
t = x + y + z;
a = rand();
b = rand();
そのまんま
デバッグビルド
- 34. デバッグビルドとリリースビルド
ループの最適化:デバッグビルド
// [ebp-8] = a
// [ebp-C] = b
// [ebp-4] = i
Copy!OptimizeLoop+0x2d:
cmp dword ptr [ebp-4],64h
jge Copy!OptimizeLoop+0x41 (010ac091)
Copy!OptimizeLoop+0x33:
mov edx,dword ptr [ebp-8]
add edx,dword ptr [ebp-0Ch]
add edx,dword ptr [ebp-4]
mov dword ptr [ebp-4],edx
jmp Copy!OptimizeLoop+0x2d (010ac07d)
Copy!OptimizeLoop+0x41:
mov eax,dword ptr [ebp-4]
t = a + b;
while (i < 100)
{
i += t;
return i;
そのまんま
}
デバッグビルド
- 35. デバッグビルドとリリースビルド
ループの最適化:リリースビルド
(a + b) をループの前に計算し、レジスタに入れておく。
i を EAXに割り当てることによって、そのまま return できる。
// ecx = a + b;
// eax = i;
Optimize!OptimizeLoop+0x20:
add eax,ecx
cmp eax,64h
jl Optimize!OptimizeLoop+0x20 (004025e0)
i += a + b;
while (i < 100)
賢い!リリースビルド
- 37. デバッグビルド
配列アクセス
// ebp-4 = i
// ebp+8 = array
// ebp+C = size
Optimize!ClearArrayByArray+0xd:
mov eax,dword ptr [ebp-4]
add eax,1
mov dword ptr [ebp-4],eax
Optimize!ClearArrayByArray+0x16:
mov ecx,dword ptr [ebp-4]
cmp ecx,dword ptr [ebp+0Ch]
jge Optimize!ClearArrayByArray+0x2d
Optimize!ClearArrayByArray+0x1e:
mov edx,dword ptr [ebp-4]
mov eax,dword ptr [ebp+8]
mov dword ptr [eax+edx*4],0
jmp Optimize!ClearArrayByArray+0xd
i ++;
if (i >= size) return;
*(array + i*4) = 0;
- 38. デバッグビルド
ポインタアクセス
// ebp-4 = p
// ebp+8 = array
// ebp+C = size
Optimize!ClearArrayByPointer+0x14:
mov edx,dword ptr [ebp-4]
add edx,4
mov dword ptr [ebp-4],edx
Optimize!ClearArrayByPointer+0x1d:
mov eax,dword ptr [ebp+0Ch]
mov ecx,dword ptr [ebp+8]
lea edx,[ecx+eax*4]
cmp dword ptr [ebp-4],edx
jae Optimize!ClearArrayByPointer+0x36
Optimize!ClearArrayByPointer+0x2b:
mov eax,dword ptr [ebp-4]
mov dword ptr [eax],0
jmp Optimize!ClearArrayByPointer+0x14
p++;
if (p >= array+size*4)
return;
*p = 0;
- 39. リリースビルド
ポインタアクセス
ソースコードにはないカウン
タ i を出現させている。
なるべくレジスタを使うこと
によって、メモリアクセスを
せずに終了判定できる。
// edx = i
// eax = p
// edi = 0
// esi = size
Optimize!ClearArrayByPointer+0x24:
inc edx
mov dword ptr [eax],edi
lea eax,[eax+4]
cmp edx,esi
jb Optimize!ClearArrayByPointer+0x24
i++;
*p = 0;
p += 4;
if (i <= size)
賢い!
- 40. リリースビルド
配列アクセス
ループが除去されている。
// ecx = size
// esp+8 = array
Optimize!ClearArrayByArray+0x8:
push edi
mov edi,dword ptr [esp+8]
xor eax,eax
rep stos dword ptr es:[edi]
pop edi
EDI = array;
EAX = 0;
EDIが指すアドレスから ECX
個のデータをEAXで埋める。
もっと賢い!
- 43. switch文の実装1: Compare-Branch
switch (x)
{
case 0:
// 処理0
break;
case 1:
// 処理1
break;
case 2:
// 処理2
break;
}
cmp x, 0
je ラベル0
cmp x, 1
je ラベル1
cmp x, 2
je ラベル2
jmp 後続の処理
ラベル0:
// 処理0
jmp 後続の処理
ラベル1:
// 処理1
jmp 後続の処理
ラベル2:
// 処理2
後続の処理:
.......
if (x == 0)
{
// 処理0
}
else if (x == 1)
{
// 処理1
}
else if (x == 2)
{
// 処理2
}
- 45. オフセット データ(void*)
+0x0000 ラベル0
のアドレス
+0x0004 ラベル1
のアドレス
+0x0008 ラベル2
のアドレス
switch文の実装2: ジャンプテーブル
switch (x)
{
case 0:
// 処理0
break;
case 1:
// 処理1
break;
case 2:
// 処理2
break;
}
ジャンプテーブル TBL
mov ecx, x
jmp dword ptr TBL[ecx*4]
ラベル0:
// 処理0
jmp 後続の処理
ラベル1:
// 処理1
jmp 後続の処理
ラベル2:
// 処理2
後続の処理:
.......
- 46. オフセット データ(void*)
+0x0000 ラベル0
のアドレス
+0x0004 ラベル1
のアドレス
+0x0008 ラベル31
のアドレス
switch文の実装3:
ルックアップテーブル+ジャンプテーブル
switch (x)
{
case 0:
// 処理0
break;
case 1:
// 処理1
break;
case 31:
// 処理2
break;
}
ジャンプテーブル TBL
mov eax, x
movzx eax, byte ptr LUT[eax]
jmp dword ptr TBL[eax*4]
ラベル0:
// 処理0
jmp 後続の処理
ラベル1:
// 処理1
jmp 後続の処理
ラベル2:
// 処理2
後続の処理:
.......
Key Value
0 0
1 1
31 2
ルックアップテーブル LUT
movzx : ゼロ拡張つきmov
- 48. switch文の最適化
case 0-2
Compare – Branch.
Switch!DoSwitch3+0x13:
001b2393 83e800 sub eax,0
001b2396 7415 je Switch!DoSwitch3+0x2d (001b23ad)
Switch!DoSwitch3+0x18:
001b2398 48 dec eax
001b2399 740c je Switch!DoSwitch3+0x27 (001b23a7)
Switch!DoSwitch3+0x1b:
001b239b 48 dec eax
001b239c 7403 je Switch!DoSwitch3+0x21 (001b23a1)
- 49. switch文の最適化
case 0-3
ジャンプテーブル
Switch!DoSwitch4+0x13:
001b23d3 83f803 cmp eax,3
001b23d6 771f ja Switch!DoSwitch4+0x37 (001b23f7)
Switch!DoSwitch4+0x18:
001b23d8 ff2485fc231b00 jmp dword ptr Switch!DoSwitch4+0x3c (001b23fc)[eax*4]
0:000> dps 001b23fc L4
001b23fc 001b23df Switch!DoSwitch4+0x1f
001b2400 001b23e5 Switch!DoSwitch4+0x25
001b2404 001b23eb Switch!DoSwitch4+0x2b
001b2408 001b23f1 Switch!DoSwitch4+0x31
- 50. switch文の最適化 : case 0-4, 31
ルックアップテーブル + ジャンプテーブル
Switch!DoSwitch32+0x13:
001b2543 83f81f cmp eax,1Fh
001b2546 7732 ja Switch!DoSwitch32+0x4a (001b257a)
Switch!DoSwitch32+0x18:
001b2548 0fb6809c251b00 movzx eax,byte ptr Switch!DoSwitch32+0x6c (001b259c)[eax]
001b254f ff248580251b00 jmp dword ptr Switch!DoSwitch32+0x50 (001b2580)[eax*4]
0:000> db 001b259c L0n32
001b259c 00 01 02 03 04 06 06 06-06 06 06 06 06 06 06 06 ................
001b25ac 06 06 06 06 06 06 06 06-06 06 06 06 06 06 06 05 ................
0:000> dps 001b2580 L7
001b2580 001b2556 Switch!DoSwitch32+0x26
001b2584 001b255c Switch!DoSwitch32+0x2c
001b2588 001b2562 Switch!DoSwitch32+0x32
001b258c 001b2568 Switch!DoSwitch32+0x38
001b2590 001b256e Switch!DoSwitch32+0x3e
001b2594 001b2574 Switch!DoSwitch32+0x44
001b2598 001b257a Switch!DoSwitch32+0x4a
- 51. switch文の最適化
case 0-4,15-18,31-33, 0x400-0x404, 0x3333-0x3337
Case 0-33 ルックアップテーブル(34要素) + ジャンプテーブル(13要素)
Case 0x401-0x404 = ジャンプテーブル(4要素)
Case 0x3334-0x3337 = ジャンプテーブル(4要素)
0x400 は Compare – Branch.
//LUT
0:000> db 003826d4 L0n34
003826d4 00 01 02 03 04 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 05 ................
003826e4 06 07 08 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 09 ................
003826f4 0a 0b
- 54. memcpyの実装
デバッグビルド
CRT の memcpy を呼ぶだけ。
0015476E push 20h
00154770 mov eax,dword ptr [source]
00154773 push eax
00154774 mov ecx,dword ptr [dest]
00154777 push ecx
00154778 call _memcpy (0151285h)
0015477D add esp,0Ch
00154780 mov eax,dword ptr [dest]
void* DoCopy32(
_Out_ char* dest,
_In_ char* source)
{
memcpy(dest, source, 32);
......
}
- 56. memcpyの実装
リリースビルド
// ecx = dest
// edx = source
01081450 movdqu xmm0,xmmword ptr [edx]
01081456 movdqu xmmword ptr [ecx],xmm0
0108145A movdqu xmm0,xmmword ptr [edx+10h]
0108145F movdqu xmmword ptr [ecx+10h],xmm0
SSE : Streaming SIMD Extensions
memcpy(dest, source, 32);
movdqu: SSE用レジスタ(XMM)に対する読み書き
- 58. memcpyの実装
リリースビルド
SSE 命令とXMMレジスタ(128bit=16byte)を使うと4命令で済んでしまう。
// ecx = dest
// edx = source
01081450 movdqu xmm0,xmmword ptr [edx]
01081456 movdqu xmmword ptr [ecx],xmm0
0108145A movdqu xmm0,xmmword ptr [edx+10h]
0108145F movdqu xmmword ptr [ecx+10h],xmm0
SSE : Streaming SIMD Extensions
memcpy(dest, source, 32);
movdqu: SSE2用レジスタ(XMM)に対する読み書き
- 59. memcpyの実装
リリースビルド (AVX許可)
// ecx = dest
// edx = source
012E1440 vmovdqu ymm0,ymmword ptr [edx]
012E1444 vmovdqu ymmword ptr [ecx],ymm0
AVX : Advanced Vector Extensions
memcpy(dest, source, 32);
vmovdqu: AVX用レジスタ(YMM)に対する読み書き
- 61. memcpyの実装
リリースビルド (AVX許可)
AVX命令とYMMレジスタ(256bit=32byte) を使うと2命令で済んでしまう。
// ecx = dest
// edx = source
012E1440 vmovdqu ymm0,ymmword ptr [edx]
012E1444 vmovdqu ymmword ptr [ecx],ymm0
AVX : Advanced Vector Extensions
memcpy(dest, source, 32);
いささか邪道?
vmovdqu: AVX用レジスタ(YMM)に対する読み書き
- 64. ベクトル演算
デバッグビルド
長い上に1024回ループ
// ebp-4 = i
// ebp-8 = sum
// ebp+8 = A
// ebp+C = B
Optimize!VectorInt+0x17:
mov eax,dword ptr [ebp-4]
add eax,1
mov dword ptr [ebp-4],eax
Optimize!VectorInt+0x20:
cmp dword ptr [ebp-4],400h
jge Optimize!VectorInt+0x44 (002123e4)
Optimize!VectorInt+0x29:
mov ecx,dword ptr [ebp-4]
mov edx,dword ptr [ebp+8]
mov eax,dword ptr [ebp-4]
mov esi,dword ptr [ebp+0Ch]
mov ecx,dword ptr [edx+ecx*4]
imul ecx,dword ptr [esi+eax*4]
add ecx,dword ptr [ebp-8]
mov dword ptr [ebp-8],ecx
jmp Optimize!VectorInt+0x17 (002123b7)
Optimize!VectorInt+0x44:
mov eax,dword ptr [ebp-8]
pop esi
mov esp,ebp
pop ebp
ret
ループ
i++;
t = A[i] * B[i];
sum += t;
ECX = i; EDX = A;
EAX = i; ESI = B;
EAX = sum;
if (i < 1024 )
{
- 65. ベクトル演算
リリースビルド
SSEによる4要素ずつの処理を2つ束
ねる(ループ展開)。
8要素ずつ128回のループで済む。
ループの結果はXMM1,XMM2レジ
スタにパックされているので、
32bitにまとめる回収班が必要。
// edi = 0x80 ループカウンタ
// edx = B[0]のアドレス
// ecx = A[16]のアドレス
// esi = A[i]からB[i]までのオフセット
Optimize!VectorInt+0x40:
lea edx,[edx+20h]
lea ecx,[ecx+20h]
movdqu xmm0,xmmword ptr [edx-20h]
movdqu xmm1,xmmword ptr [ecx-30h]
pmulld xmm1,xmm0
paddd xmm3,xmm1
movdqu xmm0,xmmword ptr [esi+ecx-20h]
movdqu xmm1,xmmword ptr [ecx-20h]
pmulld xmm1,xmm0
paddd xmm2,xmm1
dec edi
jne Optimize!VectorInt+0x40 (00e72450)
Optimize!VectorInt+0x70:
paddd xmm2,xmm3
movdqa xmm0,xmm2
psrldq xmm0,8
paddd xmm2,xmm0
movdqa xmm0,xmm2
psrldq xmm0,4
paddd xmm2,xmm0
movd ebx,xmm2
mov dword ptr [esp+10h],ebx
ループ
回収班
- 67. SSEによるベクトル演算
ループ内側
pmulld (Multiply Packed
signed dword and store
Low Dword)
パックド乗算
(詰め込んだ4つのデータを
個別に乗算)
paddd (Packed Add)
パックド加算
(詰め込んだ4つのデータ
を個別に加算)
+ + + + + + + +
× × × × × × × ×
XMM3 XMM2
int B[1024]
int A[1024]
4要素 = 16byte = 128bit
1024要素 = 4Kbyte
4要素 = 16byte = 128bit
pmulld
paddd
- 68. SSEによるベクトル演算
回収班(1)
Sab: Sa + Sb
paddd (Packed Add)
パックド加算
詰め込んだ4つのデータを個別に加算
psrldq N
(Packed Shift Right Logical
Double Quad)
N バイト右シフト
上位ビットはゼロ埋め
S3 S2 S1 S0
A3
+
B3
A2
+
B2
A1
+
B1
A0
+
B0
XMM3
XMM2
XMM2
paddd
XMM0 S3 S2 S1 S0
S3 S2
psrldq 8
+ + + +
S31 S20XMM2
paddd
XMM0
- 70. ベクトル演算
リリースビルド(AVX許可)
AVXによる8要素ずつのループを2つ
束ねる(ループ展開)。
16要素ずつ64回のループで済む。
ループの結果はYMM1,YMM2レジス
タにパックされているので、32bit
にまとめる回収班が必要。
// ecx = 0x40 ループカウンタ
// edx = B[0]のアドレス
// eax = A[32]のアドレス
// esi = A[i]からB[i]までのオフセット
Optimize!VectorInt+0x20:
lea edx,[edx+40h]
lea eax,[eax+40h]
vmovdqu ymm0,ymmword ptr [eax-60h]
vpmulld ymm0,ymm0,ymmword ptr [edx-40h]
vpaddd ymm1,ymm0,ymm1
vmovdqu ymm0,ymmword ptr [esi+eax-40h]
vpmulld ymm0,ymm0,ymmword ptr [eax-40h]
vpaddd ymm2,ymm0,ymm2
dec ecx
jne Optimize!VectorInt+0x20 (00882460)
Optimize!VectorInt+0x48:
vpaddd ymm0,ymm2,ymm1
vphaddd ymm0,ymm0,ymm0
vphaddd ymm1,ymm0,ymm0
vextracti128 xmm0,ymm1,1
vpaddd xmm0,xmm1,xmm0
vmovd eax,xmm0
ループ
回収班
- 72. AVXによるベクトル演算
ループ内側
vpmulld (Multiply
Packed signed dword and
store Low Dword)
パックド乗算
(詰め込んだ8つのデータを
個別に乗算)
vpadd (Packed Add)
パックド加算
(詰め込んだ8つのデータ
を個別に加算)
+ + + + + + + + + + + + + + + +
× × × × × × × × × × × × × × × ×
YMM1 YMM2
int B[1024]
int A[1024]
8要素 = 32byte = 256bit
1024要素 = 4Kbyte
8要素 = 32byte = 256bit 8要素 = 32byte = 256bit
vpmulld
vpadd
- 73. AVXによる
ベクトル演算
回収班(1)
vpadd (Packed Add)
パックド加算
(詰め込んだ8つのデータ
を個別に加算)
vphadd (Packed Horizontal Add)
パックド水平加算
(隣接するデータを加算)
Sab: Sa + Sb
S7 S6 S5 S4 S3 S2 S1 S0
A7
+
B7
A6
+
B6
A5
+
B5
A4
+
B4
A3
+
B3
A2
+
B2
A1
+
B1
A0
+
B0
YMM1
YMM2
YMM0
S76 S54 S76 S54
+ + + +
S32 S10 S32 S10
+
S76
54
S76
54
S76
54
S76
54
S32
10
S32
10
S32
10
S32
10
+ + +
YMM0
YMM1
vpaddd
vphaddd
vphaddd
- 76. まとめ
バグ修正の存在確認
バイナリを見ると楽しいですね。
関数呼び出し規約
1バイト・1サイクルを削る情熱を感じました。
デバッグビルドとリリースビルド
最近は簡潔なソースコードの方が速い場合があるようです。
switch文の最適化
最低限のコーダーになれた気がします。
自動ベクトル化
複雑な命令で高速実行するワクワク感があります。