C言語コンパイラ依存した話の続編「Visual Studio 2019 はどうコンパイルするのか」
最早、毒食らわば皿までといった心境です。
再び、この記事の続き。
前回は clang コンパイラについて解析しました。
ならば、 Visual Studio 2019 はどうなのか。
そういうことが気になる(笑)。
今回は、その Visual Studio 2019 のお話です。
※以下、VS = Visual Studio 2019 の意。
VS が出力したアセンブラ
以下が、VS が出力したアセンブラです。
Intel Pentium プロセッサのアセンブラになります。
コメントは // で示しました。
アセンブラに [i] とはなんぞや?
という疑問はありますが、とにかく VS がそう出力したのですから仕方ありません。
わかりやすくはありますが。
// int i = 3, j = 5;
00845E15 mov dword ptr [i],3
00845E1C mov dword ptr [j],5
// printf("MAX処理前 i = %d, j = %d\n", i, j);
// j をスタックにプッシュ (printf の第3引数)
00845E23 mov eax,dword ptr [j]
00845E26 push eax
// i をスタックにプッシュ (printf の第2引数)
00845E27 mov ecx,dword ptr [i]
00845E2A push ecx
// 文字列の先頭アドレスをスタックにプッシュ (printf の第1引数)
00845E2B push offset string "MAX\x8f\x88\x97\x9d\x91O i = %d, j = %d\n" (0849BDCh)
// printf をコール
00845E30 call _printf (084141Fh)
// スタックポインタを戻す
00845E35 add esp,0Ch
// printf("MAX処理中 i = %d, j = %d, max = %d\n", i, j, MAX(++i,j--));
// ++i
// もうここでいきなり i++ している。
// なので、「++」する前の「i」はもうない。
00845E38 mov eax,dword ptr [i]
00845E3B add eax,1
00845E3E mov dword ptr [i],eax
// 三項演算するために「i」を待避(「++」 した後)
00845E41 mov ecx,dword ptr [i]
00845E44 mov dword ptr [ebp-0DCh],ecx // ++i
// 三項演算するために「j」を待避(「--」する前)
00845E4A mov edx,dword ptr [j]
00845E4D mov dword ptr [ebp-0E0h],edx // j
// j--
00845E53 mov eax,dword ptr [j]
00845E56 sub eax,1
00845E59 mov dword ptr [j],eax
// ++i と j を比較
00845E5C mov ecx,dword ptr [ebp-0DCh]
00845E62 cmp ecx,dword ptr [ebp-0E0h]
00845E68 jle __$EncStackInitStart+82h (0845E7Eh)
// [ebp-0E4h]は三項演算の結果
// i はインクリメントしてから決定
// ++i
00845E6A mov edx,dword ptr [i]
00845E6D add edx,1
00845E70 mov dword ptr [i],edx
// [ebp-0E4h] = ++i
00845E73 mov eax,dword ptr [i]
00845E76 mov dword ptr [ebp-0E4h],eax
00845E7C jmp __$EncStackInitStart+94h (0845E90h)
// j はデクリメントする前に決定
// [ebp-0E4h] = j
00845E7E mov ecx,dword ptr [j]
00845E81 mov dword ptr [ebp-0E4h],ecx
// j--
00845E87 mov edx,dword ptr [j]
00845E8A sub edx,1
00845E8D mov dword ptr [j],edx
// printf の第4引数(MAX の結果)
00845E90 mov eax,dword ptr [ebp-0E4h]
00845E96 push eax
// printf の第3引数(i)
// MAX 演算後の値
00845E97 mov ecx,dword ptr [j]
00845E9A push ecx
// printf の第2引数(j)
// MAX 演算後の値
00845E9B mov edx,dword ptr [i]
00845E9E push edx
// printf の第1引数("MAX処理中 i = %d, j = %d, max = %d\n")
00845E9F push offset string "MAX\x8f\x88\x97\x9d\x92\x86 i = %d, j = %d, max =@"... (0849CE0h)
// printf をコール
00845EA4 call _printf (084141Fh)
// スタックポインタを戻す
00845EA9 add esp,10h
// printf("MAX処理後 i = %d, j = %d\n", i, j);
// (解説省略)
00845EAC mov eax,dword ptr [j]
00845EAF push eax
00845EB0 mov ecx,dword ptr [i]
00845EB3 push ecx
00845EB4 push offset string "MAX\x8f\x88\x97\x9d\x8c\xe3 i = %d, j = %d\n" (0849E30h)
00845EB9 call _printf (084141Fh)
clang と VS
clang は、
MAX を評価する前の i と j を待避し、
printf の引数には待避したMAX を評価する前の i と j を渡す
でしたが、 VS では
MAX を演算する前の i と j を待避せず、
printf の引数には MAX を評価した後の i と j を渡す
となっています。
実はここでふと気になりました。
三項演算が絡んでいるから?
三項演算が絡まない、シンプルなインクリメントやデクリメントだったらどうなんだろう。
・・・
そう。
またテストしてみたのでした(私ってしつこい(笑))。
それは、また、次回(笑)。
いや、というか、 gcc が出力したアセンブラもあるんだけどなぁ・・・ふん、次回は gcc で(しつこい(~_~;))