X86 असेंबली में रजिस्टरों पर उपयोग किए जाने वाले पुश / पॉप निर्देशों का क्या कार्य है?


100

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

  • आप किसी रजिस्टर को कैसे धकेल सकते हैं? इसे कहां धकेला गया है? इसकी आवश्यकता क्यों है?
  • क्या यह एकल प्रोसेसर निर्देश को उबालता है या यह अधिक जटिल है?

3
चेतावनी: इंटेल के असेंबली सिंटैक्स में सभी मौजूदा उत्तर दिए गए हैं; उदाहरण के लिए एटी एंड टी वाक्य रचना में धक्का-पॉप की तरह एक के बाद ठीक का उपयोग करता है b, w, l, या qस्मृति के आकार चालाकी से किया जा रहा निरूपित करने के लिए। उदाहरण के लिए: pushl %eaxऔरpopl %eax
Hawken

5
@hawken अधिकांश असेंबलर AT & T सिंटैक्स (विशेष रूप से गैस) को निगलने में सक्षम होते हैं, यदि पोस्ट आकार को ऑपरेंड आकार से घटाया जा सकता है, तो आकार पोस्टफ़िक्स को छोड़ा जा सकता है। यह आपके द्वारा दिए गए उदाहरणों के लिए मामला है, जैसा %eaxकि आकार में हमेशा 32 बिट होता है।
गनथर पाईज़

जवाबों:


153

एक मान को धकेलना (जरूरी नहीं कि एक रजिस्टर में संग्रहीत) का अर्थ है इसे स्टैक पर लिखना।

पॉपिंग का मतलब है कि एक रजिस्टर में ढेर के ऊपर जो कुछ भी है उसे बहाल करना । वे मूल निर्देश हैं:

push 0xdeadbeef      ; push a value to the stack
pop eax              ; eax is now 0xdeadbeef

; swap contents of registers
push eax
mov eax, ebx
pop ebx

5
पुश और पॉप के लिए स्पष्ट ऑपरेंड है r/m, न केवल पंजीकरण, इसलिए आप कर सकते हैं push dword [esi]। या फिर pop dword [esp]लोड करने और फिर उसी मान को वापस उसी पते पर संग्रहीत करने के लिए। ( github.com/HJLebbink/asm-dude/wiki/POP )। मैं केवल इसका उल्लेख करता हूं क्योंकि आप कहते हैं "जरूरी नहीं कि एक रजिस्टर"।
पीटर कॉर्डेस

2
आप popस्मृति के क्षेत्र में भी जा सकते हैं :pop [0xdeadbeef]
एसएस ऐनी

हाय, पुश / पॉप और पुशक / पॉपक में क्या अंतर है? मैं macos / Intel पर हूं
SteakOverflow

45

यहां बताया गया है कि आप किसी रजिस्टर को कैसे पुश करते हैं। मुझे लगता है कि हम x86 के बारे में बात कर रहे हैं।

push ebx
push eax

इसे ढेर पर धकेल दिया जाता है। ESPX86 सिस्टम में स्टैक नीचे की ओर बढ़ता है , रजिस्टर के मूल्य को धक्का मूल्य के आकार में घटाया जाता है।

मूल्यों को संरक्षित करने के लिए यह आवश्यक है। सामान्य उपयोग है

push eax           ;   preserve the value of eax
call some_method   ;   some method is called which will put return value in eax
mov  edx, eax      ;    move the return value to edx
pop  eax           ;    restore original eax

pushx86 में एक एकल निर्देश है, जो आंतरिक रूप से दो काम करता है।

  1. ESPधक्का मूल्य के आकार के आधार पर रजिस्टर में कमी ।
  2. ESPरजिस्टर के वर्तमान पते पर धकेल दिया गया मूल्य संग्रहीत करें।

@vavan ने इसे ठीक करने के लिए सिर्फ एक अनुरोध भेजा है
jgh fun-run 20

1
@vavan: मैंने संपादन किया।
नैट एल्ड्रेडगे

38

इसे कहां धकेला गया है?

esp - 4। ज्यादा ठीक:

  • esp 4 से घटाया जाता है
  • मान को धकेल दिया जाता है esp

pop इससे उलट होता है।

सिस्टम V ABI लिनक्स को rspएक समझदार स्टैक स्थान पर इंगित करने के लिए कहता है जब प्रोग्राम चालू होता है: प्रोग्राम लॉन्च होने पर डिफ़ॉल्ट रजिस्टर स्थिति क्या होती है (asm, linux)? जो आप आमतौर पर उपयोग करना चाहिए।

आप किसी रजिस्टर को कैसे धकेल सकते हैं?

न्यूनतम जीएनयू जीएएस उदाहरण:

.data
    /* .long takes 4 bytes each. */
    val1:
        /* Store bytes 0x 01 00 00 00 here. */
        .long 1
    val2:
        /* 0x 02 00 00 00 */
        .long 2
.text
    /* Make esp point to the address of val2.
     * Unusual, but totally possible. */
    mov $val2, %esp

    /* eax = 3 */
    mov $3, %ea 

    push %eax
    /*
    Outcome:
    - esp == val1
    - val1 == 3
    esp was changed to point to val1,
    and then val1 was modified.
    */

    pop %ebx
    /*
    Outcome:
    - esp == &val2
    - ebx == 3
    Inverses push: ebx gets the value of val1 (first)
    and then esp is increased back to point to val2.
    */

GitHub पर उपर्युक्त जोर के साथ

इसकी आवश्यकता क्यों है?

यह सच है कि उन निर्देशों को आसानी से लागू किया जा सकता है mov, addऔर sub

वे कारण हैं कि वे मौजूद हैं, यह है कि निर्देशों के संयोजन इतने लगातार होते हैं, कि इंटेल ने उन्हें हमारे लिए प्रदान करने का फैसला किया।

उन संयोजनों में अक्सर ऐसा क्यों होता है, इसका कारण यह है कि वे रजिस्टरों के मूल्यों को स्मृति में सहेजने और पुनर्स्थापित करने के लिए अस्थायी रूप से आसान बनाते हैं ताकि वे अधिलेखित न हों।

समस्या को समझने के लिए, हाथ से कुछ सी कोड संकलित करने का प्रयास करें।

एक बड़ी कठिनाई, यह तय करना है कि प्रत्येक चर कहाँ संग्रहीत किया जाएगा।

आदर्श रूप से, सभी चर रजिस्टरों में फिट होंगे, जो कि एक्सेस करने के लिए सबसे तेज़ मेमोरी है (वर्तमान में रैम की तुलना में लगभग 100 गुना तेज है)।

लेकिन निश्चित रूप से, हमारे पास रजिस्टरों की तुलना में अधिक चर आसानी से हो सकते हैं, विशेष रूप से नेस्टेड फ़ंक्शन के तर्कों के लिए, इसलिए इसका एकमात्र समाधान मेमोरी में लिखना है।

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

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

जब कंपाइलर के आवंटनकर्ता को केवल रजिस्टरों के बजाय मेमोरी में चीजों को स्टोर करने के लिए मजबूर किया जाता है, जिसे एक स्पिल के रूप में जाना जाता है ।

क्या यह एकल प्रोसेसर निर्देश को उबालता है या यह अधिक जटिल है?

हम सभी को निश्चित रूप से पता है कि इंटेल pushएक popनिर्देश और एक निर्देश है, इसलिए वे उस अर्थ में एक निर्देश हैं।

आंतरिक रूप से, इसे कई माइक्रोकोड में विस्तारित किया जा सकता है, एक को संशोधित करने espऔर एक को मेमोरी आईओ करने के लिए, और कई चक्र लेने के लिए।

लेकिन यह भी संभव है कि एक एकल pushअन्य निर्देशों के समतुल्य संयोजन से तेज हो, क्योंकि यह अधिक विशिष्ट है।

यह ज्यादातर संयुक्त राष्ट्र (डेर) प्रलेखित है:


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

@PeterCordes का कमाल! तो माइक्रो-संचालन की गणना करने के लिए इंटेल द्वारा प्रदर्शन काउंटरों को प्रलेखित किया जाता है?
सिरो सेंटिल्ली 郝海东 冠状 iro i

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

हाँ, कुछ अलग पाइपलाइन चरणों (समस्या / निष्पादित / सेवानिवृत्त) में कुल यूओपी के लिए काउंटर हैं, ताकि आप फ़्यूज़-डोमेन या अप्रयुक्त-डोमेन की गिनती कर सकें। उदाहरण के लिए यह उत्तर देखें । यदि मैं उस उत्तर को फिर से लिख रहा था, तो मैं ocperf.pyकाउंटर स्क्रिप्ट को काउंटरों के लिए आसान प्रतीकात्मक नाम प्राप्त करने के लिए उपयोग करूंगा ।
पीटर कॉर्डेस

23

इसके बराबर के दृश्यों के पीछे पुशिंग और पॉपिंग रजिस्टर हैं:

push reg   <= same as =>      sub  $8,%rsp        # subtract 8 from rsp
                              mov  reg,(%rsp)     # store, using rsp as the address

pop  reg    <= same as=>      mov  (%rsp),reg     # load, using rsp as the address
                              add  $8,%rsp        # add 8 to the rsp

ध्यान दें कि यह x86-64 एट एंड टी सिंटैक्स है।

एक जोड़ी के रूप में उपयोग किया जाता है, यह आपको स्टैक पर एक रजिस्टर को बचाने और बाद में इसे पुनर्स्थापित करने देता है। अन्य उपयोग भी हैं।


4
हां, वे क्रम सही ढंग से पुश / पॉप का अनुकरण करते हैं। (पुश / पॉप को छोड़कर झंडे को प्रभावित न करें)।
पीटर कॉर्ड्स

2
बेहतर होगा कि तुम प्रयोग करेंगे lea rsp, [rsp±8]के बजाय add/ subकरने के लिए बेहतर के प्रभाव का अनुकरण push/ popझंडे पर।
रुस्लान

12

लगभग सभी CPU स्टैक का उपयोग करते हैं। प्रोग्राम स्टैक हार्डवेयर समर्थित प्रबंधन के साथ LIFO तकनीक है।

स्टैक प्रोग्राम की मात्रा (रैम) मेमोरी है जो आमतौर पर सीपीयू मेमोरी हीप के शीर्ष पर आवंटित की जाती है और विपरीत दिशा में PUSH निर्देश (स्टैक पॉइंटर कम हो जाता है) पर बढ़ता है। स्टैक में डालने के लिए एक मानक शब्द PUSH है और स्टैक से हटाने के लिए POP है

स्टैक को स्टैक इच्छित CPU रजिस्टर के माध्यम से प्रबंधित किया जाता है, जिसे स्टैक पॉइंटर भी कहा जाता है, इसलिए जब CPU POP या PUSH करता है , तो स्टैक पॉइंटर में स्टैक पॉइंटर को लोड करेगा या स्टोर करेगा और स्टैक पॉइंटर स्वचालित रूप से कम हो जाएगा। या (से) ढेर में poped।

वाया कोडांतरक निर्देश जिसे हम स्टैक में संग्रहीत कर सकते हैं:

  1. सीपीयू रजिस्टर और स्थिरांक भी।
  2. कार्यों या प्रक्रियाओं के लिए पता वापस करें
  3. कार्य / प्रक्रियाएँ / बाहर चर में
  4. कार्य / प्रक्रियाएँ स्थानीय चर।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.