2011-08-26 2 views
6

私はx86-64アセンブリを試しています。私は、関数の集合の先頭に次のように見つけて驚いたgcc -O0 -ggcc引数レジスタがx86-64上に流出する

long myfunc(long a, long b, long c, long d, 
      long e, long f, long g, long h) 
{ 
    long xx = a * b * c * d * e * f * g * h; 
    long yy = a + b + c + d + e + f + g + h; 
    long zz = utilfunc(xx, yy, xx % yy); 
    return zz + 20; 
} 

:このダミー関数をコンパイルした

0000000000400520 <myfunc>: 
    400520:  55      push rbp 
    400521:  48 89 e5    mov rbp,rsp 
    400524:  48 83 ec 50    sub rsp,0x50 
    400528:  48 89 7d d8    mov QWORD PTR [rbp-0x28],rdi 
    40052c:  48 89 75 d0    mov QWORD PTR [rbp-0x30],rsi 
    400530:  48 89 55 c8    mov QWORD PTR [rbp-0x38],rdx 
    400534:  48 89 4d c0    mov QWORD PTR [rbp-0x40],rcx 
    400538:  4c 89 45 b8    mov QWORD PTR [rbp-0x48],r8 
    40053c:  4c 89 4d b0    mov QWORD PTR [rbp-0x50],r9 
    400540:  48 8b 45 d8    mov rax,QWORD PTR [rbp-0x28] 
    400544:  48 0f af 45 d0   imul rax,QWORD PTR [rbp-0x30] 
    400549:  48 0f af 45 c8   imul rax,QWORD PTR [rbp-0x38] 
    40054e:  48 0f af 45 c0   imul rax,QWORD PTR [rbp-0x40] 
    400553:  48 0f af 45 b8   imul rax,QWORD PTR [rbp-0x48] 
    400558:  48 0f af 45 b0   imul rax,QWORD PTR [rbp-0x50] 
    40055d:  48 0f af 45 10   imul rax,QWORD PTR [rbp+0x10] 
    400562:  48 0f af 45 18   imul rax,QWORD PTR [rbp+0x18] 

gccは非常に不思議なすべての引数がスタックに登録して、取りこぼしますさらなる操作のためにそれらをメモリから削除する。

これは-O0で発生します(問題がない場合は-O1)が、それでもなぜですか?これは私にとって反最適化のようです - なぜgccはそれをしますか?

+6

あなたはそれを後方に持っているかもしれないと思います。私は、GCCが常に(最初は)コードを生成する方法であると確信しています。それは、通常は最適化されているので、通常は表示されません(ただし、最適化が有効な場合のみです)。 – user786653

+0

これは反最適化ではなく、最適化されていないだけです。 – hirschhornsalz

+0

私はちょうどこの例をどこかで見た:http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/ :-) –

答えて

7

私は決してGCC内部の専門家ではありませんが、私はそれを撃つでしょう。残念ながら、GCCのレジスタ割り当てや情報漏洩に関する情報のほとんどは古くなっているようです(local-alloc.cのような参照ファイルはもう存在しません)。

私はgcc-4.5-20110825のソースコードを見ています。

GNU C Compiler Internalsにおいては、expand_function_startの初期機能コードはgcc/function.cであることが記載されている。そこ私たちは、パラメータを処理するため、次を見つける:

4462 /* Initialize rtx for parameters and local variables. 
4463  In some cases this requires emitting insns. */ 
4464 assign_parms (subr); 

assign_parmsでは、各引数が格納されている場所処理するコードは次のとおりです。

3207  if (assign_parm_setup_block_p (&data)) 
3208   assign_parm_setup_block (&all, parm, &data); 
3209  else if (data.passed_pointer || use_register_for_decl (parm)) 
    assign_parm_setup_reg (&all, parm, &data); 
3211  else 
3212   assign_parm_setup_stack (&all, parm, &data); 

は、集計データ型を処理し、この場合にはその限りではありませんGCCがポインタuse_register_for_declをチェックするために渡されないためです。

ここで関連する部分は次のとおりです。変数はregisterキーワードで宣言されたかどうか

1972 if (optimize) 
1973  return true; 
1974 
1975 if (!DECL_REGISTER (decl)) 
1976  return false; 

DECL_REGISTERテスト。そして今我々は答えます:ほとんどのパラメータは、最適化が有効になっていないときにスタックに入り、assign_parm_setup_stackによって処理されます。値を溢れさせる前にソースコードを通過したルートは、ポインタ引数の方がやや複雑ですが、好奇心が強い場合は同じファイルでトレースすることができます。

GCCがすべての引数と最適化を無効にしたローカル変数を無効にするのはなぜですか?デバッグを支援する。これは私のマシン上で、次の生成

1 extern int bar(int); 
2 int foo(int a) { 
3   int b = bar(a | 1); 
4   b += 42; 
5   return b; 
6 } 

gcc -O1 -cでコンパイル:この単純な関数を考えてみましょう

あなたは5行目でブレークの値を印刷しようとした場合を除いて結構です
0: 48 83 ec 08    sub $0x8,%rsp 
4: 83 cf 01    or  $0x1,%edi 
7: e8 00 00 00 00   callq c <foo+0xc> 
c: 83 c0 2a    add $0x2a,%eax 
f: 48 83 c4 08    add $0x8,%rsp 
13: c3      retq 

、あなたそれはbarへの呼び出しの後に使用していないですので、引数が上書きされたよう

(gdb) print a 
$1 = <value optimized out> 

を取得します。

6

理由のカップル:一般的な場合に

  1. 、関数の引数は、それが格納されるか、またはそのアドレスが関数内で撮影したため、ローカル変数のように扱われなければなりません。したがって、引数ごとにスタックスロットを割り当てるだけで簡単です。
  2. スタック位置でデバッグ情報を表示する方がはるかに簡単になります。引数の値は、レジスタとメモリの間を移動するのではなく、常に特定の場所にあります。

一般的に-O0コードを見ているときは、コンパイラの最優先事項ができるだけコンパイル時間を短縮し、高品質のデバッグ情報を生成していると考えてください。

+1

はい、最適化なしでは、コンパイラ具体的には、すべてのラインを独立させ、常に実際の変数からリロードして直ちに格納することで、CPUを別の行に移動したり、デバッガ内の変数の値を変更したり、正しく動作させることができます。 – doug65536

関連する問題