_the_answer:
subl $12, %esp
movl $42, %eax
addl $12, %esp
ret
पहला उपन्यास आपके कार्य में उपयोग किए जा सकने वाले चर के लिए जगह बनाने के लिए स्टैक-पॉइंटर को कम करता है। फ़्रेम पॉइंटर के लिए एक स्लॉट का उपयोग किया जा सकता है, उदाहरण के लिए, रिटर्न पता रखने के लिए दूसरा। आपने कहा कि इसे फ्रेम पॉइंटर को छोड़ देना चाहिए। इसका आमतौर पर मतलब है कि फ्रेम फ्रेम को सहेजने/पुनर्स्थापित करने के लिए यह लोड/स्टोर को छोड़ देता है। लेकिन अक्सर कोड इसके लिए स्मृति आरक्षित करेगा। कारण यह है कि यह कोड बनाता है जो ढेर का विश्लेषण बहुत आसान है। स्टैक की ऑफसेट को न्यूनतम चौड़ाई देना आसान है और इसलिए आप जानते हैं कि आप पहले स्थानीय चर स्लॉट पर पहुंचने के लिए हमेशा एफपी + 0x12 तक पहुंच सकते हैं, भले ही आप फ्रेम पॉइंटर को सहेजना छोड़ दें।
ठीक है, eax
x86 पर कॉलर को वापसी मूल्य को संभालने के लिए उपयोग किया जाता है, जहां तक मुझे पता है। और अंतिम addl बस आपके फ़ंक्शन के लिए पहले बनाए गए फ्रेम को नष्ट कर देता है।
कोड जो कार्यों के प्रारंभ और अंत में निर्देश उत्पन्न करता है उसे फ़ंक्शन के "एपिलॉग" और "प्रस्तावना" कहा जाता है। यहाँ क्या मेरी बंदरगाह करता है जब यह जीसीसी में एक समारोह की प्रस्तावना बनाने के लिए किया गया है (यह वास्तविक दुनिया पोर्ट के रूप में तेजी से और संभव के रूप में बहुमुखी होना करने का इरादा के लिए जिस तरह से और अधिक जटिल है):
void eco32_prologue(void) {
int i, j;
/* reserve space for all callee saved registers, and 2 additional ones:
* for the frame pointer and return address */
int regs_saved = registers_to_be_saved() + 2;
int stackptr_off = (regs_saved * 4 + get_frame_size());
/* decrement the stack pointer */
emit_move_insn(stack_pointer_rtx,
gen_rtx_MINUS(SImode, stack_pointer_rtx,
GEN_INT(stackptr_off)));
/* save return adress, if we need to */
if(eco32_ra_ever_killed()) {
/* note: reg 31 is return address register */
emit_move_insn(gen_rtx_MEM(SImode,
plus_constant(stack_pointer_rtx,
-4 + stackptr_off)),
gen_rtx_REG(SImode, 31));
}
/* save the frame pointer, if it is needed */
if(frame_pointer_needed) {
emit_move_insn(gen_rtx_MEM(SImode,
plus_constant(stack_pointer_rtx,
-8 + stackptr_off)),
hard_frame_pointer_rtx);
}
/* save callee save registers */
for(i=0, j=3; i<FIRST_PSEUDO_REGISTER; i++) {
/* if we ever use the register, and if it's not used in calls
* (would be saved already) and it's not a special register */
if(df_regs_ever_live_p(i) &&
!call_used_regs[i] && !fixed_regs[i]) {
emit_move_insn(gen_rtx_MEM(SImode,
plus_constant(stack_pointer_rtx,
-4 * j + stackptr_off)),
gen_rtx_REG(SImode, i));
j++;
}
}
/* set the new frame pointer, if it is needed now */
if(frame_pointer_needed) {
emit_move_insn(hard_frame_pointer_rtx,
plus_constant(stack_pointer_rtx, stackptr_off));
}
}
मैं कुछ छोड़े गए कोड जो अन्य मुद्दों से संबंधित है, मुख्य रूप से जीसीसी को बताते हुए अपवाद हैंडलिंग के लिए महत्वपूर्ण निर्देश क्या हैं (यानी जहां फ्रेम पॉइंटर संग्रहीत किया जाता है)। खैर, कैली सहेजे गए रजिस्टर्स वे हैं जिन्हें कॉलर को कॉल से पहले सहेजने की आवश्यकता नहीं होती है। बुलाया गया फ़ंक्शन आवश्यकतानुसार उन्हें सहेजने/पुनर्स्थापित करने की परवाह करता है। जैसा कि आप पहली पंक्तियों में देखते हैं, हम हमेशा वापसी पते और फ्रेम सूचक के लिए स्थान आवंटित करते हैं। वह जगह केवल कुछ बाइट्स है और इससे कोई फर्क नहीं पड़ता। लेकिन जब आवश्यक हो तो हम केवल स्टोर/भार उत्पन्न करते हैं। अंत में "हार्ड" फ्रेम सूचक "असली" फ्रेम सूचक रजिस्टर है नोट करें। कुछ जीसीसी आंतरिक कारणों से यह आवश्यक है। "Frame_pointer_needed" ध्वज जीसीसी द्वारा निर्धारित किया जाता है, जब भी मैं फ्रेम-पॉइंटर संग्रहीत नहीं कर सकता। कुछ मामलों के लिए, इसे संग्रहीत किया जाना चाहिए, उदाहरण के लिए जब alloca
(यह गतिशील रूप से स्टैकपोइंटर बदलता है) का उपयोग किया जाता है। जीसीसी सभी के बारे में परवाह करता है।ध्यान दें कि मैंने उस कोड को लिखने के बाद कुछ समय दिया है, इसलिए मुझे उम्मीद है कि ऊपर दी गई अतिरिक्त टिप्पणियां सभी गलत नहीं हैं :)
4.0.1 (ऐप्पल इंक 5488 का निर्माण)। मान लीजिए कि यह एक बग है। –
@ माइक, एक बग नहीं। कोड ठीक काम करता है क्योंकि एसयूएल को एडीएल द्वारा उलट दिया जाता है। यह अक्षम है लेकिन निश्चित रूप से एक बग नहीं है। – paxdiablo
कोई बग नहीं हो सकता है, यह हो सकता है कि 4.3 यह समझने में स्मार्ट हो कि कौन से निर्देश निकालने के लिए सुरक्षित हैं। – flussence