2016-12-14 4 views
2

なぜGCCコンパイラがdoubleを使用するときにこの3行を追加するのか、そしてIntがあるときにGCCコンパイラがこの3行を追加するのはなぜですか?Int対アセンブリの倍数

#include <cstdio> 

int main(){ 
    int i = 1; 
} 

==>

main: 
     push ebp 
     mov  ebp, esp 
     sub  esp, 16 
     mov  DWORD PTR [ebp-4], 1 
     mov  eax, 0 
     leave 
     ret 

ダブル付::

#include <cstdio> 

int main(){ 
    double i = 1; 
} 

==>int型で


main: 
     lea  ecx, [esp+4]   // This three lines 
     and  esp, -8     // ... 
     push DWORD PTR [ecx-4]  // ... 
     push ebp 
     mov  ebp, esp 
     push ecx 
     sub  esp, 20 
     fld1 
     fstp QWORD PTR [ebp-16] 
     mov  eax, 0 
     add  esp, 20 
     pop  ecx 
     pop  ebp 
     lea  esp, [ecx-4] 
     ret 

たとえば、int * s = new int(4)などのポインタを使用すると同様のことが起こります。

これがなぜ起こるのか、なぜそうでないのか説明できますか?

+2

'-O3'スイッチを使用しないと、コンパイラは冗長でもナンセンスでもコードを生成します。(まだ正しいですが) –

+0

はい、わかります。私はちょうど学んでいる:) –

+0

これはCまたはC + +ですか?それらは異なる言語であり、関数の入力/終了のために異なるコードが生成される可能性があります。 – Olaf

答えて

7

自動スコープ(スタック上)にあるdoubleの場合、余分なコードはスタックフレームを偶数の8バイト境界で整列させるため、double変数は適切なアライメントのメモリアドレスに格納されます。スタックポインタは、関数を入力するときに、8バイト境界でも揃えられることは保証されていません。また、コンパイラは余分なコードを追加してそのようにします。

これは、and esp, -8の機能です。 -8は0xFFFFFFF8です。 andを使用してespの最後の3ビットをクリアし、偶数の8バイト境界メモリアドレスを指し、それを調整します(スタックは高いメモリアドレスから低いメモリアドレスに向かって増加します)。

+0

もちろん、これがコードの機能です。しかし、なぜそれが 'double'ではなく' int'でないのでしょうか? (重大な質問ではなく、トローリングではない:私は長い間にコード生成の問題を見ていない...) –

+2

x86では、 'double'は64ビット型で、8バイトの整列が必要ですが、ABIではスタックが8バイトに整列していることを確認します。同じことが 'long long'でも起こります。なぜなら、これも8バイトのタイプだからです。 'int'には必ずしも必要ではありません。なぜなら、それはちょうど4バイトの型であり、スタックポインタは4バイト境界であることが保証されているからです。 –