2011-08-26 24 views
6

x86-64 montajıyla deneme yapıyorum. Ben işlevin montaj başında aşağıdaki bulmak için sürpriz oldu gcc -O0 -g ilegcc argümanı kaydı x86-64 üzerinde dökülüyor

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; 
} 

: Bu kukla işlevi derlenmiş olması

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 çok garip tüm argüman yığını üzerine kaydeder ve daha sonra alır dökülen Daha fazla operasyon için onları hafızadan.

Bu yalnızca -O0'da (-O1 ile sorun yok) olur, ancak yine de neden? Bu bana bir anti-optimizasyon gibi görünüyor - neden gcc bunu yapar?

+6

Geriye doğru olabileceğini düşünüyorum. GCC'nin her zaman (başlangıçta) kodu nasıl ürettiğine göre, eminim ki, normalde onu basitçe optimize edilmiş olarak görmüyorsunuz (ancak tabi ki sadece optimizasyonlar etkinse). – user786653

+0

Bu, anti optimizasyon değil, yalnızca optimizasyon değil. – hirschhornsalz

+0

Bu örneği bir yerlerde daha önce görmüştüm: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/ :-) –

cevap

7

Hiçbir şekilde bir GCC dahili uzmanı değilim, ama bir şans vereceğim. Ne yazık ki, KİK'lerin kayıt tahsisi ve dökülmesindeki bilgilerin çoğu güncelliğini yitirmiş gibi görünüyor (artık mevcut olmayan local-alloc.c gibi dosyalara başvuruyor).

gcc-4.5-20110825 kaynak koduna bakıyorum. GNU C Compiler Internals numaralı belgede, expand_function_start numaralı telefondan, expand_function_start numaralı telefondan tarafından üretildiği belirtilmektedir. Aşağıdaki assign_parms yılında

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

her argümanlar saklandığı yerde ele kod edilir: Orada biz ele parametreler için aşağıdaki bulmak

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); 

toplam veri türlerini ele ve bu durumda geçerli değildir ve veriler bir işaretçi olarak geçmediğinden, GCC use_register_for_decl'u denetler.

İşte alakalı parçasıdır: Değişken register anahtar kelime ile ilan edildi

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

DECL_REGISTER testleri ister. Ve şimdi cevabımız var: Optimizasyonlar etkinleştirilmediğinde çoğu parametre yığında yaşar ve daha sonra assign_parm_setup_stack tarafından işlenir. Kaynak kodun içinden geçen rota değeri dökmeden önce işaretçi argümanları için biraz daha karmaşıktır, ancak merak ettiğinizde aynı dosyada izlenebilir.

GCC neden tüm değişkenleri ve optimizasyonları devre dışı bırakılmış yerel değişkenleri döküyor? Hata ayıklamaya yardımcı olmak için. Bu benim makinede aşağıdaki üretir

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 ile Derleyen: bu basit işlevi düşünün

satır 5 üzerinde kırmak ve bir değerini yazdırmak çalışırsanız dışında gayet
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 

, sen o bar için çağrısından sonra kullanılmaz beri argüman üzerine alır gibi

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

olsun.

6

nedenlerden bir çift: Genel durumda,

  1. , bir işleve argüman için depolanabilir veya adresi fonksiyonu içinde alınmış, çünkü yerel bir değişken gibi tedavi edilmesi vardır. Bu nedenle, her bağımsız değişken için bir yığın yuvasını ayırmak en basit yoldur.
  2. Hata ayıklama bilgilerinin yığın konumlarıyla yayılması çok daha basit hale gelir: argümanın değeri her zaman belirli bir yerde, yazmaçlar ve bellek arasında gezinmek yerine olur.

Genelde -00 koduna baktığınızda, derleyicinin en önemli önceliklerinin derleme süresini olabildiğince düşürdüğünü ve yüksek kaliteli hata ayıklama bilgisi oluşturduğunu düşünün.

+1

Evet, ve herhangi bir optimizasyon olmadan, derleyici Özellikle tüm satırları bağımsız yapar, her zaman gerçek değişkenlerden yeniden yükler ve hemen depolar, bu da CPU'yu başka bir satıra taşımanıza veya herhangi bir değişkenin debugger'daki değerini değiştirmenize ve doğru şekilde davranmasına olanak tanır. – doug65536