मैं एक अनंत खाली लूप कैसे बनाऊंगा जिसे दूर नहीं किया जाएगा?


131

C11 मानक का तात्पर्य यह है कि निरंतर नियंत्रण अभिव्यक्तियों के साथ पुनरावृत्ति बयानों को अनुकूलित नहीं किया जाना चाहिए। मैं इस उत्तर से अपनी सलाह ले रहा हूं , जो विशेष रूप से मसौदा मानक से खंड 6.8.5 को उद्धृत करता है:

एक पुनरावृत्ति बयान जिसका नियंत्रण अभिव्यक्ति एक स्थिर अभिव्यक्ति नहीं है ... को समाप्त करने के लिए कार्यान्वयन द्वारा ग्रहण किया जा सकता है।

उस उत्तर में यह उल्लेख किया गया है कि एक लूप जैसे while(1) ;अनुकूलन के अधीन नहीं होना चाहिए।

तो ... क्लैंग / एलएलवीएम नीचे लूप का अनुकूलन क्यों करता है (इसके साथ संकलित cc -O2 -std=c11 test.c -o test)?

#include <stdio.h>

static void die() {
    while(1)
        ;
}

int main() {
    printf("begin\n");
    die();
    printf("unreachable\n");
}

मेरी मशीन पर, यह प्रिंट निकलता है begin, फिर एक अवैध निर्देश ( दुर्घटना केud2 बाद रखा गया जाल die()) पर दुर्घटनाग्रस्त हो जाता है । Godbolt पर , हम देख सकते हैं कि कॉल करने के बाद कुछ भी उत्पन्न नहीं हुआ है puts

क्लैंग को एक अनन्त लूप के उत्पादन के लिए प्राप्त करना आश्चर्यजनक रूप से कठिन काम है, -O2जबकि मैं बार-बार एक volatileचर का परीक्षण कर सकता हूं , जिसमें एक मेमोरी रीड शामिल है जो मुझे नहीं चाहिए। और अगर मैं ऐसा कुछ करता हूं:

#include <stdio.h>

static void die() {
    while(1)
        ;
}

int main() {
    printf("begin\n");
    volatile int x = 1;
    if(x)
        die();
    printf("unreachable\n");
}

... क्लैग प्रिंट के beginबाद unreachableमानो अनंत लूप कभी मौजूद नहीं था।

आप क्लैंग को आउटपुट कैसे प्राप्त करते हैं, ऑप्टिमाइज़ेशन के साथ एक उचित, नो-मेमोरी-एक्सेस अनंत लूप का उत्पादन होता है?


3
टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
भार्गव राव

2
कोई पोर्टेबल समाधान नहीं है जो एक साइड इफेक्ट को शामिल नहीं करता है। यदि आप मेमोरी एक्सेस नहीं चाहते हैं, तो आपकी सबसे अच्छी आशा अस्थिर अहस्ताक्षरित चार पंजीकृत होगी; लेकिन रजिस्टर सी ++ 17 में चला जाता है।
स्कॉट एम

25
शायद यह सवाल के दायरे में नहीं है, लेकिन मैं उत्सुक हूं कि आप ऐसा क्यों करना चाहते हैं। निश्चित रूप से आपके वास्तविक कार्य को पूरा करने का कोई और तरीका है। या यह प्रकृति में सिर्फ अकादमिक है?
क्रंचर

1
@ क्रंचर: किसी प्रोग्राम को चलाने के किसी विशेष प्रयास के प्रभाव उपयोगी हो सकते हैं, अनिवार्य रूप से बेकार या बेकार से भी बदतर। एक अंतहीन पाश में फंसने वाले कार्यक्रम के परिणामस्वरूप निष्पादन बेकार हो सकता है, लेकिन फिर भी अन्य व्यवहारों के लिए बेहतर हो सकता है जो एक संकलक स्थानापन्न कर सकता है।
सुपरकैट

6
@ क्रंचर: क्योंकि कोड एक फ्रीस्टैंडिंग संदर्भ में चल रहा हो सकता है जहां कोई अवधारणा नहीं है exit(), और क्योंकि कोड ने ऐसी स्थिति की खोज की हो सकती है जहां यह गारंटी नहीं दे सकता कि निरंतर निष्पादन के प्रभाव बेकार से भी बदतर नहीं होंगे । इस तरह की परिस्थितियों से निपटने के लिए एक जंप-टू-सेल्फ लूप एक सुंदर तरीका है, लेकिन फिर भी यह खराब स्थिति से निपटने का सबसे अच्छा तरीका हो सकता है।
सुपरकैट

जवाबों:


77

C11 मानक यह कहता है, 6.8.5 / 6:

एक पुनरावृत्ति कथन जिसका नियंत्रण अभिव्यक्ति एक स्थिर अभिव्यक्ति नहीं है, 156) जो कोई इनपुट / आउटपुट संचालन नहीं करता है, अस्थिर वस्तुओं तक नहीं पहुंचता है, और अभिव्यक्ति को नियंत्रित करने, या (के मामले में) अपने शरीर में कोई सिंक्रनाइज़ेशन या परमाणु संचालन नहीं करता है कथन) इसकी अभिव्यक्ति -3, को समाप्त करने के लिए कार्यान्वयन द्वारा ग्रहण किया जा सकता है। 157)

दो फुट के नोट मानक नहीं हैं, लेकिन उपयोगी जानकारी प्रदान करते हैं:

156) एक नियंत्रित नियंत्रण अभिव्यक्ति को एक गैर-स्थिर स्थिरांक द्वारा प्रतिस्थापित किया जाता है, जो एक स्थिर अभिव्यक्ति है।

157) इसका उद्देश्य संकलक परिवर्तनों की अनुमति देना है जैसे कि समाप्ति को सिद्ध नहीं किए जाने पर भी खाली छोरों को हटाना।

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

लूप के बाद "अगम्य कोड" का क्या होता है, हालांकि, जहां तक ​​मुझे पता है, अच्छी तरह से परिभाषित नहीं है। हालांकि, क्लैंग वास्तव में बहुत अजीब व्यवहार करता है। Gcc (x86) के साथ मशीन कोड की तुलना:

gcc 9.2 -O3 -std=c11 -pedantic-errors

.LC0:
        .string "begin"
main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:.LC0
        call    puts
.L2:
        jmp     .L2

क्लेंग 9.0.0 -O3 -std=c11 -pedantic-errors

main:                                   # @main
        push    rax
        mov     edi, offset .Lstr
        call    puts
.Lstr:
        .asciz  "begin"

gcc लूप उत्पन्न करता है, क्लैंग सिर्फ जंगल में चलता है और एरर 255 से बाहर निकलता है।

मैं क्लैंग के गैर-आज्ञाकारी व्यवहार होने के कारण झुक रहा हूं। क्योंकि मैंने आपके उदाहरण को इस तरह आगे बढ़ाने की कोशिश की:

#include <stdio.h>
#include <setjmp.h>

static _Noreturn void die() {
    while(1)
        ;
}

int main(void) {
    jmp_buf buf;
    _Bool first = !setjmp(buf);

    printf("begin\n");
    if(first)
    {
      die();
      longjmp(buf, 1);
    }
    printf("unreachable\n");
}

मैंने _Noreturnकंपाइलर को आगे बढ़ाने में मदद करने के प्रयास में C11 जोड़ा । यह स्पष्ट होना चाहिए कि यह फ़ंक्शन केवल उस कीवर्ड से लटका होगा।

setjmpप्रथम निष्पादन पर 0 वापस आ जाएगा, इसलिए इस कार्यक्रम को केवल while(1)"शुरू" (मानकर \ n फ्लश करना) प्रिंट करना और वहां रुकना चाहिए । जीसीसी के साथ ऐसा होता है।

यदि लूप को बस हटा दिया गया था, तो इसे "शुरू" 2 बार प्रिंट करना चाहिए फिर "अप्राप्य" प्रिंट करना चाहिए। हालांकि क्लैंग ( गॉडबोल्ट ) पर, यह "1 बार" शुरू होता है और फिर बाहर निकलने के कोड को वापस करने से पहले "अगम्य" होता है। 0. यह केवल स्पष्ट गलत है, चाहे आप इसे कैसे भी कहें।

मुझे यहां अपरिभाषित व्यवहार का दावा करने के लिए कोई मामला नहीं मिल सकता है, इसलिए मेरा कहना है कि यह एक बग है। किसी भी दर पर, यह व्यवहार एम्बेडेड सिस्टम जैसे कार्यक्रमों के लिए 100% बेकार बना देता है, जहां आपको बस कार्यक्रम को लटकाते हुए अनन्त छोरों पर भरोसा करने में सक्षम होना चाहिए (जबकि प्रहरी आदि के लिए इंतजार करना)।


15
मैं "यह एक क्रिस्टल स्पष्ट निरंतर अभिव्यक्ति पर असहमत हूं , इसलिए इसे समाप्त करने के लिए कार्यान्वयन द्वारा ग्रहण नहीं किया जा सकता है" । यह वास्तव में लीख picky भाषा lawyering में हो जाता है, लेकिन 6.8.5/6के रूप में है , तो (इन) तो आप मान सकते हैं (यह) । इसका मतलब यह नहीं है कि नहीं (ये) आप नहीं मान सकते हैं (यह) । यह केवल तब के लिए एक विनिर्देशन है जब शर्तों को पूरा किया जाता है, न कि जब वे एकमत होते हैं तो आप मानकों के साथ जो चाहें कर सकते हैं। और अगर कोई
वेधशाला

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

2
मैं मानता हूँ, लेकिन आप surpised नहीं किया जाएगा कि में int z=3; int y=2; int x=1; printf("%d %d\n", x, z);नहीं है वहाँ 2विधानसभा में है, इसलिए में खाली बेकार भावना xके बाद नहीं सौंपा गया था yलेकिन बाद zअनुकूलन की वजह से। तो अपने अंतिम वाक्य से जा रहे हैं, हम नियमित नियमों का पालन करते हैं, जबकि ठहराव को मानते हैं (क्योंकि हम किसी भी बेहतर विवश नहीं थे), और फाइनल में "अप्राप्य" प्रिंट छोड़ दिया। अब, हम उस बेकार स्टेटमेंट का अनुकूलन करते हैं (क्योंकि हम कोई बेहतर नहीं जानते हैं)।
कबानुस

2
@MSalters मेरी एक टिप्पणी को हटा दिया गया था, लेकिन इनपुट के लिए धन्यवाद - और मैं सहमत हूं। मेरी टिप्पणी में कहा गया है कि मुझे लगता है कि यह बहस का दिल है - एक शब्द के while(1);रूप में एक ही int y = 2;है कि हम शब्दार्थ को अनुकूलित करने की अनुमति देते हैं, भले ही उनका तर्क स्रोत में हो। N1528 से मैं इस धारणा के तहत था कि वे समान हो सकते हैं, लेकिन चूंकि लोग मेरे मुकाबले अधिक अनुभवी हैं, दूसरे तरीके से बहस कर रहे हैं, और यह जाहिरा तौर पर एक आधिकारिक बग है, फिर एक दार्शनिक बहस से परे कि क्या मानक में शब्दांकन स्पष्ट है तर्क को प्रस्तुत किया गया है।
कबनस

2
"हमेशा-हमेशा के लिए 'लूप्स एक सामान्य प्रोग्रामिंग कंस्ट्रक्शन है, क्योंकि इस तरह का कार्यान्वयन पूरी तरह से टूट जाएगा।" - मैं भावुकता को समझता हूं लेकिन तर्क त्रुटिपूर्ण है क्योंकि इसे C ++ में समान रूप से लागू किया जा सकता है, फिर भी एक C ++ कंपाइलर जो इस लूप को अनुकूलित करता है वह टूटेगा नहीं बल्कि अनुरूप होगा।
कोनराड रूडोल्फ

52

आपको एक अभिव्यक्ति सम्मिलित करने की आवश्यकता है जो एक साइड-इफेक्ट का कारण बन सकती है।

सबसे सरल उपाय:

static void die() {
    while(1)
       __asm("");
}

गॉडबोल्ट लिंक


21
क्लैंग क्यों अभिनय कर रहा है, इसकी व्याख्या नहीं करता है।
लुंडिन

4
हालांकि यह कहना कि "यह बग में एक बग है" पर्याप्त है। मैं कुछ चीज़ें आज़माना चाहता हूँ, हालांकि पहले मैं "बग" चिल्लाता हूँ।
लुंडिन

3
@ लुंडिन मुझे नहीं पता कि क्या यह एक बग है। मानक इस मामले में तकनीकी रूप से सटीक नहीं है
P__J__

4
सौभाग्य से, जीसीसी खुला स्रोत है और मैं एक संकलक लिख सकता हूं जो आपके उदाहरण को दूर करता है। और मैं ऐसा किसी भी उदाहरण के लिए कर सकता हूं, जो अब और भविष्य में आपके साथ आएगा।
थॉमस वेलर

3
@ थोमसवेलर: जीसीसी देव इस पाश को दूर करने वाले पैच को स्वीकार नहीं करेंगे; यह प्रलेखित = गारंटीकृत व्यवहार का उल्लंघन करेगा। मेरी पिछली टिप्पणी देखें: asm("")परोक्ष है asm volatile("");के रूप में यह सार मशीन में करता है और इस प्रकार एएसएम बयान कई बार के रूप में चलाना चाहिए gcc.gnu.org/onlinedocs/gcc/Basic-Asm.html । (ध्यान दें कि किसी भी मेमोरी या रजिस्टरों को शामिल करने के लिए इसके साइड इफेक्ट्स के लिए सुरक्षित नहीं है ; आपको एक "memory"क्लोबर के साथ विस्तारित एएसएम की आवश्यकता है यदि आप मेमोरी पढ़ना या लिखना चाहते हैं जो आप कभी भी सी से एक्सेस करते हैं। बेसिक एएसएम केवल सामान जैसे asm("mfence")या के लिए सुरक्षित है cli।)
पीटर कॉर्डेस

50

अन्य उत्तर पहले से ही इनलाइन असेंबली भाषा या अन्य दुष्प्रभावों के साथ क्लैंग को अनंत लूप बनाने के लिए कवर किए गए तरीके हैं। मैं सिर्फ यह पुष्टि करना चाहता हूं कि यह वास्तव में एक कंपाइलर बग है। विशेष रूप से, यह एक लंबे समय तक चलने वाला एलएलवीएम बग है - यह उन भाषाओं के "++ सभी छोरों को बिना साइड-इफ़ेक्ट के समाप्त करना चाहिए" C ++ की अवधारणा पर लागू होता है जहां यह नहीं होना चाहिए, जैसे कि सी।

उदाहरण के लिए, रस्ट प्रोग्रामिंग भाषा भी अनंत छोरों की अनुमति देती है और एलएलवीएम को बैकएंड के रूप में उपयोग करती है, और इसका यही मुद्दा है।

अल्पावधि में, ऐसा प्रतीत होता है कि एलएलवीएम यह मानकर चलता रहेगा कि "दुष्प्रभावों के बिना सभी छोरों को समाप्त करना होगा"। किसी भी भाषा के लिए जो अनंत लूप की अनुमति देती है, LLVM फ्रंट-एंड को llvm.sideeffectइस तरह के लूप में ओपकोड सम्मिलित करने की अपेक्षा करता है। यह वही है जो रुस्त करने की योजना बना रहा है, इसलिए क्लैंग (जब सी कोड को संकलित करता है) शायद यह भी करना होगा।


5
बग की गंध जैसा कुछ भी नहीं है जो एक दशक से अधिक पुराना है ... कई प्रस्तावित सुधारों और पैच के साथ ... अभी भी तय नहीं किया गया है।
इयान केम्प

4
@IKKemp: बग को ठीक करने के लिए अब उन्हें यह स्वीकार करने की आवश्यकता होगी कि बग को ठीक करने के लिए उन्हें दस साल लगे हैं। यह सुनिश्चित करने के लिए बेहतर है कि मानक उनके व्यवहार को सही ठहराने के लिए बदल जाएगा। बेशक, भले ही मानक बदल गया हो, फिर भी यह उनके व्यवहार को छोड़कर लोगों के नज़र में उचित नहीं होगा, जो मानक में बदलाव को एक संकेत के रूप में स्वीकार करेगा कि मानक का पूर्व व्यवहार जनादेश एक दोष था जिसे रेट्रोचैक्टली रूप से ठीक किया जाना चाहिए।
सुपरकैट

4
यह इस अर्थ में "निश्चित" किया गया है कि LLVM ने sideeffectop (2017 में) जोड़ा है और सामने वाले से यह अपेक्षा रखता है कि वह अपने विवेक पर छोरों को सम्मिलित करे। LLVM को लूप्स के लिए कुछ डिफॉल्ट चुनना था , और यह वही हुआ जिसे C ++ के व्यवहार के साथ संरेखित किया गया था, जानबूझकर या अन्यथा। बेशक, अभी भी कुछ अनुकूलन कार्य होना बाकी है, जैसे कि लगातार sideeffectऑप्स को एक में विलय करना । (यह वह है जो रस्ट फ्रंट-एंड का उपयोग करने से रोक रहा है।) इसलिए उस आधार पर, बग फ्रंट-एंड (क्लैंग) में है जो लूप में ऑप नहीं डालता है।
अर्नवियन

@ अर्नावियन: क्या यह संकेत देने का कोई तरीका है कि जब तक या परिणाम का उपयोग नहीं किया जाता है, तब तक संचालन स्थगित किया जा सकता है, लेकिन अगर डेटा एक प्रोग्राम को अंतहीन रूप से लूप देगा, तो पिछले डेटा निर्भरता को आगे बढ़ाने की कोशिश करने से प्रोग्राम बेकार हो जाएगा ? फॉनी साइड-इफेक्ट्स को जोड़ने से जो पूर्व उपयोगी अनुकूलन को रोक देगा, जिससे ऑप्टिमाइज़र को प्रोग्राम को बेकार से बदतर बनाने से रोका जा सके।
सुपरकैट

वह चर्चा संभवतः LLVM / क्लैंग मेलिंग सूचियों पर है। एफडब्ल्यूआईडब्ल्यू एलएलवीएम कमिट जो कि ऑप जोड़ा गया, ने इसके बारे में कई अनुकूलन पास भी सिखाए। इसके अलावा, रस्ट sideeffectने प्रत्येक फ़ंक्शन की शुरुआत में ऑप्स डालने के साथ प्रयोग किया और कोई रनटाइम प्रदर्शन प्रतिगमन नहीं देखा। एकमात्र मुद्दा एक संकलन समय का प्रतिगमन है, जाहिरा तौर पर लगातार ऑप्स के संलयन की कमी के कारण जैसे मैंने अपनी पिछली टिप्पणी में उल्लेख किया था।
अर्नियन

32

यह एक क्लैंग बग है

... एक अनंत लूप वाले फ़ंक्शन को सम्मिलित करते समय। while(1);मुख्य रूप से सीधे प्रकट होने पर व्यवहार भिन्न होता है, जो मुझे बहुत छोटी लगता है।

सारांश और लिंक के लिए @ अर्नवियन का उत्तर देखें । इस उत्तर के बाकी हिस्सों को मैंने पुष्टि करने से पहले लिखा था कि यह एक बग था, अकेले एक ज्ञात बग दें।


शीर्षक प्रश्न का उत्तर देने के लिए: मैं एक अनंत खाली लूप कैसे बनाऊंगा जिसे दूर नहीं किया जाएगा? ? - क्लेग 3.9 में और बाद में इस बग के आसपास काम करने के
लिए die()एक मैक्रो, फ़ंक्शन नहीं । (पहले के क्लैंग संस्करण या तो लूप रखते हैं या एक का उत्सर्जन करते हैंcall अनंत लूप के साथ समारोह की एक गैर इनलाइन संस्करण के लिए।) प्रकट होता है सुरक्षित है यही कारण है कि यहां तक कि अगर print;while(1);print;में समारोह inlines अपने फोन करने वाले ( Godbolt )। -std=gnu11बनाम -std=gnu99कुछ भी नहीं बदलता है।

यदि आप केवल GNU C की परवाह करते हैं, तो P__J ____asm__(""); का लूप अंदर भी काम करता है, और इसे समझने वाले किसी भी कंपाइलर के लिए किसी भी आसपास के कोड के अनुकूलन को नुकसान नहीं पहुंचाना चाहिए। जीएनयू सी बेसिक एएसएम के कथन स्पष्ट रूप से हैंvolatile , इसलिए यह एक दृश्य साइड-इफ़ेक्ट के रूप में गिना जाता है जिसे सी एब्सट्रैक्ट मशीन में "निष्पादित" करना होता है। (और हाँ, क्लेंग जीसीसी मैनुअल द्वारा प्रलेखित के रूप में सी की GNU बोली को लागू करता है।)


कुछ लोगों ने तर्क दिया है कि खाली अनंत लूप को अनुकूलित करना कानूनी हो सकता है। मैं सहमत नहीं हूँ 1 , लेकिन फिर भी अगर हम कि स्वीकार करते हैं, यह नहीं कर सकता भी कानूनी बजना मान के लिए करने के लिए लूप के बाद बयान पहुंचा नहीं जा सकता, हो और अगले समारोह में समारोह के अंत में निष्पादन गिरावट है, या कचरा में यादृच्छिक निर्देश के रूप में डिकोड करता है।

(यह क्लैंग ++ के लिए मानक-अनुरूप होगा (लेकिन अभी भी बहुत उपयोगी नहीं है), बिना किसी साइड इफेक्ट के अनंत लूप सी ++ में यूबी हैं, लेकिन सी नहीं।
(1), सी में अपरिभाषित व्यवहार? यूबी कंपाइलर मूल रूप से कुछ भी उत्सर्जन करने देता है। निष्पादन के एक मार्ग पर कोड के लिए जो निश्चित रूप से UB का सामना करेगा। asmलूप में एक कथन C ++ के लिए इस UB से बचता है। लेकिन व्यवहार में, C ++ के रूप में क्लैंग संकलन निरंतर अभिव्यक्ति को नहीं हटाता है जब इनलाइनिंग को छोड़कर अनंत लूप खोले जाते हैं, जब वे सी। के रूप में संकलन)


मैन्युअल रूप से inlining while(1); परिवर्तन होता है कि क्लैंग इसे कैसे संकलित करता है: असीम लूप में मौजूद। यह वही है जो हम एक नियम-वकील POV से उम्मीद करेंगे।

#include <stdio.h>
int main() {
    printf("begin\n");
    while(1);
    //infloop_nonconst(1);
    //infloop();
    printf("unreachable\n");
}

गॉडबोल्ट कंपाइलर एक्सप्लोरर पर, क्लेंग 9.0 -ओ 3 संकलन के रूप में सी ( -xc) के लिए x86-64:

main:                                   # @main
        push    rax                       # re-align the stack by 16
        mov     edi, offset .Lstr         # non-PIE executable can use 32-bit absolute addresses
        call    puts
.LBB3_1:                                # =>This Inner Loop Header: Depth=1
        jmp     .LBB3_1                   # infinite loop


.section .rodata
 ...
.Lstr:
        .asciz  "begin"

एक ही विकल्प के साथ एक ही संकलक एक संकलन करता है maininfloop() { while(1); } उसी को पहले कॉलputs , लेकिन फिर mainउस बिंदु के लिए निर्देश छोड़ना बंद कर देता है । इसलिए जैसा कि मैंने कहा, फंक्शन सिर्फ फंक्शन के अंत में पड़ता है, जो भी फ़ंक्शन होता है (लेकिन फंक्शन एंट्री के लिए स्टैक मिसलॉन्ग किया जाता है, इसलिए यह एक वैध टेलकॉल भी नहीं है)।

मान्य विकल्प होगा

  • एक label: jmp labelअनंत लूप का उत्सर्जन करें
  • या (यदि हम स्वीकार करते हैं कि अनंत लूप को हटाया जा सकता है) 2 वें स्ट्रिंग को प्रिंट करने के लिए एक और कॉल का उत्सर्जन करें, और फिर return 0सेmain

"अप्राप्य" को प्रिंट किए बिना क्रैश करना या अन्यथा जारी रखना C11 कार्यान्वयन के लिए स्पष्ट रूप से ठीक नहीं है, जब तक कि यूबी नहीं है कि मैंने ध्यान नहीं दिया है।


फुटनोट 1:

रिकॉर्ड के लिए, मैं इससे सहमत हूं @ लुंडिन के उत्तर से जो सबूत के लिए मानक का हवाला देता है कि C11 निरंतर-अभिव्यक्ति अनंत छोरों के लिए समाप्ति की धारणा की अनुमति नहीं देता है, भले ही वे खाली हों (I / O, वाष्पशील, सिंक्रनाइज़ेशन, या अन्य नहीं दृश्यमान प्रभाव)।

यह उन स्थितियों का समूह है जो लूप को खाली एसम लूप में संकलित करने देता है को एक सामान्य सीपीयू के लिए एक । (भले ही शरीर स्रोत में खाली न हो, लेकिन चर के असाइनमेंट डेटा-रेस UB के बिना अन्य थ्रेड्स या सिग्नल हैंडलर को दिखाई नहीं दे सकते हैं जबकि लूप चल रहा है। इसलिए एक अनुरूप कार्यान्वयन ऐसे लूप निकायों को हटा सकता है यदि यह चाहता था। इसके बाद, यह सवाल छोड़ देता है कि क्या लूप को स्वयं हटाया जा सकता है। ISO C11 स्पष्ट रूप से नहीं कहता है।)

यह देखते हुए कि C11 उस मामले को एकल के रूप में प्रस्तुत करता है जहां कार्यान्वयन लूप को समाप्त नहीं कर सकता है (और यह कि यह यूबी नहीं है), यह स्पष्ट लगता है कि वे लूप को रन-टाइम पर उपस्थित होने का इरादा रखते हैं। एक कार्यान्वयन जो सीपीयू को एक निष्पादन मॉडल के साथ लक्षित करता है जो अनंत समय में काम नहीं कर सकता है एक खाली निरंतर अनंत लूप को हटाने का कोई औचित्य नहीं है। या यहां तक ​​कि सामान्य तौर पर, सटीक शब्द इस बारे में है कि क्या उन्हें "समाप्त करने के लिए ग्रहण किया जा सकता है" या नहीं। यदि कोई लूप समाप्त नहीं हो सकता है, तो इसका मतलब है कि बाद में कोड उपलब्ध नहीं है, इससे कोई फर्क नहीं पड़ता कि आप क्या तर्क देते हैं गणित और अनन्तताओं के बारे में और किसी काल्पनिक मशीन पर अनंत मात्रा में काम करने में कितना समय लगता है।

इसके अलावा, क्लैंग केवल एक आईएसओ सी आज्ञाकारी डेथक्राफ्ट 9000 नहीं है, यह वास्तविक दुनिया के निचले स्तर के सिस्टम प्रोग्रामिंग के लिए उपयोगी है, जिसमें कर्नेल और एम्बेडेड सामान शामिल हैं। तो क्या आप C11 को हटाने की अनुमति के बारे में तर्क स्वीकार करते हैं या नहीं while(1);, इससे कोई मतलब नहीं है कि क्लैंग वास्तव में ऐसा करना चाहेगा। यदि आप लिखते हैं while(1);, कि शायद एक दुर्घटना नहीं थी। दुर्घटना से अनंत को समाप्त करने वाले छोरों को हटाना (रनटाइम चर नियंत्रण अभिव्यक्ति के साथ) उपयोगी हो सकता है, और यह संकलक को ऐसा करने के लिए समझ में आता है।

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

उदाहरण के लिए, एक आदिम OS कर्नेल में, जब अनुसूचक के पास चलाने के लिए कोई कार्य नहीं होता है तो वह निष्क्रिय कार्य चला सकता है। इसका पहला कार्यान्वयन हो सकता है while(1);

या हार्डवेयर के लिए किसी भी बिजली की बचत निष्क्रिय सुविधा के बिना, कि केवल कार्यान्वयन हो सकता है। (2000 के दशक की शुरुआत तक, मुझे लगता है कि x86 पर दुर्लभ नहीं था। हालाँकि यह hltनिर्देश मौजूद नहीं था, अगर सीपीयू के कम-शक्ति निष्क्रिय होने की स्थिति होने पर आईडीके ने इसे सार्थक मात्रा में बचाया।


1
जिज्ञासा से बाहर, क्या कोई वास्तव में एम्बेडेड सिस्टम के लिए क्लैंग का उपयोग कर रहा है? मैंने इसे कभी नहीं देखा है और मैं विशेष रूप से एम्बेडेड के साथ काम करता हूं। केवल "हाल ही में" (10 साल पहले) एम्बेडेड बाजार में प्रवेश किया और मैं एक का उपयोग करता हूं कि स्पष्ट रूप से, अधिमानतः कम अनुकूलन के साथ और हमेशा साथ -ffreestanding -fno-strict-aliasing। यह एआरएम और शायद विरासत एवीआर के साथ ठीक काम करता है।
लुंडिन

1
@ लुंडिन: एम्बेडेड के बारे में IDK, लेकिन हाँ लोग कम से कम कभी-कभी लिनक्स के साथ गुठली का निर्माण करते हैं। संभवतः MacOS के लिए डार्विन भी।
पीटर कॉर्डेस

2
bugs.llvm.org/show_bug.cgi?id=965 यह बग प्रासंगिक लगता है, लेकिन मुझे यकीन नहीं है कि हम यहां देख रहे हैं।
Bracco23

1
@ लंडिन - मुझे पूरा यकीन है कि हमने 90 के दशक में आरटीओएस की तरह VxWorks और PSOS के साथ एम्बेडेड काम के लिए जीसीसी (और अन्य टूलकिट्स) का इस्तेमाल किया था। मुझे समझ नहीं आता कि आप ऐसा क्यों कहते हैं कि GCC ने हाल ही में एम्बेडेड बाजार में प्रवेश किया है।
जेफ लीमैन

1
@JeffLearman हाल ही में मुख्यधारा बना, तब? वैसे भी, सीसीसी की शुरुआत के बाद ही सख्त सख्त अलियासिंग फियास्को हुआ, और इसके नए संस्करण अब या तो सख्त अलियासिंग उल्लंघन का सामना करने पर केले जाने के लिए प्रतीत नहीं होते हैं। जब भी मैं इसका उपयोग करता हूं, तब भी मुझे संदेह रहता है। क्लैंग के लिए, शाश्वत छोरों की बात आने पर नवीनतम संस्करण पूरी तरह से टूट गया है, इसलिए इसका उपयोग एम्बेडेड सिस्टम के लिए नहीं किया जा सकता है।
लंडिन

14

सिर्फ रिकॉर्ड के लिए, क्लैंग भी गलत व्यवहार करता है goto:

static void die() {
nasty:
    goto nasty;
}

int main() {
    int x; printf("begin\n");
    die();
    printf("unreachable\n");
}

यह सवाल के रूप में एक ही उत्पादन का उत्पादन, अर्थात्:

main: # @main
  push rax
  mov edi, offset .Lstr
  call puts
.Lstr:
  .asciz "begin"

मैं इसे C11 में अनुमति के अनुसार पढ़ने का कोई तरीका नहीं देखता, जो केवल कहता है:

6.8.6.1 (2) ए goto कथन फ़ंक्शन में नामित लेबल द्वारा उपसर्ग किए गए बयान के लिए बिना शर्त कूद का कारण बनता है।

जैसा कि goto"पुनरावृत्ति कथन" नहीं है (6.8.5 सूचियाँ while, doऔरfor ) विशेष "समाप्ति-ग्रहण" के बारे में कुछ भी नहीं है, लेकिन आप उन्हें पढ़ना चाहते हैं।

मूल प्रश्न के गॉडबोल्ट लिंक कंपाइलर x86-64 क्लैंग 9.0.0 है और झंडे हैं -g -o output.s -mllvm --x86-asm-syntax=intel -S --gcc-toolchain=/opt/compiler-explorer/gcc-9.2.0 -fcolor-diagnostics -fno-crash-diagnostics -O2 -std=c11 example.c

दूसरों के साथ जैसे x86-64 GCC 9.2 में आपको बहुत अच्छी तरह से परिपूर्ण मिलता है:

.LC0:
  .string "begin"
main:
  sub rsp, 8
  mov edi, OFFSET FLAT:.LC0
  call puts
.L2:
  jmp .L2

झंडे: -g -o output.s -masm=intel -S -fdiagnostics-color=always -O2 -std=c11 example.c


एक अनुरूप कार्यान्वयन क्रियान्वयन के समय या सीपीयू चक्रों पर एक अनियोजित अनुवाद की सीमा हो सकती है जो कि यदि पार हो गई है, या यदि कोई प्रोग्राम इनपुट सीमा से अधिक अपरिहार्य है, तो मनमाना व्यवहार हो सकता है। इस तरह की चीज़ें मानक के अधिकार क्षेत्र के बाहर, कार्यान्वयन की गुणवत्ता का मुद्दा हैं। यह अजीब प्रतीत होगा कि खराब गुणवत्ता के कार्यान्वयन का उत्पादन करने के लिए क्लैंग के अनुचर अपने अधिकार पर इतना आग्रह करेंगे, लेकिन मानक इसकी अनुमति नहीं देता है।
सुपरकैट

2
@supercat टिप्पणी के लिए धन्यवाद ... अनुवाद की सीमा को पार करने के अलावा, अनुवाद के चरण को विफल करने और निष्पादित करने से इंकार करने के अलावा और कुछ क्यों? इसके अलावा: " 5.1.1.3 डायग्नोस्टिक्स एक अनुरूप कार्यान्वयन का उत्पादन करेगा ... डायग्नोस्टिक संदेश ... यदि प्रीप्रोसेसिंग अनुवाद इकाई या अनुवाद इकाई में किसी भी वाक्यविन्यास नियम या बाधा का उल्लंघन होता है ..."। मैं यह नहीं देख सकता कि निष्पादन चरण में कैसे गलत व्यवहार कभी भी अनुरूप हो सकता है।
जोनाथनो

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

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

1
मानक "निष्पादन सीमा" का कोई संदर्भ नहीं देता है जो मुझे मिल सकता है। समारोह कॉल नेस्टिंग चीज़ें आम तौर पर ढेर आवंटन द्वारा नियंत्रित किया जाता है, लेकिन एक अनुरूप कार्यान्वयन कि सीमा 16 की गहराई हर कार्य के 16 प्रतियां बना सकते हैं करने के लिए कॉल कार्य करते है, और को कॉल किया bar()भीतर foo()से एक फोन के रूप में संसाधित किया जा __1fooकरने के लिए __2bar, से __2fooकरने के लिए __3bar, आदि और से __16fooकरने के लिए __launch_nasal_demonsहै, जो तब सभी स्वत: वस्तुओं स्थिर आवंटित किया जाना है, और क्या होगा की अनुमति होगी आम तौर पर एक अनुवाद सीमा में एक "रन-टाइम" सीमा।
सुपरकैट

5

मैं शैतान के वकील की भूमिका निभाऊंगा और तर्क दूंगा कि मानक एक अनंत लूप को अनुकूलित करने से एक संकलक को स्पष्ट रूप से मना नहीं करता है।

एक पुनरावृत्ति कथन जिसका नियंत्रण अभिव्यक्ति एक स्थिर अभिव्यक्ति नहीं है, 156) जो कोई इनपुट / आउटपुट संचालन नहीं करता है, अस्थिर वस्तुओं तक नहीं पहुंचता है, और अभिव्यक्ति को नियंत्रित करने, या (के मामले में) अपने शरीर में कोई सिंक्रनाइज़ेशन या परमाणु संचालन नहीं करता है कथन) इसकी अभिव्यक्ति -3, को समाप्त करने के कार्यान्वयन से माना जा सकता है।

चलो यह पार्स। एक पुनरावृत्ति कथन जो कुछ मानदंडों को पूरा करता है उसे समाप्त करने के लिए ग्रहण किया जा सकता है:

if (satisfiesCriteriaForTerminatingEh(a_loop)) 
    if (whatever_reason_or_just_because_you_feel_like_it)
         assumeTerminates(a_loop);

यह इस बारे में कुछ नहीं कहता कि क्या होता है यदि मानदंड संतुष्ट नहीं हैं और यह मानते हुए कि एक लूप समाप्त हो सकता है तब भी स्पष्ट रूप से मना नहीं किया जाता है जब तक कि मानक के अन्य नियम नहीं देखे जाते हैं।

do { } while(0)या while(0){}सभी पुनरावृत्ति विवरण (लूप) के बाद हैं जो उन मानदंडों को पूरा नहीं करते हैं जो एक संकलक को केवल एक कगार पर मानने की अनुमति देते हैं जिसे वे समाप्त करते हैं और फिर भी वे स्पष्ट रूप से समाप्त करते हैं।

लेकिन क्या कंपाइलर सिर्फ ऑप्टिमाइज़ while(1){}कर सकता है?

5.1.2.3p4 कहता है:

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

यह अभिव्यक्ति का उल्लेख करता है, न कि कथन, इसलिए यह 100% ठोस नहीं है, लेकिन यह निश्चित रूप से कॉल की अनुमति देता है:

void loop(void){ loop(); }

int main()
{
    loop();
}

छोड़ दिया जाना। दिलचस्प है, क्लैंग इसे छोड़ देता है, और जीसीसी नहीं करता है


"यह इस बारे में कुछ नहीं कहता कि क्या होता है अगर मानदंड संतुष्ट नहीं होते हैं" लेकिन यह करता है, 6.8.5.1 जबकि कथन: "नियंत्रण अभिव्यक्ति का मूल्यांकन लूप बॉडी के प्रत्येक निष्पादन से पहले होता है।" बस। यह एक मूल्य संगणना (स्थिर अभिव्यक्ति का) है, यह सार मशीन 5.1.2.3 के नियम के अंतर्गत आता है जो शब्द मूल्यांकन को परिभाषित करता है: " सामान्य रूप से एक अभिव्यक्ति के मूल्यांकन में मूल्य संगणना और दुष्प्रभाव की दीक्षा दोनों शामिल हैं।" और एक ही अध्याय के अनुसार, इस तरह के सभी मूल्यांकन शब्दावलियों द्वारा निर्दिष्ट के रूप में अनुक्रमित और मूल्यांकन किए जाते हैं।
लुंडिन

1
@ लुंडिन तो मूल्यांकन के साथ अंतर्संबंधित मूल्यांकन while(1){}का एक अनंत क्रम है , लेकिन मानक में यह कहाँ कहता है कि उन मूल्यांकनों को नॉनज़रो समय लेने की आवश्यकता है ? जीसीसी व्यवहार अधिक उपयोगी है, मुझे लगता है, क्यूज़ आपको भाषा के बाहर मेमोरी एक्सेस या ट्रिक्स से युक्त ट्रिक्स की आवश्यकता नहीं है। लेकिन मुझे यकीन नहीं है कि मानक इस अनुकूलन को क्लैंग में मना करते हैं। यदि गैर-अपनाने योग्य बनाने का इरादा है, तो इसके बारे में स्पष्ट होना चाहिए और अनंत लूपिंग को 5.1.2.3p2 में एक अवलोकन योग्य प्रभाव के रूप में सूचीबद्ध किया जाना चाहिए। 1{}while(1){}
PSkocik

1
मुझे लगता है कि यह निर्दिष्ट है, यदि आप 1स्थिति को मूल्य गणना के रूप में मानते हैं । निष्पादन समय बात नहीं है - क्या मायने रखती है क्या है while(A){} B;हो सकता है नहीं दूर पूरी तरह से अनुकूलित किया जाना, के लिए अनुकूल नहीं B;है और के लिए नहीं फिर से अनुक्रम B; while(A){}। सी 11 सार मशीन, जोर मेरा बोली के लिए: "भाव ए और बी के मूल्यांकन के बीच एक दृश्य बिंदु की उपस्थिति का तात्पर्य है कि हर मूल्य गणना और पक्ष प्रभाव एक साथ जुड़े हर मूल्य गणना से पहले अनुक्रम है और पक्ष प्रभाव बी के साथ जुड़े ।" मूल्य का Aस्पष्ट रूप से उपयोग किया जाता है (लूप द्वारा)।
लंडिन

2
+1 भले ही मुझे ऐसा लगता है कि "निष्पादन बिना किसी आउटपुट के अनिश्चित काल तक लटका रहता है" "साइड-इफ़ेक्ट" की किसी भी परिभाषा में "साइड-इफ़ेक्ट" है जो समझ में आता है और वैक्यूम में सिर्फ मानक से परे उपयोगी है, यह समझाने में मदद करता है वह मानसिकता जिससे यह किसी के लिए समझ में आता है।
20

1
"एक अनंत लूप का अनुकूलन" के पास : यह पूरी तरह से स्पष्ट नहीं है कि "यह" मानक या संकलक को संदर्भित करता है - शायद रेफरेज़? यह देखते हुए कि "हालांकि यह शायद" होना चाहिए और "हालांकि यह नहीं होना चाहिए" , यह शायद मानक है कि "यह" संदर्भित करता है।
पीटर मोर्टेंसन

2

मुझे यकीन हो गया है कि यह सिर्फ एक पुराना पुराना बग है। मैं अपने परीक्षण नीचे और विशेष रूप से मानक समिति में चर्चा का संदर्भ कुछ तर्क के लिए छोड़ता हूं जो पहले मेरे पास था।


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

व्यवहार अतिरिक्त अजीब है - यह अंतिम प्रिंट निकालता है, इसलिए अनंत लूप को "देखकर", लेकिन फिर लूप से छुटकारा भी मिलता है।

यह और भी बुरा है जहाँ तक मैं बता सकता हूँ। इनलाइन निकालने से हमें मिलती है:

die: # @die
.LBB0_1: # =>This Inner Loop Header: Depth=1
  jmp .LBB0_1
main: # @main
  push rax
  mov edi, offset .Lstr
  call puts
.Lstr:
  .asciz "begin"

इसलिए फ़ंक्शन बनाया जाता है, और कॉल को अनुकूलित किया जाता है। यह उम्मीद से भी अधिक लचीला है:

#include <stdio.h>

void die(int x) {
    while(x);
}

int main() {
    printf("begin\n");
    die(1);
    printf("unreachable\n");
}

फ़ंक्शन के लिए एक बहुत ही गैर-इष्टतम विधानसभा में परिणाम होता है, लेकिन फ़ंक्शन कॉल को फिर से अनुकूलित किया जाता है! और भी बदतर:

void die(x) {
    while(x++);
}

int main() {
    printf("begin\n");
    die(1);
    printf("unreachable\n");
}

मैंने एक स्थानीय चर जोड़ने और इसे बढ़ाने, एक सूचक का उपयोग करने, gotoआदि का उपयोग करने के साथ अन्य परीक्षण का एक गुच्छा बनाया ... इस बिंदु पर मैं हार मानूंगा। अगर आपको क्लैंग का उपयोग करना चाहिए

static void die() {
    int volatile x = 1;
    while(x);
}

काम करता है। यह अनुकूलन (स्पष्ट रूप से) पर बेकार है, और निरर्थक फाइनल में छोड़ देता हैprintf । कम से कम कार्यक्रम रुकता नहीं है। शायद जीसीसी आखिर क्यों?

परिशिष्ट

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

अगर मैं सही पढ़ता हूं तो हेक एन 1528 के पास अपरिभाषित व्यवहार है। विशेष रूप से

ऐसा करने के लिए एक प्रमुख मुद्दा यह है कि यह एक संभावित गैर-समाप्ति लूप में कोड को स्थानांतरित करने की अनुमति देता है

यहाँ से मुझे लगता है कि यह केवल एक चर्चा में विकसित हो सकता है कि हम क्या चाहते हैं (अपेक्षित?) इसके बजाय जो अनुमति है।


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
भार्गव राव

पुन "सादे सभी बग" : क्या आपका मतलब है " सादे पुराने बग" ?
पीटर मोर्टेंसन

@PeterMortensen "ओले" मेरे साथ भी ठीक होगा।
कबानुस

2

ऐसा लगता है कि यह क्लैग कंपाइलर में एक बग है। यदि die()फ़ंक्शन पर स्थिर फ़ंक्शन होने की कोई बाध्यता नहीं है , तो इसे दूर करें staticऔर इसे बनाएं inline:

#include <stdio.h>

inline void die(void) {
    while(1)
        ;
}

int main(void) {
    printf("begin\n");
    die();
    printf("unreachable\n");
}

यह क्लैंग कंपाइलर के साथ संकलित होने पर अपेक्षित है और पोर्टेबल होने के साथ-साथ काम कर रहा है।

कंपाइलर एक्सप्लोरर (godbolt.org) - 9.0.0-O3 -std=c11 -pedantic-errors

main:                                   # @main
        push    rax
        mov     edi, offset .Lstr
        call    puts
.LBB0_1:                                # =>This Inner Loop Header: Depth=1
        jmp     .LBB0_1
.Lstr:
        .asciz  "begin"

किस बारे में static inline?
एसएस ऐनी

1

निम्नलिखित मेरे लिए काम करता है:

#include <stdio.h>

__attribute__ ((optnone))
static void die(void) {
    while (1) ;
}

int main(void) {
    printf("begin\n");
    die();
    printf("unreachable\n");
}

पर godbolt

स्पष्ट रूप से क्लैंग को यह बताने का विकल्प नहीं है कि एक फ़ंक्शन एक अनंत लूप का उत्सर्जन करता है जैसा कि अपेक्षित है। उम्मीद है कि केवल उन सभी को बंद करने के बजाय विशेष रूप से अनुकूलन को अक्षम करने का एक तरीका है। क्लैंग अभी भी दूसरे के लिए कोड का उत्सर्जन करने से इनकार करता है printf, हालांकि। इसे करने के लिए मजबूर करने के लिए, मुझे कोड को और संशोधित करना होगाmain :

volatile int x = 0;
if (x == 0)
    die();

ऐसा लगता है कि आपको अपने अनंत लूप फ़ंक्शन के लिए अनुकूलन को अक्षम करना होगा, फिर सुनिश्चित करें कि आपके अनंत लूप को सशर्त रूप से कहा जाता है। वास्तविक दुनिया में, वैसे भी लगभग हमेशा ही ऐसा होता है।


1
printfयदि लूप वास्तव में हमेशा के लिए चला जाता है, तो दूसरे के लिए यह आवश्यक नहीं है , क्योंकि उस स्थिति में दूसरा printfवास्तव में पहुंच से बाहर है और इसलिए इसे हटाया जा सकता है। (क्लैंग की त्रुटि अप्रतिस्पर्धीता का पता लगाने और फिर लूप को हटाने जैसे कि अगम्य कोड तक पहुंचने पर दोनों में है)।
nnonneo

GCC दस्तावेज़ __attribute__ ((optimize(1))), लेकिन क्लैग इसे अनदेखा करता है: godbolt.org/z/4ba2HMgcc.gnu.org/oniltocs/gcc/Common-Function-Attributes.html
पीटर कॉर्ड्स

0

एक अनुरूप कार्यान्वयन, और कई व्यावहारिक लोग कर सकते हैं, एक सीमा तक मनमाना सीमाएं लागू कर सकते हैं कि कोई कार्यक्रम कितनी देर तक निष्पादित हो सकता है या कितने निर्देशों को निष्पादित करेगा, और मनमाने ढंग से व्यवहार करता है यदि उन सीमाओं का उल्लंघन किया जाता है - या "के रूप में" नियम के तहत। - यदि यह निर्धारित करता है कि वे अनिवार्य रूप से उल्लंघन किया जाएगा। बशर्ते कि एक कार्यान्वयन सफलतापूर्वक कम से कम एक प्रोग्राम को संसाधित कर सकता है जो एन 1570 5.2.4.1 में सूचीबद्ध सभी सीमाओं का उपयोग करता है, बिना किसी अनुवाद सीमा के, सीमाओं के अस्तित्व, जिस हद तक वे प्रलेखित हैं, और उन्हें पार करने के प्रभाव, मानक के अधिकार क्षेत्र से बाहर कार्यान्वयन के सभी मुद्दों की गुणवत्ता।

मुझे लगता है कि मानक का इरादा काफी स्पष्ट है कि संकलक को यह नहीं मानना ​​चाहिए कि while(1) {}कोई साइड-इफेक्ट वाला लूप नहीं है और न ही breakबयान समाप्त होंगे। कुछ लोग जो सोच सकते हैं, उसके विपरीत, मानक के लेखक संकलक लेखकों को बेवकूफ या अप्रिय होने के लिए आमंत्रित नहीं कर रहे थे। एक अनुरूप कार्यान्वयन किसी भी कार्यक्रम को समाप्त करने का निर्णय लेने के लिए उपयोगी हो सकता है, जो अगर बाधित नहीं होता है, तो ब्रह्मांड में परमाणुओं की तुलना में अधिक साइड-इफ़ेक्ट फ्री निर्देशों को निष्पादित करें, लेकिन एक गुणवत्ता कार्यान्वयन किसी भी धारणा के आधार पर ऐसी कार्रवाई नहीं करनी चाहिए। समाप्ति, बल्कि इस आधार पर कि ऐसा करना उपयोगी हो सकता है, और (क्लैंग के व्यवहार के विपरीत) बेकार से भी बदतर नहीं होगा।


-2

लूप का कोई साइड-इफेक्ट नहीं है, और इसलिए इसे बाहर अनुकूलित किया जा सकता है। पाश प्रभावी रूप से काम की शून्य इकाइयों की पुनरावृत्तियों की एक अनंत संख्या है। यह गणित और तर्क में अपरिभाषित है और मानक यह नहीं कहते हैं कि क्या कार्यान्वयन को अनंत प्रकार की चीजों को पूरा करने की अनुमति दी जाती है यदि प्रत्येक चीज को शून्य समय में किया जा सकता है। क्लैंग की व्याख्या अनंत काल को अनंत के बजाय शून्य के रूप में मानने में पूरी तरह से उचित है। मानक यह नहीं कहता है कि क्या अनंत लूप समाप्त हो सकता है या नहीं, यदि छोरों में सभी काम वास्तव में पूरा हो गया है।

संकलक को कुछ भी बाहर का अनुकूलन करने की अनुमति है जो मानक में परिभाषित के रूप में अवलोकन योग्य व्यवहार नहीं है। जिसमें निष्पादन का समय भी शामिल है। इस तथ्य को संरक्षित करने की आवश्यकता नहीं है कि लूप, यदि अनुकूलित नहीं है, तो अनंत समय लगेगा। इसे बदलने की अनुमति है कि बहुत कम समय के लिए - वास्तव में, कि अधिकांश अनुकूलन का बिंदु। आपका पाश अनुकूलित किया गया था।

यहां तक ​​कि अगर क्लैग ने कोड को भली-भांति अनुवादित किया है, तो आप एक अनुकूलन सीपीयू की कल्पना कर सकते हैं जो पिछली पुनरावृत्ति में आधे समय में प्रत्येक पुनरावृत्ति को पूरा कर सकता है। यह शाब्दिक समय में अनंत लूप को पूरा करेगा। क्या इस तरह के एक अनुकूलन सीपीयू मानक का उल्लंघन करता है? यह कहना काफी बेतुका लगता है कि यदि अनुकूलन में बहुत अच्छा है तो एक ऑप्टिमाइज़िंग सीपीयू मानक का उल्लंघन करेगा। एक कंपाइलर का भी यही हाल है।


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
शमूएल ल्यू

4
आपके (आपके प्रोफाइल से) अनुभव को देखते हुए, मैं केवल यह निष्कर्ष निकाल सकता हूं कि यह पोस्ट केवल कंपाइलर की रक्षा के लिए बुरे विश्वास में लिखी गई है। आप गंभीरता से तर्क दे रहे हैं कि कुछ ऐसा जो अनंत समय लेता है, उसे आधे समय में निष्पादित करने के लिए अनुकूलित किया जा सकता है। यह हर स्तर पर हास्यास्पद है और आप जानते हैं कि।
पाइप

@ पिप: मुझे लगता है कि क्लैंग और जीसीसी के रखवाले उम्मीद कर रहे हैं कि मानक का भावी संस्करण उनके संकलकों के व्यवहार को अनुमेय बना देगा, और उन संकलकों के अनुचर यह ढोंग करने में सक्षम होंगे कि इस तरह का बदलाव महज एक दीर्घकालिक दोष का सुधार था। मानक में। उदाहरण के लिए, उन्होंने C89 की कॉमन इनिशियल सीक्वेंस गारंटी का इलाज कैसे किया।
सुपरकैट

@SSAnne: हम्म ... मुझे नहीं लगता है कि सूचक-समानता तुलनाओं के परिणामों से कुछ अकुशल इनफ़ेक्शन जीसीसी और क्लैंग ड्रॉ को रोकना पर्याप्त है।
सुपरकैट

@supercat <s> अन्य लोग हैं।
एसएस ऐनी

-2

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

cc -O0 -std=c11 test.c -o test

1
बिंदु को सक्षम करने वाले अनुकूलन के साथ एक अनंत लूप बनाना है।
एसएस ऐनी

-4

खाली whileलूप का सिस्टम पर कोई दुष्प्रभाव नहीं होता है।

इसलिए क्लैंग इसे हटा देता है। इच्छित व्यवहार को प्राप्त करने के लिए "बेहतर" तरीके हैं जो आपको अपने इरादों के बारे में अधिक स्पष्ट करने के लिए मजबूर करते हैं।

while(1); baadd है।


6
कई एम्बेडेड निर्माणों में, वहाँ की कोई अवधारणा है abort()या exit()। यदि ऐसी स्थिति उत्पन्न होती है जहां कोई फ़ंक्शन निर्धारित करता है कि (शायद स्मृति भ्रष्टाचार के परिणामस्वरूप) निष्पादन लगातार खतरनाक से भी बदतर होगा, तो एम्बेडेड पुस्तकालयों के लिए एक सामान्य डिफ़ॉल्ट व्यवहार एक फ़ंक्शन को लागू करने के लिए है जो एक प्रदर्शन करता है while(1);। अधिक उपयोगी व्यवहार के विकल्प के लिए कंपाइलर के पास विकल्प होना उपयोगी हो सकता है, लेकिन कोई भी कंपाइलर लेखक जो यह पता नहीं लगा सकता है कि इस तरह के एक साधारण निर्माण को कैसे जारी रखा जा सकता है क्योंकि प्रोग्राम के निष्पादन में बाधा जटिल अनुकूलन के साथ भरोसेमंद नहीं है।
सुपरकैट

क्या कोई तरीका है जिससे आप अपने इरादों के बारे में अधिक स्पष्ट हो सकते हैं? ऑप्टिमाइज़र आपके प्रोग्राम को ऑप्टिमाइज़ करने के लिए है, और एक निरर्थक छोरों को हटा दें जो कुछ भी नहीं है जो एक अनुकूलन है। यह वास्तव में गणित की दुनिया और अधिक लागू इंजीनियरिंग दुनिया की अमूर्त सोच के बीच एक दार्शनिक अंतर है।
प्रसिद्ध जैमिस

अधिकांश कार्यक्रमों में उपयोगी क्रियाओं का एक सेट होता है जो उन्हें संभव होने पर प्रदर्शन करना चाहिए, और इससे भी बदतर-बेकार कार्यों का एक सेट जो उन्हें किसी भी परिस्थिति में कभी नहीं करना चाहिए। कई कार्यक्रमों में किसी भी विशेष मामले में स्वीकार्य व्यवहार का एक सेट होता है, जिनमें से एक, यदि निष्पादन समय अवलोकन योग्य नहीं है, तो हमेशा "कुछ मनमाना प्रतीक्षा करें और फिर सेट से कुछ कार्रवाई करें"। यदि प्रतीक्षा के अलावा अन्य सभी क्रियाएं बदतर-से-बेकार कार्यों के सेट में होती हैं, तो सेकंड की संख्या नहीं होगी N जिसके लिए "हमेशा के लिए प्रतीक्षा करें" का अवलोकन अलग होगा ...
सुपरकैट

... "एन + 1 सेकंड प्रतीक्षा करें और फिर कुछ अन्य कार्रवाई करें", इसलिए यह तथ्य कि प्रतीक्षा के अलावा अन्य सहनीय कार्यों का सेट खाली है, अवलोकनीय नहीं होगा। दूसरी ओर, यदि कोड का एक टुकड़ा संभावित कार्यों के सेट से कुछ असहनीय कार्रवाई को हटा देता है, और उन कार्यों में से एक को वैसे भी निष्पादित किया जाता है , जिसे अवलोकनीय माना जाना चाहिए। दुर्भाग्य से, सी और सी ++ भाषा के नियम "मान" शब्द का उपयोग किसी अन्य प्रकार के तर्क या मानव प्रयास के विपरीत एक अजीब तरीके से कर सकते हैं जिसे मैं पहचान सकता हूं।
सुपरकैट

1
@FamousJameis ठीक है, लेकिन क्लैंग सिर्फ लूप को नहीं हटाता है - यह सांख्यिकीय रूप से बाद में अप्राप्य के रूप में सब कुछ का विश्लेषण करता है और एक अवैध निर्देश का उत्सर्जन करता है। यदि आप यह उम्मीद करते हैं कि यह सिर्फ लूप को "हटा" दिया जाए तो यह नहीं है।
nnonneo
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.