2012-08-22 28 views
17

मैं कोड के इस छोटे-से स्निपेट है (इस समस्या का एक न्यूनतम काम कर उदाहरण मेरे पास है):अजीब व्यवहार सक्षम

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

void xorBuffer(unsigned char* dst, unsigned char* src, int len) 
{ 
    while (len != 0) 
    { 
     *dst ^= *src; 
     dst++; 
     src++; 
     len--; 
    } 
} 

int main() 
{ 
    unsigned char* a = malloc(32); 
    unsigned char* b = malloc(32); 
    int t; 

    memset(a, 0xAA, 32); 
    memset(b, 0xBB, 32); 

    xorBuffer(a, b, 32); 

    printf("result = "); 
    for (t = 0; t < 32; t++) printf("%.2x", a[t]); 
    printf("\n"); 

    return 0; 
} 

इस कोड को माना जाता है दो 32- अनन्य या के प्रदर्शन करने के लिए बाइट मेमोरी बफर (अवधारणात्मक रूप से, यह a = a^b करना चाहिए)। 0xAA^0xBB = 0x11 के बाद से, इसे "11" बार-बार प्रिंट करना चाहिए।

मेरे समस्या यह है, जब मैं इस MinGW-जीसीसी (विंडोज़) के तहत संकलन, इस डिबग मोड (एक भी अनुकूलन नहीं), लेकिन xorBuffer पाश के माध्यम से एक SIGILL रास्ते के मध्य में के साथ दुर्घटनाओं में पूरी तरह से काम करता है जब -O3 से शुरू अनुकूलन सक्षम हैं। इसके अलावा, अगर मैं अपमानजनक लूप में एक printf डालता हूं, तो यह पूरी तरह से फिर से काम करेगा। मुझे संदेह है कि भ्रष्टाचार ढेर है लेकिन मैं नहीं देखता कि मैं यहां क्या कर रहा हूं।

ऑप्टिमाइज़ेशन सक्षम के साथ जीडीबी के साथ डीबग करने का प्रयास करना एक खो गया कारण है क्योंकि सभी जीडीबी मुझे दिखाता है कि हर चर के लिए मुझे "परिवर्तनीय अनुकूलित" है (और, ज़ाहिर है, अगर मैं एक वैरिएबल आउट करने की कोशिश करता हूं और प्रिंट करता हूं, तो यह अचानक काम करेगा)।

क्या कोई जानता है कि बिल्ली यहां क्या चल रहा है? मैंने इस मुद्दे पर बहुत लंबा आवास बिताया है, और मुझे इसे आगे बढ़ने के लिए ठीक से ठीक करने की ज़रूरत है। मेरा अनुमान है कि मुझे कुछ मौलिक सी पॉइंटर ज्ञान याद आ रहा है, लेकिन मेरे लिए कोड सही दिखता है। यह बफर वृद्धि से हो सकता है, लेकिन जहां तक ​​मुझे पता है, sizeof(unsigned char) == 1, इसलिए यह प्रत्येक बाइट को एक-एक करके जा रहा है।

इसके लायक होने के लिए, कोड मेरे लिनक्स बॉक्स पर जीसीसी पर अनुकूलन के साथ भी काम करता है।

तो ... यहां क्या सौदा है? धन्यवाद!

के रूप में अनुरोध किया है, पूरे कार्यक्रम के विधानसभा उत्पादन:

-O2 के साथ: clicky

-O3 के साथ: clicky

मैं जीसीसी 4.6.2 पर इस व्यवहार का निरीक्षण (के साथ चल रहा MinGW)

+3

'O2' का उपयोग करने के बारे में क्या? 'ओ 3' बहुत जोखिम भरा है। सिगिल का अर्थ है - सिग्नल, अवैध निर्देश के कारण। मुझे एक कंपाइलर बग की तरह लग रहा है। या मुझे कुछ याद आ रही है। –

+1

यह मेरे जीसीसी 4.4.3 पर सही काम करता है। यदि कोई भी कुछ भी नहीं देखता है, तो आप दोनों मामलों में - 'ओ 3' और 'ओ 2' के साथ जीसीसी द्वारा उत्पादित असेंबली कोड दिखाने की कोशिश कर सकते हैं (यदि यह ठीक काम करता है, अन्यथा - अनुकूलन का निम्न स्तर) –

+0

@ किर्किलकिरोव यह बग मूल रूप से उच्च प्रदर्शन वाली लाइब्रेरी में खोजा गया था, इसलिए ओ 2 पर जाने के लिए वास्तव में हमारे लिए सबसे अच्छा समाधान नहीं है - बेशक अगर यह एक कंपाइलर बग है तो हम ठीक होने तक ओ 2 तक गिर जाएंगे। मैं बिना किसी अनुकूलन के उत्पादन और ओ 3 के साथ असेंबली पोस्ट करूंगा। – Thomas

उत्तर

8

मेरी टिप्पणी से:

मा सुनिश्चित करें कि कंपाइलर के पास लक्ष्य आर्किटेक्चर के बारे में सही जानकारी है। ऐसा लगता है कि -O3 आउटपुट पढ़ने से, कि कंपाइलर आपको सिम ऑप्टिमाइज़ेशन सेट कर रहा है, यह असर में वेक्टर निर्देशों (जैसे movdqa) का उपयोग कर कोड को समानांतर बना रहा है। यदि लक्ष्य प्रोसेसर 100% से मेल नहीं खाता है जो संकलक कोड उत्सर्जित कर रहा है, तो आप अवैध निर्देशों का अंत कर सकते हैं।

+0

धन्यवाद, यह था (विवरण के लिए अपना खुद का "विस्तार उत्तर" देखें)। – Thomas

8

मैं इसे अनविंड के उत्तर के विस्तार के रूप में जोड़ रहा हूं (जिसे मैं स्वीकार करता हूं क्योंकि यह मुझे सही रास्ते पर मिला है)।

अनुकूलित कोड के माध्यम से निकलने के बाद, मैंने AVX निर्देशों को देखा। सबसे पहले, मैंने सोचा कि यह किसी समस्या का कारण नहीं बनना चाहिए, क्योंकि मेरे प्रोसेसर AVX निर्देश सेट का समर्थन करता है। हालांकि, यह पता चला है कि दो अलग AVX संस्करण हैं: AVX1 और AVX2। और, जबकि मेरा प्रोसेसर केवल AVX1 का समर्थन करता है, जीसीसी अनिश्चित रूप से AVX2 ऑपकोड का उपयोग करता है जब तक प्रोसेसर दो संस्करणों में से किसी एक का समर्थन करता है (llvm ने वही गलती की है, उस पर bug reports हैं)। यह है, जहां तक ​​मैं कल्पना कर सकता हूं, गलत ऑपरेशन, और एक कंपाइलर बग।

परिणाम AVX1 प्रणाली पर AVX2 कोड है, जो स्पष्ट रूप से एक अवैध निर्देश की ओर जाता है।यह कोड से कई चीजों को बताता है, कोड से 25 बिट्स (256-बिट रजिस्टर चौड़ाई की वजह से) के इनपुट में असफल होने पर, मेरे लिनक्स बॉक्स पर काम करने वाले कोड पर, जो एसएसई 3 तक सीमित CPU समर्थन के साथ वर्चुअल मशीन होता है।

फिक्स या तो अक्षम करने के लिए है- ओ 3 और वापस -O2 पर जाएं, जहां जीसीसी सरल कोड को अनुकूलित करने के लिए सबसे कट्टर सिम निर्देशों का सहारा नहीं लेगा, या volatile कीवर्ड का उपयोग करने के लिए जो इसे लागू करने के लिए मजबूर करेगा बफ़र्स बाइट प्रति बाइट, बड़ी मेहनत से, इसलिए जैसे:

*(unsigned char volatile *)dst ^= *(unsigned char volatile *)src; 

यह बहुत धीमी गति से निश्चित रूप से है और शायद सिर्फ -O2 का उपयोग कर से भी बदतर (सम्पूर्ण कार्यक्रम नतीजों अनदेखी), लेकिन यह चारों ओर बफर से गुज़रते हुए काम किया जा सकता है अंत में int द्वारा int और अंत में पैडिंग, जो गति के मामले में पर्याप्त है।

एक और अच्छा फिक्स जीसीसी के एक संस्करण में अपग्रेड करना है जिसमें यह बग नहीं है (यह संस्करण अभी तक मौजूद नहीं है, मैंने चेक नहीं किया है)।

संपादित करें: परम ठीक पूरी तरह से (कोई कोड संशोधनों के साथ बग negating जीसीसी में -mno-AVX झंडा फेंक, जिससे किसी भी और सभी AVX opcodes अक्षम करने, और एक समझौता संकलक संस्करण है आसानी से एक बार हटाया जा सकता है उपलब्ध)।

क्या एक प्रतिकूल संकलक बग है।

+0

कहने के लिए +1 पूरी कहानी है; विचित्रता के इस टुकड़े को शिकार करने पर बधाई। –

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