जीसीसी विकल्प -fomit- फ्रेम-पॉइंटर को समझने की कोशिश कर रहा है


80

मैंने Google से मुझे gccविकल्प का अर्थ बताने के लिए कहा -fomit-frame-pointer, जो मुझे नीचे दिए गए कथन पर पुनर्निर्देशित करता है।

-फोमिट-फ्रेम-पॉइंटर

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

प्रत्येक फ़ंक्शन के मेरे ज्ञान के अनुसार, सभी स्थानीय चर और कुछ और जानकारी रखने के लिए प्रक्रिया मेमोरी के ढेर में एक सक्रियण रिकॉर्ड बनाया जाएगा। मुझे उम्मीद है कि यह फ़्रेम पॉइंटर का अर्थ किसी फ़ंक्शन के सक्रियण रिकॉर्ड का पता है।

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


5
डिबग करने से कोड का केवल एक क्रैश डंप जो इस विकल्प के साथ संकलित किया गया था, आपको इस विकल्प को अपने अल्फाइल से बाहर निकालने के लिए पर्याप्त होगा। यह किसी भी निर्देश btw को दूर नहीं करता है, यह सिर्फ अनुकूलक को भंडारण के लिए काम करने के लिए एक और रजिस्टर देता है।
हंस पैजेंट

1
@ हंसपेंट दरअसल, यह रिलीज बिल्ड के लिए काफी उपयोगी है। मेकफाइल में दो लक्ष्य होना - Releaseऔर Debugवास्तव में बहुत उपयोगी है, इस विकल्प को एक उदाहरण के रूप में लें।
कोटसकस

3
@VladislavToncharov मुझे लगता है कि आपको अपने- Releaseनिर्माण को चलाने वाले ग्राहक से क्रैश डंप को हटाने की आवश्यकता नहीं है?
एंड्रियास मैग्यूसन

जवाबों:


60

अधिकांश छोटे कार्यों को फ्रेम पॉइंटर की आवश्यकता नहीं होती है - बड़े कार्यों को एक की आवश्यकता होती है।

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

मुझे नहीं लगता कि आपको कोडिंग के लिए अपनी रणनीति के हिस्से के रूप में "कार्य करने की कोशिश नहीं करनी चाहिए, फ्रेम पॉइंटर" - जैसा कि मैंने कहा, सरल कार्यों की उन्हें आवश्यकता नहीं है, इसलिए उपयोग करें -fomit-frame-pointer, और आपको एक और रजिस्टर उपलब्ध होगा। रजिस्टर आवंटनकर्ता के लिए, और कार्यों के लिए प्रवेश / निकास पर 1-3 निर्देश सहेजें। यदि आपके फ़ंक्शन को फ़्रेम पॉइंटर की आवश्यकता है, तो यह इसलिए है क्योंकि कंपाइलर तय करता है कि फ़्रेम पॉइंटर का उपयोग न करने से बेहतर विकल्प है। यह फ्रेम पॉइंटर के बिना फ़ंक्शन करने का लक्ष्य नहीं है, यह एक कोड है जो सही और तेज़ दोनों तरह से काम करता है।

ध्यान दें कि "फ्रेम पॉइंटर न होने" को बेहतर प्रदर्शन देना चाहिए, लेकिन यह कुछ जादू की गोली नहीं है जो बहुत सुधार देता है - विशेष रूप से x86-64 पर नहीं, जिसमें पहले से ही 16 रजिस्टर हैं। 32-बिट x86 पर, क्योंकि इसमें केवल 8 रजिस्टर हैं, जिनमें से एक स्टैक पॉइंटर है, और दूसरे को फ्रेम पॉइंटर के रूप में लेने का मतलब है कि 25% रजिस्टर-स्पेस लिया जाता है। इसे बदलने के लिए 12.5% ​​काफी सुधार है। बेशक, 64-बिट के लिए संकलन करने से काफी मदद मिलेगी।


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

3
हां, इसलिए, यह मानते हुए कि हम उपयोग नहीं कर रहे हैं alloca[कौन करता है? - मुझे यकीन है कि मैंने कभी भी ऐसा कोड नहीं लिखा है जो उपयोग करता है alloca] या variable size local arrays[जो कि आधुनिक रूप है alloca], फिर भी कंपाइलर MAY तय करते हैं कि फ्रेम-पॉइंटर का उपयोग करना एक बेहतर विकल्प है - क्योंकि कंपाइलर्स को आँख बंद करके नहीं लिखने के लिए लिखा जाता है। दिए गए विकल्प, लेकिन आपको सर्वोत्तम विकल्प प्रदान करते हैं।
मैट पीटरसन

6
@MatsPetersson VLA इससे अलग है alloca: जैसे ही आप उस दायरे को छोड़ते हैं, जिसमें वे घोषित किए जाते हैं, छोड़ दिया जाता है, जबकि allocaअंतरिक्ष केवल तभी मुक्त होता है जब आप फ़ंक्शन छोड़ते हैं। allocaमुझे लगता है कि यह VLA का अनुसरण करने में बहुत आसान है।
जेन्स गस्टेड 21

35
यह शायद ध्यान देने योग्य बात है कि g86 -fomit-frame-pointerx86-64 के लिए डिफ़ॉल्ट रूप से है।
२२:०३ बजे

5
@JensGustedt, समस्या यह नहीं है कि जब उन्हें फेंक दिया जाता है, तो समस्या यह है कि उनका आकार (जैसे alloca'एड स्पेस) संकलन समय पर अज्ञात है । आमतौर पर कंपाइलर स्थानीय चर का पता प्राप्त करने के लिए फ्रेम पॉइंटर का उपयोग करेगा, यदि स्टैक फ्रेम का आकार नहीं बदलता है, तो यह स्टैक पॉइंटर से एक निश्चित ऑफसेट पर उन्हें पता लगा सकता है।
वॉनब्रांड

15

यह सभी इंटेल प्लेटफार्मों पर बीपी / ईबीपी / आरबीपी रजिस्टर के बारे में है। स्टैक सेगमेंट के लिए यह रजिस्टर डिफॉल्ट करता है (स्टैक सेगमेंट तक पहुंचने के लिए एक विशेष उपसर्ग की आवश्यकता नहीं है)।

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

(स्रोत - http://css.csail.mit.edu/6.858/2017/readings/i386/s02_03.htm )

चूंकि अधिकांश 32-बिट प्लेटफॉर्म पर, डेटा सेगमेंट और स्टैक सेगमेंट समान हैं, इसलिए स्टैक के साथ EBP / RBP का यह जुड़ाव अब कोई समस्या नहीं है। तो 64-बिट प्लेटफार्मों पर है: 2003 में AMD द्वारा प्रस्तुत x86-64 आर्किटेक्चर, ने 64-बिट मोड में विभाजन के लिए समर्थन को काफी हद तक गिरा दिया है: खंड के चार रजिस्टर: CS, SS, DS और ES को 0 पर मजबूर किया जाता है। । X86 32-बिट और 64-बिट प्लेटफ़ॉर्म की इन परिस्थितियों का अनिवार्य रूप से मतलब है कि ईबीपी / आरबीपी रजिस्टर का उपयोग मेमोरी को एक्सेस करने वाले प्रोसेसर निर्देशों में बिना किसी उपसर्ग के किया जा सकता है।

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

"यह फ्रेम पॉइंटर्स को बचाने, सेट करने और पुनर्स्थापित करने के निर्देशों से बचा जाता है" का अर्थ प्रत्येक फ़ंक्शन के प्रवेश पर निम्नलिखित कोड से बचने से है:

या enterअनुदेश, जो इंटेल 80286 और 80386 प्रोसेसर पर बहुत उपयोगी था।

फ़ंक्शन वापसी से पहले, निम्न कोड का उपयोग किया जाता है:

या leaveनिर्देश।

डिबगिंग टूल स्टैक डेटा को स्कैन कर सकते हैं और लोकेट करते समय इन पुश किए गए ईबीपी रजिस्टर डेटा का उपयोग कर सकते हैं call sites, अर्थात फ़ंक्शन के नाम और उन्हें क्रमबद्ध रूप से बुलाया जाने वाले तर्कों को प्रदर्शित करने के लिए।

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

इसलिए प्रोग्रामर के लिए यह समझना बहुत महत्वपूर्ण है कि कंपाइलर विकल्पों के संदर्भ में स्टैक फ्रेम क्या है - क्योंकि कंपाइलर इस कोड को जेनरेट करने के लिए नियंत्रित कर सकता है या नहीं।

कुछ मामलों में, स्टैक फ्रेम (रूटीन के लिए प्रवेश और निकास कोड) को कंपाइलर द्वारा छोड़ा जा सकता है, और चर को सीधे आधार पॉइंटर (बीपी /) के बजाय स्टैक पॉइंटर (एसपी / ईएसपी / आरएसपी) के माध्यम से एक्सेस किया जाएगा। ईएसपी / आरएसपी)। एक कंपाइलर के लिए कुछ कार्यों के लिए स्टैक फ़्रेम को छोड़ना अलग हो सकता है, उदाहरण के लिए: (1) फ़ंक्शन एक पत्ती फ़ंक्शन है (यानी एक अंत-इकाई जो अन्य कार्यों को कॉल नहीं करती है); (2) कोई अपवाद उपयोग नहीं किया जाता है; (3) स्टैक पर आउटगोइंग मापदंडों के साथ कोई रूटीन नहीं कहा जाता है; (4) फ़ंक्शन का कोई पैरामीटर नहीं है।

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

सामान्यताओं से वापस विशिष्टताओं पर लौटना: यदि आप -fomit-frame-pointerजीसीसी संकलक विकल्प का उपयोग करेंगे , तो आप रूटीन के लिए प्रवेश और निकास कोड दोनों पर जीत सकते हैं, और एक अतिरिक्त रजिस्टर होने पर (जब तक कि यह पहले से ही डिफ़ॉल्ट रूप से चालू न हो या दूसरे के साथ निहित हो) विकल्प, इस मामले में, आप पहले से ही ईबीपी / आरबीपी रजिस्टर का उपयोग करने के लाभ से लाभान्वित हो रहे हैं और इस विकल्प को स्पष्ट रूप से निर्दिष्ट करके कोई अतिरिक्त लाभ प्राप्त नहीं किया जाएगा यदि यह पहले से ही निहित है)। कृपया ध्यान दें, हालांकि, 16-बिट और 32-बिट मोड में, बीपी रजिस्टर में 8-बिट भागों को एक्सेस करने की क्षमता नहीं है जैसे कि AX (AL और AH) है।

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

कृपया यह भी जान लें कि डिबगिंग या ऑप्टिमाइज़ेशन से संबंधित अन्य संकलक विकल्प, -fomit-frame-pointerविकल्प को चालू या बंद कर सकते हैं।

मुझे gcc.gnu.org पर कोई आधिकारिक जानकारी नहीं मिली है कि -fomit-frame-pointer x86 प्लेटफार्मों पर अन्य विकल्प कैसे प्रभावित करते हैं , https://gcc.gnu.org/oniltocs/gcc-3.4.4/gcc/Optimize-Options.html केवल निम्नलिखित बताता है:

-O ऐसी मशीनों पर भी ऑन-ऑफोमेट-फ्रेम-पॉइंटर को चालू करता है जहां ऐसा करने से डिबगिंग में हस्तक्षेप नहीं होता है।

तो यह प्रति से प्रलेखन से स्पष्ट नहीं है कि क्या -fomit-frame-pointerचालू किया जाएगा यदि आप -Ox86 प्लेटफॉर्म पर एक ही विकल्प के साथ संकलन करते हैं । यह अनुभवजन्य रूप से परीक्षण किया जा सकता है, लेकिन इस मामले में जीसीसी डेवलपर्स से कोई प्रतिबद्धता नहीं है कि भविष्य में इस विकल्प के व्यवहार को नोटिस के बिना नहीं बदला जाए।

हालांकि, पीटर कॉर्डेस ने टिप्पणियों में बताया है कि -fomit-frame-pointerx86-16 प्लेटफार्मों और x86-32 / 64 प्लेटफार्मों के बीच डिफ़ॉल्ट सेटिंग्स के लिए एक अंतर है।

यह विकल्प - -fomit-frame-pointer- इंटेल C ++ कम्पाइलर 15.0 के लिए भी प्रासंगिक है , न केवल GCC के लिए:

इंटेल कंपाइलर के लिए, इस विकल्प में एक उपनाम है /Oy

यहाँ इंटेल ने इसके बारे में क्या लिखा है:

ये विकल्प निर्धारित करते हैं कि ईबीपी का उपयोग अनुकूलन में एक सामान्य-उद्देश्य रजिस्टर के रूप में किया जाता है या नहीं। विकल्प -fomit- फ्रेम-पॉइंटर और / Oy इस उपयोग की अनुमति देते हैं। विकल्प -fno-omit- फ्रेम-पॉइंटर और / Oy- इसे अस्वीकृत करें।

कुछ डीबगर्स ईबीपी को स्टैक फ्रेम पॉइंटर के रूप में उपयोग करने की उम्मीद करते हैं, और जब तक ऐसा नहीं होता तब तक स्टैक बैकट्रेस का उत्पादन नहीं किया जा सकता है। -Fno-omit- फ्रेम-पॉइंटर और / Oy- ऑप्शन कंपाइलर को निर्देश देते हैं कि वह कोड बनाए जो ईबीपी को सभी कार्यों के लिए स्टैक फ्रेम पॉइंटर के रूप में बनाए रखता है और उपयोग करता है ताकि एक डीबगर अभी भी निम्नलिखित किए बिना स्टैक बैकट्रेस का उत्पादन कर सके:

Forfno-omit-फ़्रेम-पॉइंटर: -O0 के साथ ऑप्टिमाइज़ेशन बंद करना / Oy के लिए: - बंद करना / O1, / O2, या / O3 ऑप्टिमाइज़ेशन -fno-omit-फ़्रेम-पॉइंटर विकल्प सेट है जब आप विकल्प निर्दिष्ट करते हैं - O0 या -g विकल्प। जब आप विकल्प -O1, -O2, या -O3 निर्दिष्ट करते हैं -fomit- फ्रेम-पॉइंटर विकल्प सेट किया जाता है।

जब आप / O1, / O2, या / O3 विकल्प निर्दिष्ट करते हैं तो / Oy विकल्प सेट किया जाता है। जब आप / Od विकल्प निर्दिष्ट करते हैं तो विकल्प / Oy- सेट किया जाता है।

-Fno-omit-फ़्रेम-पॉइंटर या / Oy- विकल्प का उपयोग करने से उपलब्ध सामान्य-उद्देश्य रजिस्टरों की संख्या 1 से कम हो जाती है और परिणामस्वरूप थोड़ा कम कुशल कोड हो सकता है।

नोट लिनक्स * सिस्टम के लिए: वर्तमान में GCC 3.2 अपवाद हैंडलिंग के साथ एक समस्या है। इसलिए, इंटेल कंपाइलर इस विकल्प को नजरअंदाज कर देता है जब C ++ के लिए GCC 3.2 स्थापित किया जाता है और अपवाद हैंडलिंग चालू होता है (डिफ़ॉल्ट)।

कृपया ध्यान रखें कि उपरोक्त उद्धरण केवल इंटेल C ++ 15 संकलक के लिए प्रासंगिक है, जीसीसी के लिए नहीं।


1
16-बिट कोड, और डीएस के बजाय एसएस के लिए बीपी डिफ़ॉल्ट रूप से, जीसीसी के लिए वास्तव में प्रासंगिक नहीं है। gcc -m16मौजूद है, लेकिन यह एक अजीब विशेष-मामला है जो मूल रूप से 32-बिट कोड बनाता है जो 16-बिट मोड में पूरे स्थान पर उपसर्गों का उपयोग करके चलता है। यह भी ध्यान दें कि -fomit-frame-pointerx86 पर वर्षों के लिए डिफ़ॉल्ट रूप से सक्षम किया गया है -m32, और x86-64 ( -m64) की तुलना में अधिक लंबा है ।
पीटर कॉर्डेस

@PeterCordes - धन्यवाद, मैंने आपके द्वारा उठाए गए मुद्दों के अनुसार संपादन अपडेट किया है।
मैक्सिम मासियुटिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.