見出し画像

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 で(しつこい(~_~;))

この記事が気に入ったらサポートをしてみませんか?