LEA निर्देश का उद्देश्य क्या है?


676

मेरे लिए, यह सिर्फ एक कायरतापूर्ण MOV की तरह लगता है। इसका उद्देश्य क्या है और मुझे इसका उपयोग कब करना चाहिए?


2
उन पते पर LEA का उपयोग करना भी देखें जो पते / संकेत नहीं हैं? : LEA सिर्फ एक शिफ्ट-एंड-ऐड निर्देश है। यह शायद 8086 में जोड़ा गया था क्योंकि हार्डवेयर पहले से ही एड्रेसिंग मोड्स को डीकोड और कैलकुलेट करने के लिए है, इसलिए नहीं कि यह केवल "पतों" के लिए उपयोग के लिए "इरादा" है। याद रखें कि विधानसभा में संकेत केवल पूर्णांक हैं।
पीटर कॉर्डेस

जवाबों:


798

जैसा कि दूसरों ने बताया है, LEA (लोड प्रभावी पता) का उपयोग अक्सर कुछ गणनाओं को करने के लिए "ट्रिक" के रूप में किया जाता है, लेकिन यह इसका प्राथमिक उद्देश्य नहीं है। X86 इंस्ट्रक्शन सेट को पास्कल और सी जैसी उच्च-स्तरीय भाषाओं का समर्थन करने के लिए डिज़ाइन किया गया था, जहां विशेष रूप से इन्ट्स या छोटे स्ट्रक्चर्स के एरे- कॉमन हैं। उदाहरण के लिए, एक संरचना का प्रतिनिधित्व करने वाले (x, y) निर्देशांक पर विचार करें:

struct Point
{
     int xcoord;
     int ycoord;
};

अब एक बयान की तरह कल्पना करें:

int y = points[i].ycoord;

जहां points[]की एक सरणी है Point। सरणी के आधार मान लिया जाये कि में पहले से ही है EBX, और चर iमें है EAX, और xcoordऔर ycoordप्रत्येक 32 बिट (ताकि हैं ycoordstruct में ऑफसेट 4 बाइट पर है), इस बयान का संकलन किया जा सकता है:

MOV EDX, [EBX + 8*EAX + 4]    ; right side is "effective address"

जो पहुंचेंगे yमें EDX। 8 का पैमाना कारक है क्योंकि प्रत्येक Pointआकार में 8 बाइट्स हैं। अब उसी अभिव्यक्ति पर विचार करें जिसका उपयोग "ऑपरेटर" के पते और:

int *p = &points[i].ycoord;

इस स्थिति में, आप इसका मान नहीं चाहते हैं ycoord, लेकिन इसका पता है। यही वह जगह है जहां LEA(प्रभावी पते को लोड करें) इसके बजाय MOV, संकलक उत्पन्न कर सकता है

LEA ESI, [EBX + 8*EAX + 4]

जिसमें पता लोड होगा ESI


112
क्या movनिर्देश का विस्तार करना और कोष्ठक से बाहर जाना क्लीनर नहीं था ? MOV EDX, EBX + 8*EAX + 4
नाटन येलिन

14
@imacake एक विशेष MOV के साथ LEA को प्रतिस्थापित करके आप वाक्यविन्यास को साफ रखते हैं: [] कोष्ठक हमेशा सी में एक सूचक को डीफ़्रेंडिंग करने के बराबर होते हैं। कोष्ठक के बिना, आप हमेशा सूचक के साथ ही व्यवहार करते हैं।
नाटन येलिन

139
एक MOV निर्देश (EBX + 8 * EAX + 4) में गणित करना मान्य नहीं है। LEA ESI, [EBX + 8 * EAX + 4] वैध है क्योंकि यह एक एड्रेसिंग मोड है जो x86 सपोर्ट करता है। en.wikipedia.org/wiki/X86#Addressing_modes
एरिक

29
@JonathanDickinson LEA MOVएक अप्रत्यक्ष स्रोत की तरह है, सिवाय इसके कि यह केवल अप्रत्यक्ष है और नहीं MOV। यह वास्तव में गणना पते से नहीं पढ़ता है , बस गणना करता है।
हॉब्स

24
एरिक, टूर टिप्पणी सटीक नहीं है। MOV eax, [ebx + 8 * ecx + 4] वैध है। हालाँकि, MOV ने thst मेमोरी लोकेशन की सामग्री लौटा दी जबकि LEA ने पता दिया
Olorin

562

से "सभा के ज़ेन" Abrash द्वारा:

LEA, एकमात्र निर्देश जो गणना को संबोधित करने वाली मेमोरी करता है लेकिन वास्तव में मेमोरी को संबोधित नहीं करता है। LEAएक मानक मेमोरी एड्रेसिंग ऑपरेंड को स्वीकार करता है, लेकिन निर्दिष्ट रजिस्टर में गणना की गई मेमोरी ऑफसेट से अधिक कुछ भी नहीं करता है, जो किसी भी सामान्य उद्देश्य रजिस्टर हो सकता है।

वह हमें क्या देता है? दो चीजें जो ADDप्रदान नहीं करती हैं:

  1. दो या तीन ऑपरेंड के साथ प्रदर्शन करने की क्षमता, और
  2. किसी भी रजिस्टर में परिणाम को संग्रहीत करने की क्षमता ; स्रोत ऑपरेंड में से केवल एक नहीं।

और LEAझंडे को नहीं बदलता है।

उदाहरण

  • LEA EAX, [ EAX + EBX + 1234567 ]गणना EAX + EBX + 1234567(वह तीन ऑपरेंड)
  • LEA EAX, [ EBX + ECX ]EBX + ECXपरिणाम के साथ या तो ओवरराइड किए बिना गणना करता है।
  • स्थिरांक द्वारा गुणा (दो, तीन, पांच या नौ से), यदि आप इसका उपयोग करते हैं जैसे LEA EAX, [ EBX + N * EBX ](एन 1,2,4,8 हो सकता है)।

अन्य usecase छोरों में काम है: के बीच का अंतर है LEA EAX, [ EAX + 1 ]और INC EAXयह है कि बाद में परिवर्तन होता है EFLAGSलेकिन पूर्व नहीं करता है; यह CMPराज्य को संरक्षित करता है।


42
@AbidRahmanK कुछ उदाहरण: LEA EAX, [ EAX + EBX + 1234567 ]के योग की गणना करता है EAX, EBXऔर 1234567(यह तीन ऑपरेंड है)। परिणाम के साथ या तो ओवरराइड किए बिनाLEA EAX, [ EBX + ECX ] गणना करता है। तीसरी चीज (फ्रैंक द्वारा सूचीबद्ध नहीं है) के लिए उपयोग किया जाता है, लगातार (दो, तीन, पांच या नौ से गुणा ), यदि आप इसे पसंद करते हैं ( 1,2,4,8 हो सकता है)। अन्य usecase छोरों में काम है: के बीच का अंतर है और यह है कि बाद में परिवर्तन होता है लेकिन पूर्व नहीं करता है; यह राज्य को संरक्षित करता हैEBX + ECX LEALEA EAX, [ EBX + N * EBX ]NLEA EAX, [ EAX + 1 ]INC EAXEFLAGSCMP
फ्रैंकएच।

@FrankH। मुझे अभी भी समझ में नहीं आया है, तो यह कहीं और पर एक पॉइंटर लोड करता है?

6
@ ripDaddy69 हाँ, जैसे - अगर "लोड" से आपका मतलब है "पता गणना / सूचक अंकगणित" करता है। यह मैमोरी एक्सेस नहीं करता (यानी "डीरेसीफिकेशन" पॉइंटर नहीं करता क्योंकि इसे C प्रोग्रामिंग शब्दों में कहा जाएगा)।
फ्रैंक।

2
+1: यह स्पष्ट करता है कि किस प्रकार के 'ट्रिक्स' LEAका उपयोग किया जा सकता है ... (देखें "LEA (लोड प्रभावी पता) को अक्सर IJ कैनेडी के लोकप्रिय उत्तर में कुछ कम्प्यूटेशंस करने के लिए" ट्रिक "के रूप में उपयोग किया जाता है)
असद अब्राहिम

3
2 ऑपरेंड LEA के बीच एक बड़ा अंतर है जो तेज़ है और 3 ऑपरेंड LEA जो धीमा है। इंटेल ऑप्टिमाइज़ेशन मैनुअल कहता है कि तेज़ पथ LEA एकल चक्र है और धीमा पथ LEA तीन चक्र लेता है। इसके अलावा, स्काईलेक पर दो तेज़ पथ कार्यात्मक इकाइयां (पोर्ट 1 और 5) हैं और केवल एक धीमी पथ कार्यात्मक इकाई (पोर्ट 1) है। असेंबली / कंपाइलर कोडिंग नियम 33 मैनुअल में भी 3 ऑपरेंड एलएए का उपयोग करने के खिलाफ चेतावनी देता है।
ओल्सोनिस्ट

110

का एक अन्य महत्वपूर्ण विशेषता LEAअनुदेश है कि यह इस तरह के रूप हालत कोड में परिवर्तन नहीं करता है CFऔर ZF, जैसे गणित निर्देश से पता कंप्यूटिंग, जबकि ADDया MULनहीं करता है। यह सुविधा निर्देशों के बीच निर्भरता के स्तर को कम करती है और इस प्रकार संकलक या हार्डवेयर अनुसूचक द्वारा आगे अनुकूलन के लिए जगह बनाती है।


1
हां, leaकभी-कभी कंपाइलर (या मानव कोडर) के लिए एक फ्लैग रिजल्ट को क्लोब किए बिना गणित करने के लिए उपयोगी होता है। लेकिन leaतेजी से नहीं है add। अधिकांश x86 निर्देश झंडे लिखते हैं। उच्च-प्रदर्शन x86 कार्यान्वयन को EFLAGS का नाम बदलना है या अन्यथा सामान्य कोड के लिए तेजी से चलाने के लिए लिखने के बाद लिखने के खतरे से बचें, इसलिए ध्वज लिखने से बचने वाले निर्देश उस वजह से बेहतर नहीं हैं। ( आंशिक ध्वज सामान समस्याएँ पैदा कर सकता है, INC निर्देश 1 ADD देखें : क्या यह बात है? )
पीटर कॉर्ड्स

2
@PeterCordes: इसे यहाँ लाने के लिए नफरत है - लेकिन क्या मैं इस नए [x86-lea] टैग को सोचने में अकेला हूँ और यह अनावश्यक है?
माइकल पेट

2
@MichaelPetch: हाँ, मुझे लगता है कि यह बहुत विशिष्ट है। ऐसा लगता है कि शुरुआत करने वाले को भ्रमित करना चाहिए जो मशीनी भाषा को नहीं समझते हैं और यह सब कुछ (बिंदु सहित) बिट्स / बाइट्स / पूर्णांक हैं, इसलिए बड़ी संख्या में वोटों के साथ इसके बारे में बहुत सारे प्रश्न हैं। लेकिन इसके लिए एक टैग होने का तात्पर्य है कि भविष्य के प्रश्नों की एक खुली-समाप्त संख्या के लिए जगह है, जब वास्तव में लगभग 2 या 3 कुल होते हैं जो केवल डुप्लिकेट नहीं होते हैं। (यह क्या है? पूर्णांक को गुणा करने के लिए इसका उपयोग कैसे करें? और यह AGUs बनाम ALUs पर आंतरिक रूप से कैसे चलता है और किस विलंबता / थ्रूपुट के साथ है। और शायद यह "उद्देश्य" है)
पीटर कॉर्डेस

@PeterCordes: मैं सहमत हूं, और यदि इन सभी पदों को संपादित किया जा रहा है, तो बहुत से बाहर निकलने वाले LEA संबंधित प्रश्नों में से कुछ का एक डुप्लिकेट है। टैग के बजाय, किसी भी डुप्लिकेट की पहचान की जानी चाहिए और उसे चिह्नित किया जाना चाहिए।
माइकल पेट

1
@EvanCarroll: यदि आप पहले ही समाप्त नहीं हुए हैं, तो सभी LEA प्रश्नों को टैग करें। जैसा कि ऊपर चर्चा की गई है, हम सोचते हैं कि x86-lea एक टैग के लिए बहुत विशिष्ट है, और भविष्य में गैर-डुप्लिकेट प्रश्नों के लिए बहुत अधिक गुंजाइश नहीं है। मुझे लगता है कि वास्तव में "बेस्ट" क्यू एंड ए में से अधिकांश के लिए एक कठिन लक्ष्य के रूप में चुनना बहुत काम होगा, हालांकि, या वास्तव में तय करना है कि किन लोगों को विलय करने के लिए मॉड्स प्राप्त करना है।
पीटर कॉर्डेस

93

सभी स्पष्टीकरणों के बावजूद, LEA एक अंकगणितीय ऑपरेशन है:

LEA Rt, [Rs1+a*Rs2+b] =>  Rt = Rs1 + a*Rs2 + b

यह सिर्फ इतना है कि इसका नाम शिफ्ट + ऐड ऑपरेशन के लिए एक्सटेलीली बेवकूफ है। इसका कारण पहले से ही शीर्ष रेटेड जवाबों में समझाया गया था (यानी इसे सीधे उच्च स्तरीय मेमोरी संदर्भों को मैप करने के लिए डिज़ाइन किया गया था)।


8
और यह कि अंकगणित पता-गणना हार्डवेयर द्वारा किया जाता है।
बेन वोइग्ट

30
@BenVoigt मैं कहता था कि, क्योंकि मैं एक पुराना ब्लॉक्स हूं। परंपरागत रूप से, x86 CPUs ने इसके लिए एड्रेसिंग यूनिट्स का उपयोग किया, इस पर सहमति व्यक्त की। लेकिन "जुदाई" इन दिनों बहुत धुंधली हो गई है। कुछ सीपीयू ने अब एजीयू को समर्पित नहीं किया है , दूसरों ने एजीयू पर नहीं LEAबल्कि साधारण पूर्णांक ALU पर निष्पादित करने के लिए चुना है । सीपीयू स्पेक्स को इन दिनों बहुत बारीकी से पढ़ना पड़ता है ताकि पता लगाया जा सके कि "सामान कहाँ चलता है" ...
फ्रैंकएच।

2
@FrankH .: आउट-ऑफ-ऑर्डर सीपीयू आमतौर पर एलईए पर चलते हैं, जबकि कुछ इन-ऑर्डर सीपीयू (जैसे एटम) कभी-कभी इसे एजीयू पर चलाते हैं (क्योंकि वे मेमोरी एक्सेस को संभालने में व्यस्त नहीं हो सकते हैं)।
पीटर कॉर्ड्स

3
नहीं, नाम बेवकूफ नहीं है। LEAआपको वह पता देता है जो किसी भी मेमोरी से संबंधित एड्रेसिंग मोड से उत्पन्न होता है। यह एक बदलाव नहीं है और ऑपरेशन जोड़ें।
काज

3
एफडब्ल्यूआईडब्ल्यू बहुत कम (यदि कोई है) वर्तमान x86 सीपीयू हैं जो एजीयू पर ऑपरेशन करते हैं। अधिकांश या सभी बस किसी अन्य अंकगणित सेशन की तरह ALU का उपयोग करते हैं।
मधुमक्खी पालन

77

शायद LEA निर्देश के बारे में एक और बात। आप तेजी से बढ़ते रजिस्टर के लिए LEA का उपयोग 3, 5 या 9 से भी कर सकते हैं।

LEA EAX, [EAX * 2 + EAX]   ;EAX = EAX * 3
LEA EAX, [EAX * 4 + EAX]   ;EAX = EAX * 5
LEA EAX, [EAX * 8 + EAX]   ;EAX = EAX * 9

13
चाल के लिए +1। लेकिन मैं एक सवाल पूछना चाहता हूं (बेवकूफ हो सकता है), क्यों न सीधे इस तरह से तीन के साथ गुणा किया जाए LEA EAX, [EAX*3]?
आबिद रहमान के

13
@ अबिद रहमान के: इंस्ट्रक्शन अनडे x86 सीपीयू इंस्ट्रक्शन सेट जैसे कोई निर्देश नहीं है।
जीजे

50
@ AsbidRahmanK इंटेल asm सिंटैक्स के बावजूद यह गुणन की तरह दिखता है, अंतिम निर्देश शिफ्ट ऑपरेशन को एन्कोड कर सकता है।
ओपकोड

6
@Koray Tugay: आप shlरजिस्टरों को 2,4,8,16 से गुणा करने के निर्देश की तरह बाईं ओर की शिफ्ट का उपयोग कर सकते हैं ... यह तेज और छोटा है। लेकिन 2 की शक्ति के विभिन्न संख्याओं के साथ गुणा करने के लिए हम सामान्य रूप से mulनिर्देश का उपयोग करते हैं जो अधिक दिखावा और धीमा है।
जीजे

7
@GJ। हालाँकि इस तरह की कोई एन्कोडिंग नहीं है, लेकिन कुछ असेम्बलर्स इसे एक शॉर्टकट के रूप में स्वीकार करते हैं, जैसे कि फ़ेस। इसलिए जैसे के lea eax,[eax*3]बराबर अनुवाद होगा lea eax,[eax+eax*2]
रुस्लान

59

lea"लोड प्रभावी पता" का एक संक्षिप्त नाम है। यह गंतव्य संचालक के स्रोत स्रोत द्वारा स्थान संदर्भ के पते को लोड करता है। उदाहरण के लिए, आप इसका उपयोग कर सकते हैं:

lea ebx, [ebx+eax*8]

एक निर्देश के साथ आगे (64-बिट / तत्व सरणी में) ebxपॉइंटर eaxआइटम को स्थानांतरित करने के लिए । मूल रूप से, आप x86 आर्किटेक्चर द्वारा समर्थित जटिल एड्रेसिंग मोड्स से लाभान्वित होते हैं ताकि पॉइंटर्स को कुशलता से जोड़ सकें।


23

यदि आप LEAएक MOVसे अधिक उपयोग करते हैं , तो आपको पता की गणना करने के लिए उपयोग किए जाने वाले रजिस्टरों पर अंकगणित करने की आवश्यकता है। प्रभावी रूप से, आप "फ्री" के लिए प्रभावी ढंग से संयोजन में रजिस्टरों में से कई पर अंकगणितीय अंकगणित करने के लिए क्या मात्रा में प्रदर्शन कर सकते हैं।

इसके बारे में वास्तव में भ्रमित करने वाली बात यह है कि आप आम तौर पर लिखने की LEAतरह ही MOVकरते हैं लेकिन आप वास्तव में मेमोरी को डीरफेर नहीं कर रहे हैं। दूसरे शब्दों में:

MOV EAX, [ESP+4]

यह किस ESP+4बिंदु की सामग्री को आगे बढ़ाएगा EAX

LEA EAX, [EBX*8]

यह प्रभावी पते EBX * 8को EAX में ले जाएगा , न कि उस स्थान पर जो मिला है। जैसा कि आप देख सकते हैं, यह भी, दो (स्केलिंग) के कारकों से गुणा करना संभव है, जबकि एक MOVजोड़ने / घटाने के लिए सीमित है।


सभी को क्षमा करें। @ big.heart ने मुझे तीन घंटे पहले इस बात का जवाब देकर मुझे बेवकूफ बनाया, यह मेरे विधानसभा प्रश्न में "नया" के रूप में दिखा।
डेविड होल्जर

1
जब यह मेमोरी एड्रेसिंग नहीं करता है तो सिंटैक्स ब्रैकेट का उपयोग क्यों करता है?
गोलपोस्ट १ '’१

3
@ q4w56 यह उन चीजों में से एक है, जहां उत्तर है, "बस यही आप इसे कैसे करते हैं।" मेरा मानना ​​है कि यह एक कारण है कि लोगों के पास इतना कठिन समय है कि LEAवे क्या करते हैं।
डेविड होल्ज़र

2
@ q4w56: यह एक शिफ्ट + ऐड निर्देश है जो मेमोरी ऑपरेंड सिंटैक्स और मशीन-कोड एन्कोडिंग का उपयोग करता है । कुछ CPU पर यह AGU हार्डवेयर का उपयोग भी कर सकता है, लेकिन यह एक ऐतिहासिक विवरण है। अभी भी प्रासंगिक तथ्य यह है कि डिकोडर हार्डवेयर इस तरह की शिफ्ट + ऐड को डिकोड करने के लिए पहले से मौजूद है, और LEA हमें मेमोरी एड्रेसिंग के बजाय अंकगणित के लिए इसका उपयोग करने देता है। (या पते की गणना के लिए यदि एक इनपुट वास्तव में एक सूचक है)।
पीटर कॉर्ड्स

20

8086 में निर्देशों का एक बड़ा परिवार है जो एक रजिस्टर ऑपरेंड और एक प्रभावी पते को स्वीकार करता है, उस प्रभावी पते के ऑफसेट भाग की गणना करने के लिए कुछ संगणना करता है, और कुछ ऑपरेशन करता है जिसमें रजिस्टर और मेमोरी को गणना पते से संदर्भित किया जाता है। उस परिवार के निर्देशों में से एक का व्यवहार करना काफी सरल था, उस वास्तविक मेमोरी ऑपरेशन को छोड़कर। यह, निर्देश:

mov ax,[bx+si+5]
lea ax,[bx+si+5]

लगभग आंतरिक रूप से लागू किए गए थे। अंतर एक छोड़ दिया गया कदम है। दोनों निर्देश कुछ इस तरह काम करते हैं:

temp = fetched immediate operand (5)
temp += bx
temp += si
address_out = temp  (skipped for LEA)
trigger 16-bit read  (skipped for LEA)
temp = data_in  (skipped for LEA)
ax = temp

जैसा कि इंटेल ने सोचा था कि यह निर्देश शामिल करने लायक था, मुझे बिल्कुल यकीन नहीं है, लेकिन यह तथ्य कि इसे लागू करना सस्ता था, एक बड़ा कारक रहा होगा। एक अन्य कारक यह तथ्य होगा कि इंटेल के असेंबलर ने बीपी रजिस्टर के सापेक्ष प्रतीकों को परिभाषित करने की अनुमति दी थी। यदि fnordबीपी-सापेक्ष प्रतीक के रूप में परिभाषित किया गया था (उदाहरण बीपी + 8), तो कोई कह सकता है:

mov ax,fnord  ; Equivalent to "mov ax,[BP+8]"

अगर कोई बीपी-रिलेटिव एड्रेस में डेटा स्टोर करने के लिए स्टोसव जैसा कुछ इस्तेमाल करना चाहता है, तो कहने में सक्षम है

mov ax,0 ; Data to store
mov cx,16 ; Number of words
lea di,fnord
rep movs fnord  ; Address is ignored EXCEPT to note that it's an SS-relative word ptr

से अधिक सुविधाजनक था:

mov ax,0 ; Data to store
mov cx,16 ; Number of words
mov di,bp
add di,offset fnord (i.e. 8)
rep movs fnord  ; Address is ignored EXCEPT to note that it's an SS-relative word ptr

ध्यान दें कि दुनिया को "ऑफ़सेट" भूल जाने से स्थान की सामग्री [बीपी + 8] का मूल्य होगा, मूल्य 8 के बजाय, डीआई में जोड़ा जाएगा। उफ़।


12

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

LEA इकाई के बारे में कुछ विवरणों के लिए Haswell वास्तुकला के इस लेख की जाँच करें: http://www.realworldtech.com/haswell-cpu/4/

एक अन्य महत्वपूर्ण बिंदु जो अन्य उत्तरों में उल्लिखित नहीं है, वह LEA REG, [MemoryAddress]निर्देश है PIC (स्थिति स्वतंत्र कोड) जो संदर्भ के लिए इस निर्देश में पीसी सापेक्ष पते को एन्कोड करता है MemoryAddress। यह अलग है, MOV REG, MemoryAddressजो सापेक्ष आभासी पते को एन्कोड करता है और आधुनिक ऑपरेटिंग सिस्टम (जैसे एएसएलआर सामान्य विशेषता है) में रिलोकेटिंग / पैचिंग की आवश्यकता होती है। तो LEAऐसे नॉन PIC को PIC में बदलने के लिए इस्तेमाल किया जा सकता है।


2
"अलग LEA ALU" भाग ज्यादातर असत्य है। आधुनिक CPU leaएक या अधिक ALU पर निष्पादित होते हैं जो अन्य अंकगणितीय निर्देशों को निष्पादित करते हैं (लेकिन आम तौर पर उनमें से अन्य अंकगणित की तुलना में कम)। उदाहरण के लिए, उल्लिखित हसवेल सीपीयू चार अलग-अलग ALU पर या अन्य सबसे बुनियादी अंकगणितीय कार्यों को निष्पादित addया subकर सकता है, लेकिन केवल एक (जटिल ) या दो (सरल ) पर निष्पादित कर सकता है । इससे भी महत्वपूर्ण बात, उन दो- योग्य ALU केवल चार में से दो हैं जो अन्य निर्देशों को निष्पादित कर सकते हैं, इसलिए दावा किए गए कोई समानता लाभ नहीं है। lealealealea
मधुमक्खी पालन

आपके द्वारा लिंक किया गया (सही ढंग से) लेख दिखाता है कि LEA पूर्णांक ALU (add / sub / boolean), और Haswell में पूर्णांक MUL इकाई के समान पोर्ट पर है। (और FP ALD / MUL / FMA सहित वेक्टर ALU)। केवल साधारण LEA इकाई पोर्ट 5 पर है, जो ADD / SUB / जो भी है, और वेक्टर फेरबदल, और अन्य सामान चलाता है। केवल यही कारण है कि मैं नीच नहीं हूं, आप RIP- सापेक्ष LEA (केवल x86-64 के लिए) के उपयोग को इंगित करते हैं।
पीटर कॉर्ड्स

8

LEA निर्देश का उपयोग CPU द्वारा प्रभावी पते की गणना के समय से बचने के लिए किया जा सकता है। यदि किसी पते का बार-बार उपयोग किया जाता है, तो हर बार उपयोग किए जाने वाले प्रभावी पते की गणना करने के बजाय उसे रजिस्टर में संग्रहीत करना अधिक प्रभावी होता है।


जरूरी नहीं कि आधुनिक x86 पर। संबोधित करने वाले अधिकांश मोड में समान लागत होती है, कुछ केवेट के साथ। तो [esi]कहने की तुलना में शायद ही कभी सस्ता है [esi + 4200]और केवल की तुलना में सस्ता है [esi + ecx*8 + 4200]
BeeOnRope

@BeeOnRope [esi]से सस्ता नहीं है [esi + ecx*8 + 4200]। लेकिन परेशान करने की तुलना क्यों? वे समकक्ष नहीं हैं। यदि आप चाहते हैं कि पूर्व को उसी मेमोरी लोकेशन को बाद के रूप में नामित किया जाए, तो आपको अतिरिक्त निर्देशों की आवश्यकता है: आपको 8. esiसे ecxगुणा के मूल्य में जोड़ना होगा । उह ओह, गुणा आपके सीपीयू झंडे को बंद करने वाला है! फिर आपको 4200 जोड़ना होगा। ये अतिरिक्त निर्देश कोड आकार में जोड़ते हैं (अनुदेश कैश में जगह लेने के लिए, साइकिल लाने के लिए)।
कज़

2
@ काज़ - मुझे लगता है कि आपको मेरी बात याद आ रही थी (या फिर ओपी की बात याद आती थी)। मेरी समझ यह है कि ओपी कह रहा है कि यदि आप [esi + 4200]निर्देशों के अनुक्रम में बार-बार कुछ का उपयोग करने जा रहे हैं , तो पहले प्रभावी पते को एक रजिस्टर में लोड करना और उसका उपयोग करना बेहतर होता है। उदाहरण के लिए, लिखने के बजाय add eax, [esi + 4200]; add ebx, [esi + 4200]; add ecx, [esi + 4200], आपको पसंद करना चाहिए lea edi, [esi + 4200]; add eax, [edi]; add ebx, [edi]; add ecx, [edi], जो शायद ही कभी तेज होता है। कम से कम इस उत्तर की स्पष्ट व्याख्या है।
मधुमक्खी पालन

कारण मैं की तुलना की गई थी तो [esi]और [esi + 4200](या [esi + ecx*8 + 4200](मैं इसे समझ) जो इस सरलीकरण ओपी का प्रस्ताव किया गया है है: कि एक ही परिसर पते के साथ एन निर्देश सरल (एक reg) को संबोधित करते हुए, प्लस वन के साथ एन निर्देश के रूप में तब्दील कर रहे हैं lea, चूंकि जटिल संबोधन "समय लेने वाला" है। वास्तव में, यह आधुनिक x86 पर भी धीमा है, लेकिन केवल विलंबता-वार जो एक ही पते के साथ लगातार निर्देशों के लिए मायने नहीं रखता है।
BeeOnRope

1
शायद आप कुछ रजिस्टर दबाव को राहत देते हैं, हां - लेकिन इसके विपरीत मामला हो सकता है: यदि रजिस्टरों ने आपके साथ प्रभावी पते को लाइव किया है, तो आपको परिणाम को बचाने के लिए एक और रजिस्टर की आवश्यकता है leaताकि यह उस स्थिति में दबाव बढ़ाए। सामान्य तौर पर, इंटरमीडिएट स्टोर करना रजिस्टर दबाव का एक कारण है, इसका कोई समाधान नहीं है - लेकिन मुझे लगता है कि ज्यादातर स्थितियों में यह एक धोने वाला है। @ काज़
बीऑनरोप २६'१az

7

LEA (लोड प्रभावी पता) निर्देश, उस पते को प्राप्त करने का एक तरीका है जो इंटेल प्रोसेसर के मेमोरी एड्रेसिंग मोड में से किसी से उत्पन्न होता है।

यह कहना है, अगर हमारे पास इस तरह से एक डेटा चाल है:

MOV EAX, <MEM-OPERAND>

यह निर्दिष्ट मेमोरी स्थान की सामग्री को लक्ष्य रजिस्टर में ले जाता है।

यदि हम MOVद्वारा प्रतिस्थापित करते हैं LEA, तो मेमोरी लोकेशन के पते की गणना उसी तरह से की जाती है जैसे कि <MEM-OPERAND>एड्रेसिंग एक्सप्रेशन। लेकिन मेमोरी लोकेशन के कंटेंट के बजाय हम लोकेशन को ही डेस्टिनेशन में ले आते हैं।

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

उदाहरण के लिए, हम LEAकेवल एक साधारण प्रत्यक्ष पते पर उपयोग कर सकते हैं । कोई अंकगणित इसमें शामिल नहीं है:

MOV EAX, GLOBALVAR   ; fetch the value of GLOBALVAR into EAX
LEA EAX, GLOBALVAR   ; fetch the address of GLOBALVAR into EAX.

यह मान्य है; हम इसे लिनक्स प्रांप्ट पर परख सकते हैं:

$ as
LEA 0, %eax
$ objdump -d a.out

a.out:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
   0:   8d 04 25 00 00 00 00    lea    0x0,%eax

यहां, स्केल किए गए मूल्य का कोई जोड़ नहीं है, और कोई ऑफसेट नहीं है। शून्य को EAX में ले जाया जाता है। हम यह कर सकते हैं कि एक तत्काल ऑपरेंड के साथ भी एमओवी का उपयोग करना।

यही कारण है कि लोग जो सोचते हैं कि कोष्ठक LEAअतिसुंदर हैं, उनसे गंभीर रूप से गलती होती है; कोष्ठक LEAवाक्यविन्यास नहीं हैं, लेकिन संबोधित मोड का हिस्सा हैं।

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

पता शून्य से मान लोड करने में विरोधाभास:

$ as
movl 0, %eax
$ objdump -d a.out | grep mov
   0:   8b 04 25 00 00 00 00    mov    0x0,%eax

यह बहुत समान एन्कोडिंग है, देखिए? बस 8dके LEAलिए बदल गया है 8b

बेशक, यह LEAएन्कोडिंग तत्काल शून्य में जाने से अधिक है EAX:

$ as
movl $0, %eax
$ objdump -d a.out | grep mov
   0:   b8 00 00 00 00          mov    $0x0,%eax

LEAइस संभावना को बाहर करने का कोई कारण नहीं है, हालांकि सिर्फ इसलिए कि एक छोटा विकल्प है; यह केवल उपलब्ध एड्रेसिंग मोड के साथ एक ऑर्थोगोनल तरीके से संयोजन कर रहा है।


6

यहाँ एक उदाहरण है।

// compute parity of permutation from lexicographic index
int parity (int p)
{
  assert (p >= 0);
  int r = p, k = 1, d = 2;
  while (p >= k) {
    p /= d;
    d += (k << 2) + 6; // only one lea instruction
    k += 2;
    r ^= p;
  }
  return r & 1;
}

संकलक विकल्प के रूप में ओ (ऑप्टिमाइज़) के साथ, संकेतित कोड लाइन के लिए gcc को अंतिम निर्देश मिलेगा।


6

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

एक लंबी कहानी को छोटा करने के लिए, निर्देश और s निर्देशों के src ऑपरेंड को संलग्न करने वाले कोष्ठक के साथ अंतिम निर्देश और mov निर्देश दोनों का उपयोग किया जा सकता है। वे के साथ संलग्न कर रहे हैं () , में अभिव्यक्ति () एक ही तरीके से की जाती है; हालाँकि, दो निर्देश एक अलग तरीके से src ऑपरेंड में परिकलित मान की व्याख्या करेंगे।

चाहे एक्सप्रेशन का उपयोग लीक या मूव के साथ किया जाए, src मूल्य की गणना नीचे की तरह की जाती है।

D (Rb, Ri, S) => (Reg [Rb] + S * Reg [Ri] + D)

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

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

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

उदाहरण कोड

#define _GNU_SOURCE 1  /* To pick up REG_RIP */
#include <stdio.h> 
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>


uint32_t
register_handler (uint32_t event, void (*handler)(int, siginfo_t*, void*))
{
        uint32_t ret = 0;
        struct sigaction act;

        memset(&act, 0, sizeof(act));
        act.sa_sigaction = handler;
        act.sa_flags = SA_SIGINFO;
        ret = sigaction(event, &act, NULL);
        return ret;
}

void
segfault_handler (int signum, siginfo_t *info, void *priv)
{
        ucontext_t *context = (ucontext_t *)(priv);
        uint64_t rip = (uint64_t)(context->uc_mcontext.gregs[REG_RIP]);
        uint64_t faulty_addr = (uint64_t)(info->si_addr);

        printf("inst at 0x%lx tries to access memory at %ld, but failed\n",
                rip,faulty_addr);
        exit(1);
}

int
main(void)
{
        int result_of_lea = 0;

        register_handler(SIGSEGV, segfault_handler);

        //initialize registers %eax = 1, %ebx = 2

        // the compiler will emit something like
           // mov $1, %eax
           // mov $2, %ebx
        // because of the input operands
        asm("lea 4(%%rbx, %%rax, 8), %%edx \t\n"
            :"=d" (result_of_lea)   // output in EDX
            : "a"(1), "b"(2)        // inputs in EAX and EBX
            : // no clobbers
         );

        //lea 4(rbx, rax, 8),%edx == lea (rbx + 8*rax + 4),%edx == lea(14),%edx
        printf("Result of lea instruction: %d\n", result_of_lea);

        asm volatile ("mov 4(%%rbx, %%rax, 8), %%edx"
                       :
                       : "a"(1), "b"(2)
                       : "edx"  // if it didn't segfault, it would write EDX
          );
}

निष्पादन परिणाम

Result of lea instruction: 14
inst at 0x4007b5 tries to access memory at 14, but failed

1
अलग-अलग कथनों में अपनी इनलाइन asm को तोड़ना असुरक्षित है, और आपके क्लोबर्स की सूची अधूरी है। मूल-एएसएम ब्लॉक बताता है कि संकलक में कोई क्लोबर्स नहीं है, लेकिन यह वास्तव में कई रजिस्टरों को संशोधित करता है। इसके अलावा, आप =dसंकलक को यह बताने के लिए उपयोग कर सकते हैं कि परिणाम EDX में है, जिससे बचत होगी mov। आपने आउटपुट पर एक प्रारंभिक-क्लॉबर घोषणा भी छोड़ दी। यह प्रदर्शित करता है कि आप क्या प्रदर्शित करने की कोशिश कर रहे हैं, लेकिन इनलाइन एएसएम का एक भ्रामक बुरा उदाहरण भी है जो अन्य संदर्भों में उपयोग होने पर टूट जाएगा। यह एक स्टैक अतिप्रवाह उत्तर के लिए एक खराब चीज है।
पीटर कॉर्डेस

यदि आप %%विस्तारित asm में उन सभी रजिस्टर नामों पर लिखना नहीं चाहते हैं , तो इनपुट बाधाओं का उपयोग करें। पसंद है asm("lea 4(%%ebx, %%eax, 8), %%edx" : "=d"(result_of_lea) : "a"(1), "b"(2));। संकलक init रजिस्टरों को बताने का मतलब है कि आपको क्लोबर घोषित करने की आवश्यकता नहीं है। इससे पहले कि आप झट-झटके से चीजों को ओवर-कॉंप्लिमेट कर रहे हों, इससे पहले कि तत्काल-रजिस्टर पूरे रजिस्टर को ओवरराइट कर दे, भी।
पीटर कॉर्डेस

@PeterCordes धन्यवाद, पीटर, क्या आप चाहते हैं कि मैं इस उत्तर को हटा दूं या आपकी टिप्पणियों के बाद इसे संशोधित करूं?
जेहुक ली

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

1
कोई भी कहां कहता है कि mov 4(%ebx, %eax, 8), %edxयह अमान्य है? वैसे भी, हाँ, इसके लिए movयह समझने के लिए लिखना "a"(1ULL)होगा कि संकलक को आपके पास 64-बिट मान है, और इस प्रकार यह सुनिश्चित करने की आवश्यकता है कि यह पूरे रजिस्टर को भरने के लिए विस्तारित है। व्यवहार में यह अभी भी उपयोग करेगा mov $1, %eax, क्योंकि EAX शून्य लिखना RAX में विस्तारित होता है, जब तक कि आपके पास आसपास के कोड की एक अजीब स्थिति न हो, जहां कंपाइलर को पता था कि RAX = 0xff00000001या कुछ और। के लिए lea, आप अभी भी 32-बिट ऑपरेंड-आकार का उपयोग कर रहे हैं, इसलिए इनपुट रजिस्टरों में किसी भी आवारा उच्च बिट्स का 32-बिट परिणाम पर कोई प्रभाव नहीं पड़ता है।
पीटर कॉर्डेस

4

LEA: सिर्फ एक "अंकगणित" निर्देश ।।

MOV ऑपरेंड्स के बीच डेटा ट्रांसफर करता है लेकिन लीक सिर्फ गणना है


एलईए स्पष्ट रूप से डेटा को स्थानांतरित करता है; यह एक गंतव्य ऑपरेंड है। एलईए हमेशा गणना नहीं करता है; यह गणना करता है कि क्या स्रोत ऑपरेंड में व्यक्त प्रभावी पता गणना करता है। LEA EAX, GLOBALVAR गणना नहीं करता है; यह केवल GLAXALVAR के पते को EAX में ले जाता है।
कज़

@ आपकी प्रतिक्रिया के लिए धन्यवाद। मेरा स्रोत था "एलईए (प्रभावी पते को लोड करना) अनिवार्य रूप से एक अंकगणितीय अनुदेश है - यह किसी भी वास्तविक मेमोरी एक्सेस का प्रदर्शन नहीं करता है, लेकिन आमतौर पर पते की गणना के लिए उपयोग किया जाता है (हालांकि आप इसके साथ सामान्य उद्देश्य पूर्णांक की गणना कर सकते हैं)।" फार्म एल्डैड-ईलम पुस्तक पृष्ठ 149
लेखाकार

@ काज: यही कारण है कि जब पते पहले से ही लिंक-टाइम स्थिरांक है, तो LEA बेमानी है; mov eax, offset GLOBALVARइसके बजाय का उपयोग करें । आप LEA का उपयोग कर सकते हैं, लेकिन इसकी तुलना में यह थोड़ा बड़ा कोड-आकार है mov r32, imm32और कम बंदरगाहों पर चलता है, क्योंकि यह अभी भी पता-गणना प्रक्रिया से गुजरता हैlea reg, symbolकेवल RIP- सापेक्ष LEA के लिए 64-बिट में उपयोगी है, जब आपको निम्न 32 बिट्स के बाहर PIC और / या पते की आवश्यकता होती है। 32 या 16-बिट कोड में, शून्य लाभ है। LEA एक अंकगणितीय निर्देश है जो सीपीयू की क्षमता को संबोधित मोड को डिकोड / कंप्यूट करने के लिए उजागर करता है।
पीटर कॉर्डेस

@ काज: उसी तर्क से, आप कह सकते हैं कि imul eax, edx, 1यह गणना नहीं करता है: यह सिर्फ edx को eax पर कॉपी करता है। लेकिन वास्तव में यह 3 चक्र विलंबता के साथ आपके डेटा को गुणक के माध्यम से चलाता है। या कि rorx eax, edx, 0सिर्फ प्रतियां (शून्य से घुमाएं)।
पीटर कॉर्डेस

@PeterCordes मेरा कहना है कि LEA EAX, GLOBALVAL और MOV EAX, GLOBALVAR दोनों ही एक तत्काल ऑपरेंड से पते को पकड़ते हैं। 1 का कोई गुणक नहीं है, या 0 का ऑफसेट लागू किया जा रहा है; यह हार्डवेयर स्तर पर ऐसा हो सकता है लेकिन यह असेंबली भाषा या निर्देश सेट में नहीं देखा गया है।
काज

1

सभी सामान्य "गणना" निर्देश जैसे गुणा, अनन्य जोड़ने या स्थिति के झंडे को सेट करें जैसे शून्य, साइन। यदि आप एक जटिल पते का उपयोग करते हैं, AX xor:= mem[0x333 +BX + 8*CX] तो झंडे एक्सोर ऑपरेशन के अनुसार सेट किए जाते हैं।

अब आप कई बार पते का उपयोग करना चाह सकते हैं। एक रजिस्टर में इस तरह के अतिरिक्त को लोड करना कभी भी स्टेटस फ्लैग सेट करने का इरादा नहीं करता है और सौभाग्य से यह नहीं है। वाक्यांश "लोड प्रभावी पता" प्रोग्रामर को इससे अवगत कराता है। यह वह जगह है जहाँ अजीब अभिव्यक्ति से आता है।

यह स्पष्ट है कि एक बार प्रोसेसर अपनी सामग्री को संसाधित करने के लिए जटिल पते का उपयोग करने में सक्षम है, यह अन्य उद्देश्यों के लिए गणना करने में सक्षम है। वास्तव में इसका उपयोग x <- 3*x+1एक निर्देश में परिवर्तन करने के लिए किया जा सकता है । यह असेंबली प्रोग्रामिंग में एक सामान्य नियम है: निर्देशों का उपयोग करें हालांकि यह आपकी नाव को हिलाता है। केवल एक चीज जो मायने रखती है, वह है कि निर्देश द्वारा सन्निहित विशेष परिवर्तन आपके लिए उपयोगी है।

जमीनी स्तर

MOV, X| T| AX'| R| BX|

तथा

LEA, AX'| [BX]

AX पर समान प्रभाव है लेकिन स्टेटस फ्लैग पर नहीं। (यह सियासिस संकेतन है।)


"यह विधानसभा प्रोग्रामिंग में एक सामान्य नियम है: निर्देशों का उपयोग करें हालांकि यह आपकी नाव को हिलाता है।" मैं व्यक्तिगत रूप से उस सलाह को हाथ नहीं लगाऊंगा, जो call lbl lbl: pop raxतकनीकी रूप से "काम" जैसी चीजों के मूल्य के रूप में प्राप्त करता है rip, लेकिन आप शाखा की भविष्यवाणी को बहुत दुखी करेंगे। निर्देशों का उपयोग करें जो आप चाहते हैं, लेकिन अगर आप कुछ मुश्किल करते हैं तो आश्चर्यचकित न हों और इसके परिणाम आपके सामने नहीं हैं
The6P4C

@ The6P4C यह एक उपयोगी चेतावनी है। हालांकि अगर शाखा की भविष्यवाणी को दुखी करने का कोई विकल्प नहीं है, तो इसके लिए एक को जाना होगा। असेंबली प्रोग्रामिंग में एक और सामान्य नियम है। कुछ करने के लिए वैकल्पिक तरीके हो सकते हैं और आपको विकल्पों में से बुद्धिमानी से चुनना होगा। रजिस्टर AL के लिए रजिस्टर BL की सामग्री प्राप्त करने के लिए सैकड़ों तरीके हैं। यदि RAX के शेष को संरक्षित करने की आवश्यकता नहीं है, तो LEA एक विकल्प हो सकता है। हजारों प्रकार के x86 प्रोसेसर में से कुछ पर झंडे को प्रभावित नहीं करना एक अच्छा विचार हो सकता है। Groetjes अल्बर्ट
Albert van der Horst

-1

यदि कोई पहले ही उल्लेख कर चुका हो, तो मुझे माफ़ कर देना, लेकिन x86 के दिनों में जब मेमोरी सेगमेंटेशन अभी भी प्रासंगिक था, तो आपको इन दोनों निर्देशों से समान परिणाम नहीं मिल सकते हैं:

LEA AX, DS:[0x1234]

तथा

LEA AX, CS:[0x1234]

1
"प्रभावी पता" एक seg:offजोड़ी का "ऑफसेट" हिस्सा है । LEA खंड आधार से प्रभावित नहीं है; उन दोनों निर्देशों को (अक्षम रूप से) 0x1234AX में डाला जाएगा। x86 दुर्भाग्य से एक रजिस्टर या रजिस्टर-जोड़ी में पूर्ण रैखिक पते (प्रभावी + खंड आधार) की गणना करने का एक आसान तरीका नहीं है।
पीटर कॉर्डेस

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