2012-01-11 27 views
20

मैं व्यवहार देख रहा हूं, मुझे इस कोड को जीसीसी में विभिन्न अनुकूलन स्तरों के साथ संकलित करते समय अपेक्षा नहीं है।जीसीसी बदलते सी प्रोग्राम व्यवहार में अनुकूलन स्तर

फ़ंक्शन टेस्ट को 64 बिट हस्ताक्षरित पूर्णांक को लोगों के साथ भरना चाहिए, उन्हें shift_size बिट्स को बाईं ओर स्थानांतरित करें, और 32 बिट बिट्स को 32 बिट हस्ताक्षरित पूर्णांक के रूप में वापस करें।

जब मैं -00 के साथ संकलित करता हूं तो मुझे परिणाम मिलने की उम्मीद होती है।

जब मैं -O2 के साथ संकलित करता हूं, तो मैं 32 बिट्स या उससे अधिक तक स्थानांतरित करने का प्रयास नहीं करता हूं।

असल में, मुझे वास्तव में परिणाम मिलते हैं, यदि मैं x86 पर बिट चौड़ाई से अधिक या बराबर शिफ्ट द्वारा 32 बिट पूर्णांक स्थानांतरित कर रहा था, जो शिफ्ट आकार के केवल 5 कम बिट्स का उपयोग करके एक शिफ्ट है ।

लेकिन मैं 64 बिट नंबर स्थानांतरित कर रहा हूं, इसलिए < 64 को स्थानांतरित करना कानूनी अधिकार होना चाहिए?

मुझे लगता है कि यह मेरी समझ में एक बग है और संकलक में नहीं, लेकिन मैं इसे समझने में सक्षम नहीं हूं।

मेरे मशीन: जीसीसी (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 i686-linux-gnu

#include <stdint.h> 
#include <stdio.h> 
#include <inttypes.h> 

uint32_t test(unsigned int shift_size) { 
    uint64_t res = 0; 
    res = ~res; 
    res = res << shift_size; //Shift size < uint64_t width so this should work 
    return res; //Implicit cast to uint32_t 
} 

int main(int argc, char *argv[]) 
{ 
    int dst; 
    sscanf(argv[1], "%d", &dst); //Get arg from outside so optimizer doesn't eat everything 
    printf("%" PRIu32 "l\n", test(dst)); 
    return 0; 
} 

उपयोग:

$ gcc -Wall -O0 test.c 
$ ./a.out 32 
0l 
$ gcc -Wall -O2 test.c 
$ ./a.out 32 
4294967295l 

gcc -S -Wall -O0 test.c

gcc -S -Wall -O2 test.c

+1

क्या आप अपने कंपाइलर द्वारा जेनरेट किए गए असेंबली कोड के प्रासंगिक हिस्सों को भी पोस्ट कर सकते हैं? (जीसीसी के लिए '-S') –

+0

एफडब्ल्यूआईडब्ल्यू यह जीसीसी 4.2.1 का उपयोग करके' -O0' से '-O3' के सभी अनुकूलन स्तरों पर मेरे लिए सही परिणाम (' 0l') देता है, इसलिए मुझे संदेह है कि आपके पास * हो सकता है * एक जीसीसी बग मिला। –

+0

एचएम, कोड मेरे लिए अनुकूलन स्तर दोनों में ठीक काम करता है ... (जीसीसी संस्करण 4.5.0 20100604, ओपनएसयूएसई 11.3 (x86_64)) – Bort

उत्तर

1

ऐसा लगता है कि यह एक जैसा दिखता है बग। में

res = res << shift_size 
return (uint32_t)res; 

:

return ((uint32_t)res) << shift_size; 

बाद अब 32 या बड़ा के लिए अच्छी तरह से परिभाषित किया गया है मेरे अनुमान कि संकलक से अंतिम दो पंक्तियों मुड़ा गया है।

3

ऐसा लगता है कि यह मुझे 32-बिट-विशिष्ट कंपाइलर बग हो सकता है। प्रश्न और जीसीसी 4.2.1 के कोड के साथ मैं gcc -m32 -O2 ... के साथ संकलित होने तक बग को पुन: उत्पन्न कर सकता हूं।

हालांकि, अगर मैं एक डिबग printf जोड़ें:

uint32_t test(unsigned int shift_size) { 
    uint64_t res = 0; 
    res = ~res; 
    res = res << shift_size; //Shift size < uint64_t width so this should work 
    printf("res = %llx\n", res); 
    return res; //Implicit cast to uint32_t 
} 

तो समस्या दूर चला जाता है।

अगला चरण बग की कोशिश करने और पहचानने/पुष्टि करने के लिए जेनरेट कोड को देखना होगा।

+0

अस्थायी कामकाज के रूप में, शायद कोई 'dev/null' पर प्रिंट कर सकता है:' FILE * null; शून्य = fopen ("/ dev/null", "w"); fprintf (शून्य, "res =% llx \ n", res); fclose (शून्य); ' –

+1

एक अस्थायी कामकाज के रूप में, 'res'' अस्थिर' बनाने के साथ ही काम करता है और कोई फ़ाइल ऑपरेशन की आवश्यकता नहीं होती है। – Fanael

5

"%u" (या "%lu") और uint32_t आवश्यक रूप से संगत नहीं हैं।

#include <inttypes.h> 

    //printf("%ul\n", test(dst)); 
    printf("%" PRIu32 "l\n", test(dst)); 

प्रयास करें एक "%u" (या "%lu") विनिर्देशक के साथ एक uint32_t मूल्य मुद्रण अपरिभाषित व्यवहार आह्वान कर सकते हैं।

+0

अच्छी कॉल, uint32_t हस्ताक्षरित int नहीं हो सकता है। – dreamlax

+0

@dreamlax: मुझे लगता है कि बिंदु यह है कि ओपी ने "% lu" की बजाय गलती से "% ul" का उपयोग किया, जो प्रिंटफ द्वारा "% u" के रूप में लिया जा रहा है। PRIU32 का उपयोग करना गलत विनिर्देशक का उपयोग करने की संभावना को हटा देता है। – tinman

+1

एक ही बिंदु ''% u ''और' "% lu" दोनों पर लागू होता है। – pmg

-2

मैं कहता C99 याद नहीं है, लेकिन ऐसा लगता है कि जीसीसी में, uint32_t कम से कम 32 बिट होते हैं, और अधिक हो सकती है, इसलिए जब आप यह अनुकूलन, यह 64 बिट वर, जिसमें तेजी से होता है का उपयोग 64-बिट मशीन।

+2

नहीं, uint32_t, यदि उपलब्ध हो, तो बिल्कुल 32 बिट्स है। (uint_fast32_t या uint_least32_t बड़ा हो सकता है, लेकिन uint32_t नहीं) – nos

+0

uint64_t uint32_t से सभी मामलों में तेज़ नहीं है: यदि आपके पास इंटेल/amd64 मशीनों पर 64 बिट पता स्थान है, तो 16 बिट और 32 बिट इंडेक्स के लिए यह एरे में अनुक्रमण के लिए तेज़ है परिवर्तित हो जाएं जबकि 8 बिट और 64 बिट उस असेंबलर भाषा में कानूनी ऑफसेट हैं। लेकिन गुणा और विभाजन धीमे हैं अगर आप बड़े आकार का उपयोग करते हैं। – comonad

4

मैं इसे दोबारा करने में सक्षम था। यहाँ -O2 साथ तैयार किए गए कोड के प्रासंगिक सा है:

movl $-1, %eax 
    movl $-1, %edx 
    sall %cl, %eax 
    xorl %edx, %edx 
    testb   $32, %cl 
    cmovne  %eax, %edx 
    cmovne  %edx, %eax ; This appears to be the instruction in error. 
          ; It looks as though gcc thought that %edx might still 
          ; be zero at this point, because if the shift count is 
          ; >= 32 then %eax should be zero after this. 

और यहाँ -O0 साथ बराबर सा है:

movl -16(%ebp), %eax 
    movl -12(%ebp), %edx 
    shldl %cl,%eax, %edx 
    sall %cl, %eax 
    testb   $32, %cl 
    je      L3 
    movl    %eax, %edx 
    xorl    %eax, %eax   ; correctly zeros %eax if shift count >= 32 
L3: 
    movl %eax, -16(%ebp) 
    movl %edx, -12(%ebp) 

संकलक है:

i686-apple-darwin11-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3) 

अपने gcc -S उत्पादन पोस्ट करने के लिए धन्यवाद। मैंने देखा और यह थोड़ा अलग है, लेकिन महत्वपूर्ण भाग में मेरी मशीन पर जो भी देखा गया है, वही त्रुटि है।