2013-02-26 133 views
8

के लिए प्रासंगिक उपयोग वर्तमान में मैं अपने कंप्यूटर संगठन midterm के लिए पढ़ रहा हूं, और मैं स्टैक पॉइंटर और स्टैक को पूरी तरह से समझने की कोशिश कर रहा हूं। मैं इन निम्न तथ्यों कि अवधारणा के चारों ओर पता:

  • यह पिछले बाहर सिद्धांत रूप में पहले इस प्रकार
  • और ढेर करने के लिए कुछ जोड़ने की प्रक्रिया दो चरणों में होता है:

    addi $sp, $sp, -4 
    sw $s0, 0($sp) 
    

मुझे लगता है कि मुझे पूरी तरह से समझने से रोक रहा है कि मैं एक प्रासंगिक, आत्म स्पष्ट स्थिति के साथ नहीं आ सकता जहां मुझे आवश्यकता होगी और/या एक स्टैक पॉइंटर के साथ डेटा का ट्रैक रखना चाहते हैं।

कोई व्यक्ति पूरी तरह से अवधारणा को विस्तारित कर सकता है और मुझे कुछ उपयोगी कोड उदाहरण दे सकता है?

+2

आपको पते के लिए हस्ताक्षरित ऐड/सब का उपयोग करना चाहिए, या यदि ओवरफ्लो है (0x7FFFFFFF और 0x80000000 के बीच की सीमा पार कर रहा है) तो आप अपवाद/जाल को जोखिम दे रहे हैं। –

+0

अभ्यास में हां, लेकिन परीक्षण के प्रयोजनों के लिए मुझे नहीं लगता कि उसे हमें उस सूक्ष्मता को समझने की आवश्यकता है। –

उत्तर

17

स्टैक का एक महत्वपूर्ण उपयोग subroutine कॉल घोंसला है।

प्रत्येक सबराउटिन में उस सबराउटिन के लिए स्थानीय चर का एक सेट हो सकता है। इन चरों को आसानी से स्टैक फ्रेम में स्टैक पर संग्रहीत किया जा सकता है। कुछ कॉलिंग सम्मेलन भी ढेर पर तर्क पारित करते हैं।

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

मान लीजिए आप इस सी टुकड़ा है:

extern void foo(); 
extern int bar(); 
int baz() 
{ 
    int x = bar(); 
    foo(); 
    return x; 
} 

MIPS विधानसभा तो लग सकता है जैसे:

addiu $sp, $sp, -8 # allocate 2 words on the stack 
sw $ra, 4($sp)  # save $ra in the upper one 
jal bar    # this overwrites $ra 
sw $v0, ($sp)  # save returned value (x) 
jal foo    # this overwrites $ra and possibly $v0 
lw $v0, ($sp)  # reload x so we can return it 
lw $ra, 4($sp)  # reload $ra so we can return to caller 
addiu $sp, $sp, 8 # restore $sp, freeing the allocated space 
jr $ra    # return 
6

MIPS बुला सम्मेलन a3 और के माध्यम से रजिस्टर a0 में होने की पहले चार फ़ंक्शन पैरामीटर की आवश्यकता है बाकी, अगर स्टैक पर अधिक हैं। इसके अलावा, रजिस्टरों में पारित होने के बावजूद, पहले चार पैरामीटर के लिए स्टैक पर चार स्लॉट आवंटित करने के लिए फ़ंक्शन कॉलर की भी आवश्यकता होती है।

तो, यदि आप पैरामीटर पांच (और आगे पैरामीटर) तक पहुंचना चाहते हैं, तो आपको sp का उपयोग करने की आवश्यकता है। यदि फ़ंक्शन बदले में अन्य फ़ंक्शंस कॉल करता है और कॉल के बाद इसके पैरामीटर का उपयोग करता है, तो उसे खोने/ओवरराइट करने से बचने के लिए स्टैक पर उन चार स्लॉट में a0a3 स्टोर करना होगा। फिर, आप इन रजिस्टरों को स्टैक पर लिखने के लिए sp का उपयोग करें।

यदि आपके फ़ंक्शन में स्थानीय चर हैं और वे सभी रजिस्टरों में नहीं रख सकते हैं (जैसे a0a3 के माध्यम से जब यह अन्य कार्यों को कॉल करता है), तो उन लोगों के लिए ऑन-स्टैक स्पेस का उपयोग करना होगा स्थानीय चर, जो फिर से sp के उपयोग की आवश्यकता है।

उदाहरण के लिए, अगर आप इस था:

int tst5(int x1, int x2, int x3, int x4, int x5) 
{ 
    return x1 + x2 + x3 + x4 + x5; 
} 

अपने disassembly की तरह कुछ होगा:

tst5: 
     lw  $2,16($sp) # r2 = x5; 4 slots are skipped 
     addu $4,$4,$5 # x1 += x2 
     addu $4,$4,$6 # x1 += x3 
     addu $4,$4,$7 # x1 += x4 
     j  $31  # return 
     addu $2,$4,$2 # r2 += x1 

देखें, spx5 उपयोग करने के लिए प्रयोग किया जाता है।

और फिर आप इस तरह कोड कुछ है अगर:

int binary(int a, int b) 
{ 
    return a + b; 
} 

void stk(void) 
{ 
    binary(binary(binary(1, 2), binary(3, 4)), binary(binary(5, 6), binary(7, 8))); 
} 

यह है कि क्या यह संकलन के बाद disassembly में दिखाई देता है:

binary: 
     j  $31      # return 
     addu $2,$4,$5    # r2 = a + b 

stk: 
     subu $sp,$sp,32    # allocate space for local vars & 4 slots 
     li  $4,0x00000001   # 1 
     li  $5,0x00000002   # 2 
     sw  $31,24($sp)    # store return address on stack 
     sw  $17,20($sp)    # preserve r17 on stack 
     jal  binary     # call binary(1,2) 
     sw  $16,16($sp)    # preserve r16 on stack 

     li  $4,0x00000003   # 3 
     li  $5,0x00000004   # 4 
     jal  binary     # call binary(3,4) 
     move $16,$2     # r16 = binary(1,2) 

     move $4,$16     # r4 = binary(1,2) 
     jal  binary     # call binary(binary(1,2), binary(3,4)) 
     move $5,$2     # r5 = binary(3,4) 

     li  $4,0x00000005   # 5 
     li  $5,0x00000006   # 6 
     jal  binary     # call binary(5,6) 
     move $17,$2     # r17 = binary(binary(1,2), binary(3,4)) 

     li  $4,0x00000007   # 7 
     li  $5,0x00000008   # 8 
     jal  binary     # call binary(7,8) 
     move $16,$2     # r16 = binary(5,6) 

     move $4,$16     # r4 = binary(5,6) 
     jal  binary     # call binary(binary(5,6), binary(7,8)) 
     move $5,$2     # r5 = binary(7,8) 

     move $4,$17     # r4 = binary(binary(1,2), binary(3,4)) 
     jal  binary     # call binary(binary(binary(1,2), binary(3,4)), binary(binary(5,6), binary(7,8))) 
     move $5,$2     # r5 = binary(binary(5,6), binary(7,8)) 

     lw  $31,24($sp)    # restore return address from stack 
     lw  $17,20($sp)    # restore r17 from stack 
     lw  $16,16($sp)    # restore r16 from stack 
     addu $sp,$sp,32    # remove local vars and 4 slots 
     j  $31      # return 
     nop 

मुझे आशा है कि मैं गलतियों के बिना कोड एनोटेट गए हैं।

तो, ध्यान दें कि कंपाइलर फ़ंक्शन में r16 और r17 का उपयोग करना चुनता है लेकिन उन्हें स्टैक पर संरक्षित करता है। चूंकि फ़ंक्शन किसी अन्य को कॉल करता है, इसलिए उसे इसे r31 में रखने के बजाय स्टैक पर अपना रिटर्न पता सुरक्षित रखना होगा।

पीएस याद रखें कि एमआईपीएस पर सभी शाखा/कूद निर्देश वास्तव में तुरंत एक नए स्थान पर नियंत्रण स्थानांतरित करने से पहले तुरंत निम्नलिखित निर्देश निष्पादित करते हैं। यह भ्रमित हो सकता है।