2012-12-30 10 views
7

मैंने मैक ओएसएक्स (एक्सकोड 4.2.1) और this पर लिनक्स पर जीसीसी 4.4.5 और जीसीसी-एलएलवीएम पर निम्नलिखित कोड का प्रयास किया है। नीचे प्रासंगिक कार्यों के स्रोत और उत्पन्न डिस्सेप्लर हैं। तर्क के बगल में: (जोड़ा gcc -O2 main.c साथ संकलित)यह कोड पूंछ-कॉल अनुकूलन से जीसीसी और llvm को क्यों रोकता है?

#include <stdio.h> 
__attribute__((noinline)) 
static void g(long num) 
{ 
     long m, n; 
     printf("%p %ld\n", &m, n); 
     return g(num-1); 
} 
__attribute__((noinline)) 
static void h(long num) 
{ 
     long m, n; 
     printf("%ld %ld\n", m, n); 
     return h(num-1); 
} 
__attribute__((noinline)) 
static void f(long * num) 
{ 
     scanf("%ld", num); 
     g(*num); 
     h(*num);   
     return f(num); 
} 
int main(void) 
{ 
     printf("int:%lu long:%lu unsigned:%lu\n", sizeof(int), sizeof(long), sizeof(unsigned)); 
     long num; 
     f(&num);     
     return 0; 
} 

08048430 <g>: 
8048430: 55     push %ebp 
8048431: 89 e5    mov %esp,%ebp 
8048433: 53     push %ebx 
8048434: 89 c3    mov %eax,%ebx 
8048436: 83 ec 24    sub $0x24,%esp 
8048439: 8d 45 f4    lea -0xc(%ebp),%eax 
804843c: c7 44 24 08 00 00 00 movl $0x0,0x8(%esp) 
8048443: 00 
8048444: 89 44 24 04   mov %eax,0x4(%esp) 
8048448: c7 04 24 d0 85 04 08 movl $0x80485d0,(%esp) 
804844f: e8 f0 fe ff ff  call 8048344 <[email protected]> 
8048454: 8d 43 ff    lea -0x1(%ebx),%eax 
8048457: e8 d4 ff ff ff  call 8048430 <g> 
804845c: 83 c4 24    add $0x24,%esp 
804845f: 5b     pop %ebx 
8048460: 5d     pop %ebp 
8048461: c3     ret  
8048462: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi 
8048469: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi 

08048470 <h>: 
8048470: 55     push %ebp 
8048471: 89 e5    mov %esp,%ebp 
8048473: 83 ec 18    sub $0x18,%esp 
8048476: 66 90    xchg %ax,%ax 
8048478: c7 44 24 08 00 00 00 movl $0x0,0x8(%esp) 
804847f: 00 
8048480: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) 
8048487: 00 
8048488: c7 04 24 d8 85 04 08 movl $0x80485d8,(%esp) 
804848f: e8 b0 fe ff ff  call 8048344 <[email protected]> 
8048494: eb e2    jmp 8048478 <h+0x8> 
8048496: 8d 76 00    lea 0x0(%esi),%esi 
8048499: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi 

080484a0 <f>: 
80484a0: 55     push %ebp 
80484a1: 89 e5    mov %esp,%ebp 
80484a3: 53     push %ebx 
80484a4: 89 c3    mov %eax,%ebx 
80484a6: 83 ec 14    sub $0x14,%esp 
80484a9: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi 
80484b0: 89 5c 24 04   mov %ebx,0x4(%esp) 
80484b4: c7 04 24 e1 85 04 08 movl $0x80485e1,(%esp) 
80484bb: e8 94 fe ff ff  call 8048354 <[email protected]> 
80484c0: 8b 03    mov (%ebx),%eax 
80484c2: e8 69 ff ff ff  call 8048430 <g> 
80484c7: 8b 03    mov (%ebx),%eax 
80484c9: e8 a2 ff ff ff  call 8048470 <h> 
80484ce: eb e0    jmp 80484b0 <f+0x10> 

हम देख सकते हैं कि g() और h() ज्यादातर & (का पता) को छोड़कर समान हैं ऑपरेटर printf() की m (और अप्रासंगिक %ld और %p)। हालांकि, h() पूंछ-कॉल अनुकूलित है और g() नहीं है। क्यूं कर?

उत्तर

6

जी() में, आप स्थानीय चर का पता ले रहे हैं और इसे किसी फ़ंक्शन में पास कर रहे हैं। एक "पर्याप्त स्मार्ट कंपाइलर" को एहसास होना चाहिए कि printf उस सूचक को संग्रहीत नहीं करता है। इसके बजाए, जीसीसी और एलएलवीएम मानते हैं कि printf कहीं भी पॉइंटर स्टोर कर सकता है, इसलिए एम युक्त कॉल फ्रेम को रिकर्सन में "लाइव" होना चाहिए। इसलिए, कोई टीसीओ नहीं।

3

यह & है जो यह करता है। यह संकलक को बताता है कि m स्टैक पर संग्रहीत किया जाना चाहिए। भले ही इसे printf पर पास किया गया हो, संकलक को यह मानना ​​है कि इसे किसी और द्वारा एक्सेस किया जा सकता है और इस प्रकार कॉल के बाद g पर स्टैक से साफ किया जाना चाहिए।

इस विशेष मामले में, printf को संकलक द्वारा जाना जाता है (और यह जानता है कि यह पॉइंटर्स को सहेजता नहीं है), इसे शायद इस अनुकूलन को करने के लिए सिखाया जा सकता है।

इस पर अधिक जानकारी के लिए, 'एनलिस से बचें' देखें।

संबंधित मुद्दे