2017-10-25 40 views
6

पूंछ कॉल अनुकूलन (TCO) के साथ प्रयोग, मैं निम्नलिखित उत्सुक उदाहरण पर ठोकर खाई:जीसीसी एक संस्करण के लिए पूंछ कॉल अनुकूलन क्यों करता है लेकिन दूसरे के लिए नहीं?

unsigned long long int fac1(unsigned long long int n){ 
    if (n==0) 
    return 1; 
    return n*fac1(n-1); 
} 

वास्तव में, मैं बहुत प्रभावित थे, कि gcc was able (-O2 ध्वज के साथ) यहाँ TCO प्रदर्शन करने के लिए है, क्योंकि यह नहीं है कि सीधे आगे:

012:

fac1(unsigned long long): 
     testq %rdi, %rdi 
     movl $1, %eax 
     je  .L4 
.L3: 
     imulq %rdi, %rax 
     subq $1, %rdi 
     jne  .L3 
     rep ret 
.L4: 
     rep ret 

हालांकि, unsigned long long intunsigned int करने से वापसी प्रकार में परिवर्तन के बाद जीसीसी नहीं Tlo प्रदर्शन करने में सक्षम था

हम स्पष्ट रूप से resulting assembly में पुनरावर्ती कॉल देख सकते हैं:

fac2(unsigned long long): 
     testq %rdi, %rdi 
     jne  .L16 
     movl $1, %eax 
     ret 
.L16: 
     pushq %rbx 
     movq %rdi, %rbx 
     leaq -1(%rdi), %rdi 
     call fac2(unsigned long long) 
     imull %ebx, %eax 
     popq %rbx 
     ret 

सबसे पहले, मैं एक चूक अनुकूलन के रूप में इस को खारिज कर दिया, लेकिन अब मैं, कि यकीन नहीं है क्योंकि clang isn't able रूप में अच्छी तरह इस अनुकूलन प्रदर्शन करने के लिए । तो हो सकता है कि उस भाषा की सूक्ष्मताएं हैं जिनके बारे में मुझे पता नहीं है, इस अनुकूलन को रोकें।

फ़ंक्शन fac2 फ़ंक्शन के लिए पूंछ-कॉल-ऑप्टिमाइज़ेशन क्यों नहीं करता है, लेकिन केवल fac1 के लिए?


यह मेरे लिए स्पष्ट है, कि दूसरे संस्करण में, परिणाम downcasted किया जाना चाहिए। जाहिर है यह एकमात्र अंतर है। लेकिन यह एक समस्या क्यों होनी चाहिए और टलो को रोकना चाहिए?

उदाहरण के लिए, अगर मैं संकलक मदद और एक क्लासिक पूंछ-प्रत्यावर्तन के रूप में मेरे समारोह को फिर से लिखने (संस्करण fac2 के समान परिणाम चाहिए):

unsigned int tlo_fac(unsigned long long int n, unsigned long long int cur){ 
    if (n==0) 
    return cur; 
    return tlo_fac(n-1, n*cur); 
} 

unsigned int fac(unsigned long long int n){ 
    return tlo_fac(n,1); 
} 

मैं एक Tlo-अनुकूलित संस्करण जो identical to fac1 है मिल (उच्च 32 बिट allowed to contain garbage हैं, इसलिए imulq इनलाइनिंग के बाद इस्तेमाल किया जा सकता):

fac(unsigned long long): 
     testq %rdi, %rdi 
     movl $1, %eax 
     je  .L10 
.L11: 
     imulq %rdi, %rax 
     subq $1, %rdi 
     jne  .L11 
.L10: 
     rep ret 
+2

हो सकता है क्योंकि वहाँ डाली है 'बदले में की जरूरत n * fac2 (n-1),' 'fact2 करने के लिए कॉल के बाद (n-1) ' –

+0

@ गौरावसेगल लेकिन यह कलाकार क्यों रोक देगा? – ead

+0

क्योंकि रिकर्सन गहराई का विश्लेषण करने के बाद कास्ट किया जाएगा। अगर आप पैरामीटर को 'fact2 (unisnged int n)' में बदलते हैं, तो आपको ऑप्टिमाइज़ेशन मिल जाएगा। –

उत्तर

2

fact2() में, के बाद प्रत्यावर्तन पूरा हो गया है एक डाली fr की आवश्यकता होगी ओम unsigned long long int को unsigned int

unsigned int fac2(unsigned int n) नीचे विधानसभा पैदा करता है,

fac2(unsigned int): 
     testl %edi, %edi 
     movl $1, %eax 
     je  .L10 
.L9: 
     imull %edi, %eax 
     subl $1, %edi 
     jne  .L9 
     rep ret 
.L10: 
     rep ret 
+2

यह जीसीसी को भ्रमित कर सकता है, लेकिन यह मूल रूप से * पुनरावृत्ति को पुन: लिखने से रोकता है – harold

+0

मुझे पता है कि एक कलाकार की आवश्यकता है और यह भी कि आउटपुट प्रकार में इनपुट पैरामीटर प्रकार बदलना एक tlo'ed संस्करण का कारण बन जाएगा। लेकिन यह मुझे स्पष्ट नहीं है, यह टीएलओ को क्यों रोक देगा। – ead

+1

@ead पूंछ कॉल अनुकूलन करने के लिए, रिकर्सिव कॉल फ़ंक्शन से किया जाने वाला आखिरी काम होना चाहिए, 'वापसी n * fac1 (n-1);', आखिरी बात यह नहीं है कि फ़ंक्शन कॉल 'तथ्य (एन- 1) ', लेकिन परिणाम का कास्टिंग। –

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