วัตถุประสงค์ของการเรียนการสอน LEA คืออะไร?


676

สำหรับฉันดูเหมือนว่าเป็น MOV ที่ขี้ขลาด วัตถุประสงค์คืออะไรและควรใช้เมื่อใด


2
ดูเพิ่มเติมที่การใช้ LEA กับค่าที่ไม่ใช่ที่อยู่ / ตัวชี้? : LEA เป็นเพียงคำสั่งแบบกะและเพิ่ม มันอาจถูกเพิ่มไปที่ 8086 เพราะฮาร์ดแวร์มีอยู่แล้วเพื่อถอดรหัสและคำนวณโหมดการกำหนดแอดเดรสไม่ใช่เพราะมัน "ตั้งใจ" เท่านั้นสำหรับใช้กับที่อยู่ จำไว้ว่าพอยน์เตอร์เป็นเพียงจำนวนเต็มในการประกอบ
Peter Cordes

คำตอบ:


798

ตามที่คนอื่น ๆ ได้ชี้ให้เห็น LEA (ที่อยู่ที่มีประสิทธิภาพในการโหลด) มักใช้เป็น "เคล็ดลับ" เพื่อทำการคำนวณบางอย่าง แต่นั่นไม่ใช่จุดประสงค์หลัก ชุดการเรียนการสอน x86 ได้รับการออกแบบมาเพื่อรองรับภาษาระดับสูงเช่น Pascal และ C โดยที่อาร์เรย์ - โดยเฉพาะอาร์เรย์ของ ints หรือ structs ขนาดเล็ก - เป็นเรื่องปกติ พิจารณาตัวอย่างเช่นโครงสร้างที่เป็นตัวแทน (x, y) พิกัด:

struct Point
{
     int xcoord;
     int ycoord;
};

ตอนนี้คิดว่าคำสั่งเช่น:

int y = points[i].ycoord;

ที่เป็นอาร์เรย์ของpoints[] Pointสมมติว่าฐานของอาร์เรย์มีอยู่แล้วEBXและตัวแปรiอยู่ในEAXและxcoordและycoordแต่ละบิต 32 บิต (ดังนั้นycoordจะอยู่ที่ออฟเซ็ต 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
Natan Yellin

14
@imacake โดยการแทนที่ LEA ด้วย MOV แบบพิเศษคุณจะทำให้ไวยากรณ์สะอาดอยู่เสมอ: [] วงเล็บเหลี่ยมจะเท่ากับการลดการชี้พอยน์เตอร์ใน C เสมอหากไม่มีวงเล็บเหลี่ยมคุณจะต้องจัดการกับพอยน์เตอร์เสมอ
Natan Yellin

139
การทำคณิตศาสตร์ในคำสั่ง MOV (EBX + 8 * EAX + 4) ไม่ถูกต้อง LEA ESI, [EBX + 8 * EAX + 4] ใช้ได้เนื่องจากเป็นโหมดการกำหนดแอดเดรสที่ x86 รองรับ en.wikipedia.org/wiki/X86#Addressing_modes
Erik

29
@JonathanDickinson LEA เป็นเหมือนMOVกับแหล่งอ้อมยกเว้นก็เพียง MOVแต่ความร้ายและไม่ ไม่ได้อ่านจากที่อยู่ที่คำนวณได้จริงเพียงคำนวณมัน
ฮอบส์

24
เอริคความคิดเห็นเกี่ยวกับการท่องเที่ยวไม่ถูกต้อง MOV eax, [ebx + 8 * ecx + 4] นั้นถูกต้อง อย่างไรก็ตาม MOV จะส่งคืนเนื้อหาของตำแหน่งหน่วยความจำส่วนที่ในขณะที่ LEA ส่งคืนที่อยู่
Olorin

562

จาก"Zen of Assembly"โดย 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 ](N สามารถเป็น 1,2,4,8)

กรณีอื่นคือประโยชน์ในลูป: ความแตกต่างระหว่างLEA EAX, [ EAX + 1 ]และINC EAXคือการเปลี่ยนแปลงที่หลังEFLAGSแต่อดีตไม่ได้; นี้จะรักษาCMPสถานะ


42
@AbidRahmanK ตัวอย่างบางส่วน: LEA EAX, [ EAX + EBX + 1234567 ]คำนวณผลรวมของEAX, EBXและ1234567(ที่สามตัวถูกดำเนินการ) LEA EAX, [ EBX + ECX ]คำนวณEBX + ECX โดยไม่มีการแทนที่ด้วยผลลัพธ์ สิ่งที่สามLEAใช้สำหรับ (ไม่ได้ระบุไว้โดย Frank) คือการคูณด้วยค่าคงที่ (สอง, สาม, ห้าหรือเก้า) ถ้าคุณใช้มันเช่นLEA EAX, [ EBX + N * EBX ]( Nอาจเป็น 1,2,4,8) กรณีอื่นคือประโยชน์ในลูป: ความแตกต่างระหว่างLEA EAX, [ EAX + 1 ]และINC EAXคือการเปลี่ยนแปลงที่หลังEFLAGSแต่อดีตไม่ได้; นี้จะรักษาCMPสถานะ
FrankH

@FrankH ฉันยังไม่เข้าใจจึงโหลดตัวชี้ไปยังที่อื่นได้หรือไม่

6
@ ripDaddy69 ใช่เรียงลำดับของ - ถ้าโดย "โหลด" คุณหมายถึง "ทำการคำนวณที่อยู่ / เลขคณิตตัวชี้" มันไม่เข้าถึงหน่วยความจำ (เช่นไม่ใช่ตัวชี้ "dereference" ตามที่มันถูกเรียกในคำศัพท์การเขียนโปรแกรม C)
FrankH

2
+1: สิ่งนี้ทำให้ชัดเจนว่า 'เคล็ดลับ' ชนิดใดที่LEAสามารถใช้สำหรับ ... (ดู "LEA (ที่อยู่ที่มีประสิทธิภาพในการโหลด) มักใช้เป็น" เคล็ดลับ "เพื่อทำการคำนวณบางอย่าง" ในคำตอบยอดนิยมของ IJ Kennedy ด้านบน)
Assad Ebrahim

3
มีความแตกต่างใหญ่ระหว่าง 2 โอเปอแรนท์ LEA ซึ่งเร็วและโอเปอเรเตอร์ 3 ตัวที่ช้า คู่มือการเพิ่มประสิทธิภาพของ Intel กล่าวว่าเส้นทางที่รวดเร็ว LEA เป็นรอบเดียวและเส้นทางที่ช้า LEA ใช้เวลาสามรอบ ยิ่งไปกว่านั้นใน Skylake มีหน่วยการทำงานที่รวดเร็วสองทาง (พอร์ต 1 และ 5) และมีเพียงหนึ่งหน่วยการทำงานที่มีเส้นทางเดินช้า (พอร์ต 1) แอสเซมบลี / คอมไพเลอร์การเข้ารหัสกฎ 33 ในคู่มือแม้เตือนการใช้ 3 ตัวถูกดำเนินการ LEA
Olsonist

110

คุณสมบัติที่สำคัญอีกประการของการLEAเรียนการสอนคือมันไม่ได้เปลี่ยนรหัสเงื่อนไขเช่นCFและZFในขณะที่คำนวณที่อยู่ด้วยคำแนะนำทางคณิตศาสตร์เช่นADDหรือMULไม่ คุณลักษณะนี้จะลดระดับการพึ่งพาระหว่างคำแนะนำและทำให้มีที่ว่างสำหรับการปรับแต่งเพิ่มเติมโดยคอมไพเลอร์หรือตัวกำหนดเวลาฮาร์ดแวร์


1
ใช่leaบางครั้งมีประโยชน์สำหรับคอมไพเลอร์ (หรือมนุษย์ coder) ที่จะทำคณิตศาสตร์โดยไม่ต้องปิดกั้นผลธง แต่ไม่ได้เร็วกว่าlea addคำแนะนำ x86 ส่วนใหญ่เขียนการตั้งค่าสถานะ การใช้งาน x86 ที่มีประสิทธิภาพสูงต้องเปลี่ยนชื่อ EFLAGS หรือหลีกเลี่ยงอันตรายจากการเขียนหลังจากเขียนเพื่อให้รหัสปกติทำงานได้อย่างรวดเร็วดังนั้นคำแนะนำที่หลีกเลี่ยงการเขียนแบบธงจะไม่ดีขึ้นเพราะสิ่งนั้น ( สิ่งที่ตั้งค่าสถานะบางส่วนสามารถสร้างปัญหาให้ดูที่การเรียนการสอน INC เทียบกับเพิ่ม 1: มันเป็นเรื่องสำคัญ? )
Peter Cordes

2
@PeterCordes: เกลียดที่จะนำสิ่งนี้มาที่นี่ แต่ - ฉันอยู่คนเดียวในความคิดแท็ก [x86-lea] ใหม่นี้ซ้ำซ้อนและไม่จำเป็น?
Michael Petch

2
@MichaelPetch: ใช่ฉันคิดว่ามันเจาะจงเกินไป ดูเหมือนว่าผู้เริ่มต้นสับสนที่ไม่เข้าใจภาษาเครื่องและทุกอย่าง (รวมถึงพอยน์เตอร์) เป็นเพียงบิต / ไบต์ / จำนวนเต็มดังนั้นจึงมีคำถามมากมายเกี่ยวกับเรื่องนี้ด้วยคะแนนเสียงจำนวนมาก แต่การมีแท็กสำหรับมันบ่งบอกว่ามีที่ว่างสำหรับคำถามปลายเปิดจำนวนเมื่อในความเป็นจริงมีทั้งหมดประมาณ 2 หรือ 3 ทั้งหมดที่ไม่ได้ซ้ำกัน (? มันคืออะไรวิธีที่จะใช้สำหรับการคูณจำนวนเต็มและวิธีการทำงานภายในบน Agus กับ ALUs และกับสิ่งที่แฝง / ผ่านและบางทีมันอาจจะเป็น "ตั้งใจ" วัตถุประสงค์?.)
ปีเตอร์ Cordes

@PeterCordes: ฉันเห็นด้วยและหากมีสิ่งใดที่โพสต์ทั้งหมดเหล่านี้ได้รับการแก้ไขจะค่อนข้างซ้ำซ้อนกับคำถาม LEA ที่เกี่ยวข้อง แทนที่จะระบุแท็กรายการซ้ำใด ๆ ควรระบุและทำเครื่องหมาย imho
Michael Petch

1
@EvanCarroll: ติดแท็กคำถาม LEA ทั้งหมดหากคุณยังไม่เสร็จ ตามที่กล่าวไว้ข้างต้นเราคิดว่าx86-leaเจาะจงเกินไปสำหรับแท็กและไม่มีขอบเขตสำหรับคำถามที่ไม่ซ้ำซ้อนในอนาคต ฉันคิดว่ามันจะทำงานมากไปจริงเลือก "ดีที่สุด" Q & A เป็นเป้าหมาย dup สำหรับส่วนมากของพวกเขาแม้ว่าหรือที่จริงการตัดสินใจว่าคนที่จะได้รับวัยรุ่นที่จะผสาน
Peter Cordes

93

แม้จะมีคำอธิบายทั้งหมด LEA คือการดำเนินการทางคณิตศาสตร์:

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

มันเป็นเพียงชื่อที่โง่สุดขั้วสำหรับการดำเนินการ shift + add เหตุผลที่อธิบายไว้ในคำตอบที่ได้รับความนิยมสูงสุด (เช่นถูกออกแบบมาเพื่อแมปการอ้างอิงหน่วยความจำระดับสูงโดยตรง)


8
และการคำนวณทางคณิตศาสตร์นั้นดำเนินการโดยฮาร์ดแวร์การคำนวณที่อยู่
Ben Voigt

30
@ BenVoigt ฉันเคยบอกว่าเพราะฉันเป็นเจ้าหมอเก่า :-) ตามเนื้อผ้า x86 ซีพียูใช้หน่วยที่อยู่สำหรับเรื่องนี้ตกลง แต่การแยก "" ได้กลายเป็นพร่ามัววันนี้ ซีพียูบางตัวไม่มีAGU เฉพาะอีกต่อไปแล้วบางตัวเลือกที่จะไม่ดำเนินการLEAกับ AGUs แต่ใช้กับเลขจำนวนเต็ม ALU ปกติ เราต้องอ่านรายละเอียดซีพียูอย่างใกล้ชิดในวันนี้เพื่อค้นหา "สิ่งที่เรียกใช้" ...
FrankH

2
@FrankH: โดยปกติแล้วซีพียูที่ล้าสมัยจะเรียกใช้ LEA ใน ALUs ในขณะที่บาง CPU เรียงตามลำดับ (เช่น Atom) บางครั้งเรียกใช้บน AGUs (เพราะพวกเขาไม่สามารถจัดการการเข้าถึงหน่วยความจำได้)
Peter Cordes

3
ไม่ชื่อไม่ได้โง่ LEAให้ที่อยู่ซึ่งเกิดขึ้นจากโหมดการกำหนดแอดเดรสที่เกี่ยวข้องกับหน่วยความจำ มันไม่ได้เป็นกะและเพิ่มการดำเนินงาน
Kaz

3
FWIW มีซีพียู x86 ปัจจุบันน้อยมาก (ถ้ามี) ที่ดำเนินการกับ AGU ส่วนใหญ่หรือทั้งหมดเพียงใช้ ALU เหมือนกับ op เลขคณิตอื่น ๆ
BeeOnRope

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]?
Abid Rahman K

13
@Abid Rahman K: ไม่มีชุดคำสั่ง unde x86 CPU ชุดคำสั่ง
GJ

50
@AbidRahmanK แม้จะมีไวยากรณ์ของ Intel asm ทำให้ดูเหมือนการคูณ แต่คำสั่ง lea สามารถเข้ารหัสการดำเนินการแบบเลื่อนเท่านั้น opcode มี 2 บิตเพื่ออธิบายการเปลี่ยนแปลงดังนั้นคุณสามารถคูณได้เพียง 1,2,4 หรือ 8
ithkuil

6
@Koray Tugay: คุณสามารถใช้กะซ้ายเช่นshlคำแนะนำสำหรับการคูณการลงทะเบียน 2,4,8,16 ... มันเร็วขึ้นและสั้นลง แต่สำหรับการคูณด้วยจำนวนที่แตกต่างกันของกำลังสองเราใช้mulคำสั่งปกติซึ่งอวดอ้างมากกว่าและช้ากว่า
GJ

7
@GJ แม้ว่าจะไม่มีการเข้ารหัสดังกล่าว แต่ผู้ประกอบการบางคนยอมรับว่านี่เป็นทางลัดเช่น fasm ดังนั้นเช่นจะแปลเทียบเท่าlea eax,[eax*3] lea eax,[eax+eax*2]
Ruslan

59

leaเป็นตัวย่อของ "address effective load" มันโหลดที่อยู่ของการอ้างอิงตำแหน่งโดยตัวถูกดำเนินการแหล่งที่มาไปยังตัวถูกดำเนินการปลายทาง ตัวอย่างเช่นคุณสามารถใช้มันเพื่อ:

lea ebx, [ebx+eax*8]

เพื่อย้ายรายการebxตัวชี้eaxเพิ่มเติม (ในอาร์เรย์ 64- บิต / องค์ประกอบ) ด้วยคำสั่งเดียว โดยทั่วไปคุณจะได้รับประโยชน์จากโหมดการกำหนดแอดเดรสที่ซับซ้อนที่สนับสนุนโดยสถาปัตยกรรม x86 เพื่อจัดการกับพอยน์เตอร์ได้อย่างมีประสิทธิภาพ


23

เหตุผลที่ใหญ่ที่สุดที่คุณใช้LEAมากกว่า a MOVคือถ้าคุณต้องการคำนวณเลขคณิตในรีจิสเตอร์ที่คุณใช้ในการคำนวณที่อยู่ ได้อย่างมีประสิทธิภาพคุณสามารถดำเนินการสิ่งที่ยอดเงินให้ตัวชี้เลขคณิตในการลงทะเบียนหลายอย่างรวมกันได้อย่างมีประสิทธิภาพสำหรับ "ฟรี"

สิ่งที่ทำให้เกิดความสับสนเกี่ยวกับเรื่องนี้ก็คือโดยปกติแล้วคุณมักจะเขียนสิ่งที่LEAคล้ายกันMOVแต่คุณไม่ได้อ้างถึงความจำจริง ในคำอื่น ๆ :

MOV EAX, [ESP+4]

นี้จะย้ายเนื้อหาของสิ่งที่จุดที่จะเข้าสู่ESP+4EAX

LEA EAX, [EBX*8]

นี่จะย้ายที่อยู่ที่มีประสิทธิภาพEBX * 8ไปยัง EAX ไม่ใช่สิ่งที่พบในที่ตั้งนั้น อย่างที่คุณเห็นมีความเป็นไปได้ที่จะคูณด้วยปัจจัยสอง (การขยาย) ในขณะที่ a MOVถูก จำกัด ไว้ที่การเพิ่ม / การลบ


ขออภัยทุกคน @ big.heart หลอกฉันโดยให้คำตอบเมื่อสามชั่วโมงที่ผ่านมาทำให้มันปรากฏเป็น "ใหม่" ในคำถามชุดประชุมของฉัน
David Hoelzer

1
เหตุใดไวยากรณ์จึงใช้วงเล็บเหลี่ยมเมื่อไม่ได้กำหนดที่อยู่หน่วยความจำ
golopot

3
@ q4w56 นี่คือหนึ่งในสิ่งที่คำตอบคือ "นั่นเป็นวิธีที่คุณทำ" ฉันเชื่อว่าเป็นหนึ่งในเหตุผลที่ผู้คนมีเวลายากลำบากในการหาสิ่งที่LEAทำ
David Hoelzer

2
@ q4w56: เป็นคำสั่ง shift + add ที่ใช้ไวยากรณ์ตัวถูกดำเนินการหน่วยความจำและการเข้ารหัสรหัสเครื่อง สำหรับ CPU บางตัวอาจใช้ฮาร์ดแวร์ AGU แต่นั่นเป็นรายละเอียดที่ผ่านมา ข้อเท็จจริงที่เกี่ยวข้องยังคงมีอยู่ว่าฮาร์ดแวร์ตัวถอดรหัสมีอยู่แล้วสำหรับการถอดรหัส shift + add ชนิดนี้และ LEA ช่วยให้เราใช้มันสำหรับเลขคณิตแทนที่จะเป็นหน่วยความจำที่อยู่ (หรือสำหรับการคำนวณที่อยู่หากหนึ่งอินพุตเป็นตัวชี้)
Peter Cordes

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

สำหรับเหตุผลที่ Intel คิดว่าคำสั่งนี้มีค่ารวมถึงฉันไม่แน่ใจ แต่ความจริงที่ว่าราคาถูกในการติดตั้งอาจเป็นปัจจัยสำคัญ อีกปัจจัยหนึ่งก็คือความจริงที่ว่าแอสเซมเบลอร์ของ Intel อนุญาตให้มีการกำหนดสัญลักษณ์ที่สัมพันธ์กับการลงทะเบียน BP หากfnordถูกกำหนดเป็นสัญลักษณ์ที่สัมพันธ์กับ BP (เช่น BP + 8) เราสามารถพูดได้ว่า:

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

หากใครต้องการใช้บางอย่างเช่น stosw เพื่อจัดเก็บข้อมูลไปยังที่อยู่ที่สัมพันธ์กับความสามารถในการพูด

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

โปรดทราบว่าการลืมโลก "ชดเชย" จะทำให้เนื้อหาของสถานที่ตั้ง [BP + 8] แทนที่จะเพิ่มค่า 8 ลงใน DI อุ่ย


12

ดังที่คำตอบที่มีอยู่แล้วกล่าวถึงLEAข้อดีของการดำเนินการกับหน่วยความจำเลขคณิตโดยไม่ต้องเข้าถึงหน่วยความจำบันทึกผลลัพธ์ทางคณิตศาสตร์ไปยังรีจิสเตอร์อื่นแทนที่จะเป็นรูปแบบง่าย ๆ ของการเพิ่มคำสั่ง ประโยชน์ด้านประสิทธิภาพที่แท้จริงคือโปรเซสเซอร์ที่ทันสมัยมีหน่วย LEA ALU และพอร์ตแยกต่างหากสำหรับการสร้างที่อยู่ที่มีประสิทธิภาพ (รวมถึงLEAและที่อยู่อ้างอิงหน่วยความจำอื่น ๆ ) ซึ่งหมายความว่าการดำเนินการทางคณิตศาสตร์ในLEAและการดำเนินการทางคณิตศาสตร์อื่น ๆ ใน ALU แกน

ตรวจสอบบทความของสถาปัตยกรรม Haswell นี้เพื่อดูรายละเอียดเกี่ยวกับหน่วยงาน LEA: http://www.realworldtech.com/haswell-cpu/4/

อีกจุดที่สำคัญที่ไม่ได้กล่าวถึงในคำตอบอื่น ๆ คือLEA REG, [MemoryAddress]คำสั่งคือ PIC (รหัสอิสระของตำแหน่ง) ซึ่งเข้ารหัสที่อยู่ PC ญาติในการอ้างอิงMemoryAddressนี้ สิ่งนี้แตกต่างจากการMOV REG, MemoryAddressเข้ารหัสที่อยู่เสมือนแบบสัมพัทธ์และต้องการการย้าย / แก้ไขในระบบปฏิบัติการสมัยใหม่ (เช่น ASLR เป็นคุณสมบัติทั่วไป) ดังนั้นLEAสามารถใช้ในการแปลง PIC ที่ไม่ใช่เช่น PIC


2
ส่วน "แยก LEA ALU" ส่วนใหญ่ไม่เป็นความจริง Modern CPUs ดำเนินleaการกับ ALU เดียวกันหนึ่งตัวหรือมากกว่าที่รันคำสั่งทางคณิตศาสตร์อื่น ๆ (แต่โดยทั่วไปจะน้อยกว่าเลขคณิตอื่น ๆ ) ตัวอย่างเช่น Haswell CPU ที่กล่าวถึงสามารถดำเนินการaddหรือsubการดำเนินการทางคณิตศาสตร์ขั้นพื้นฐานอื่น ๆ ส่วนใหญ่ในสี่ ALUs ที่แตกต่างกันแต่สามารถดำเนินการleaในหนึ่ง (ซับซ้อนlea) หรือสอง (ง่ายlea) ที่สำคัญกว่านั้นleaALU ที่มีความสามารถสองตัวนั้นเป็นเพียงสองในสี่ที่สามารถดำเนินการคำสั่งอื่น ๆ ได้ดังนั้นจึงไม่มีประโยชน์ในการขนานที่อ้างว่า
BeeOnRope

บทความที่คุณเชื่อมโยง (ถูกต้อง) แสดงว่า LEA อยู่บนพอร์ตเดียวกันกับจำนวนเต็ม ALU (เพิ่ม / sub / boolean) และหน่วย MUL จำนวนเต็มใน Haswell (และ ALU ของเวกเตอร์รวมถึง FP ADD / MUL / FMA) หน่วย LEA แบบง่าย ๆ เท่านั้นที่อยู่บนพอร์ต 5 ซึ่งเรียกใช้ ADD / SUB / อะไรก็ตามและ vector shuffles และอื่น ๆ เหตุผลเดียวที่ฉันไม่ได้ลงคะแนนคือคุณชี้ให้เห็นถึงการใช้งาน LEA ที่สัมพันธ์กับ RIP (สำหรับ x86-64 เท่านั้น)
Peter Cordes

8

คำสั่ง LEA สามารถใช้เพื่อหลีกเลี่ยงการคำนวณที่อยู่ที่มีประสิทธิภาพโดย CPU หากมีการใช้ที่อยู่ซ้ำ ๆ จะมีประสิทธิภาพมากกว่าในการจัดเก็บไว้ในการลงทะเบียนแทนการคำนวณที่อยู่ที่มีประสิทธิภาพทุกครั้งที่มีการใช้งาน


ไม่จำเป็นต้องทันสมัย ​​x86 โหมดการกำหนดแอดเดรสส่วนใหญ่มีค่าใช้จ่ายเท่ากันโดยมีข้อ จำกัด บางประการ ดังนั้น[esi]จะไม่ค่อยมีราคาถูกกว่าการพูดและเป็นเพียงไม่ค่อยมีราคาถูกกว่า[esi + 4200] [esi + ecx*8 + 4200]
BeeOnRope

@BeeOnRope ไม่ได้ราคาถูกกว่า[esi] [esi + ecx*8 + 4200]แต่ทำไมต้องเปรียบเทียบ พวกเขาจะไม่เทียบเท่า ถ้าคุณต้องการให้อดีตกำหนดตำแหน่งหน่วยความจำเดียวกันกับหลังคุณต้องการคำแนะนำเพิ่มเติม: คุณต้องเพิ่มesiค่าของการecxคูณด้วย 8 เอ๊ะโอ้การคูณจะทำให้ธง CPU ของคุณแย่ลง! จากนั้นคุณต้องเพิ่ม 4200 คำแนะนำเพิ่มเติมเหล่านี้จะเพิ่มขนาดรหัส (ใช้พื้นที่ในแคชคำสั่ง, รอบเพื่อดึงข้อมูล)
Kaz

2
@Kaz - ฉันคิดว่าคุณพลาดจุดของฉัน (หรือไม่เช่นนั้นฉันพลาดจุดของ OP) ความเข้าใจของฉันคือว่า OP กำลังบอกว่าถ้าคุณจะใช้บางอย่างเช่น[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]ซึ่งไม่ค่อยเร็วกว่า อย่างน้อยนั่นเป็นการตีความที่ธรรมดาของคำตอบนี้
BeeOnRope

ดังนั้นเหตุผลที่ฉันได้รับการเปรียบเทียบ[esi]และ[esi + 4200](หรือ[esi + ecx*8 + 4200]เป็นที่นี้ทำให้เข้าใจง่าย OP จะเสนอ (ตามที่ผมเข้าใจมัน): ว่าคำแนะนำ N กับที่อยู่ที่ซับซ้อนเหมือนกันจะกลายเป็นคำแนะนำ N กับง่าย (หนึ่ง reg) ที่อยู่, บวกหนึ่งlea, เนื่องจากการระบุที่ซับซ้อนคือ "ใช้เวลานาน" ในความเป็นจริงมันช้าลงแม้ในรุ่น x86 ที่ทันสมัย ​​แต่มีความหน่วงแฝงที่ไม่น่าจะมีความสำคัญสำหรับคำสั่งที่ต่อเนื่องซึ่งมีที่อยู่เดียวกัน
BeeOnRope

1
บางทีคุณอาจลดความกดดันในการลงทะเบียนใช่ - แต่ตรงกันข้ามอาจเป็นกรณี: หากการลงทะเบียนที่คุณสร้างที่อยู่ที่มีประสิทธิภาพนั้นเป็นรายการสดคุณต้องลงทะเบียนอีกครั้งเพื่อบันทึกผลลัพธ์leaเพื่อเพิ่มความกดดันในกรณีนั้น โดยทั่วไปการจัดเก็บตัวกลางเป็นสาเหตุของความกดดันในการลงทะเบียนไม่ใช่วิธีแก้ปัญหา แต่ฉันคิดว่าในสถานการณ์ส่วนใหญ่จะเป็นการล้าง @Kaz
BeeOnRope

7

คำสั่ง LEA (Load Effective Address) เป็นวิธีการรับที่อยู่ซึ่งเกิดขึ้นจากโหมดการกำหนดแอดเดรสหน่วยความจำของโปรเซสเซอร์ Intel

กล่าวคือหากเรามีการย้ายข้อมูลดังนี้:

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.

สิ่งนี้ถูกต้อง; เราสามารถทดสอบได้ที่พร้อมท์ Linux:

$ 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 เราสามารถทำได้โดยใช้ MOV กับตัวถูกดำเนินการทันที

นี่คือเหตุผลที่คนที่คิดว่าวงเล็บในLEAฟุ่มเฟือยจะเข้าใจผิดอย่างรุนแรง; วงเล็บไม่ใช่LEAไวยากรณ์ แต่เป็นส่วนหนึ่งของโหมดการกำหนดแอดเดรส

หน่วยงาน LEA เป็นของจริงในระดับฮาร์ดแวร์ คำสั่งที่สร้างขึ้นจะเข้ารหัสโหมดการกำหนดแอดเดรสจริงและโปรเซสเซอร์ดำเนินการจนถึงจุดที่คำนวณที่อยู่ จากนั้นจะย้ายที่อยู่นั้นไปยังปลายทางแทนที่จะสร้างการอ้างอิงหน่วยความจำ (เนื่องจากการคำนวณที่อยู่ของโหมดการกำหนดแอดเดรสในคำสั่งอื่นไม่มีผลกับแฟล็ก CPU LEAจึงไม่มีผลกับแฟล็ก CPU)

ตรงกันข้ามกับการโหลดค่าจากศูนย์ที่อยู่:

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

มันเป็นการเข้ารหัสที่คล้ายกันมากเห็นไหม เพียงแค่8dการมีการเปลี่ยนแปลงไปLEA8b

แน่นอนว่าการ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;
}

ด้วย -O (ปรับให้เหมาะสม) เป็นตัวเลือกคอมไพเลอร์ gcc จะค้นหาคำสั่ง lea สำหรับบรรทัดโค้ดที่ระบุ


6

ดูเหมือนว่าคำตอบจำนวนมากเสร็จสมบูรณ์แล้วฉันต้องการเพิ่มโค้ดตัวอย่างอีกหนึ่งตัวอย่างเพื่อแสดงว่าคำสั่งการย้ายและการย้ายทำงานแตกต่างกันอย่างไรเมื่อพวกเขามีรูปแบบนิพจน์เหมือนกัน

เพื่อให้เนื้อเรื่องสั้น ๆ คุณสามารถใช้คำสั่ง lea และคำสั่ง mov ได้พร้อมกับวงเล็บที่ล้อมรอบ src operand ของคำสั่ง เมื่อมีการล้อมรอบด้วย()นิพจน์ใน()จะถูกคำนวณด้วยวิธีเดียวกัน อย่างไรก็ตามสองคำสั่งจะตีความค่าที่คำนวณได้ในตัวถูกดำเนินการ src ในวิธีที่ต่างกัน

ไม่ว่าจะเป็นนิพจน์ที่ใช้กับ lea หรือ mov ค่า src จะถูกคำนวณดังนี้

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

อย่างไรก็ตามเมื่อใช้กับคำสั่ง mov มันจะพยายามเข้าถึงค่าที่ชี้ไปตามที่อยู่ที่สร้างโดยนิพจน์ด้านบนและจัดเก็บไปยังปลายทาง

ในทางตรงกันข้ามเมื่อคำสั่ง lea ถูกดำเนินการด้วยนิพจน์ด้านบนมันจะโหลดค่าที่สร้างขึ้นตามไปยังปลายทาง

โค้ดด้านล่างดำเนินการคำสั่ง lea และคำสั่ง mov ด้วยพารามิเตอร์เดียวกัน อย่างไรก็ตามเพื่อจับความแตกต่างฉันได้เพิ่มตัวจัดการสัญญาณระดับผู้ใช้เพื่อตรวจจับข้อผิดพลาดในการแบ่งเซ็กเมนต์ที่เกิดจากการเข้าถึงที่อยู่ผิดเนื่องจากคำสั่ง 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 แบบอินไลน์ของคุณออกเป็นคำสั่งแยกต่างหากนั้นไม่ปลอดภัยและรายการตัวแยกของคุณไม่สมบูรณ์ Basic-asm block บอกคอมไพเลอร์ว่าไม่มีตัวบล็อก แต่จริง ๆ แล้วมันจะทำการแก้ไขหลายรีจิสเตอร์ นอกจากนี้คุณยังสามารถใช้=dเพื่อบอกคอมไพเลอร์ผลที่ได้คือใน EDX, movประหยัด คุณยังคงออกประกาศการอุดตันก่อนหน้านี้ในการส่งออก สิ่งนี้แสดงให้เห็นถึงสิ่งที่คุณพยายามแสดง แต่ก็เป็นตัวอย่างที่ไม่ดีของอินไลน์ asm ที่ทำให้เข้าใจผิดซึ่งจะแตกถ้าใช้ในบริบทอื่น ๆ นั่นเป็นสิ่งที่ไม่ดีสำหรับคำตอบล้นสแต็ค
ปีเตอร์

หากคุณไม่ต้องการเขียน%%ชื่อลงทะเบียนทั้งหมดใน Extended asm ให้ใช้ข้อ จำกัด การป้อนข้อมูล asm("lea 4(%%ebx, %%eax, 8), %%edx" : "=d"(result_of_lea) : "a"(1), "b"(2));เช่น การให้ผู้ลงทะเบียนคอมไพเลอร์เริ่มต้นหมายความว่าคุณไม่ต้องประกาศตัวอุดตัน คุณกำลังทำเรื่องซับซ้อนด้วย xor-zeroing ก่อนที่ mov-ทันทีจะเขียนทับการลงทะเบียนทั้งหมดเช่นกัน
Peter Cordes

@PeterCordes ขอบคุณ Peter คุณต้องการให้ฉันลบคำตอบนี้หรือแก้ไขตามความคิดเห็นของคุณ?
Jaehyuk Lee

1
หากคุณแก้ไข inline asm มันจะไม่ทำอันตรายใด ๆ และอาจเป็นตัวอย่างที่ดีสำหรับผู้เริ่มต้นที่ไม่เข้าใจคำตอบอื่น ๆ ไม่จำเป็นต้องลบและเป็นการแก้ไขที่ง่ายเหมือนที่ฉันแสดงในความคิดเห็นล่าสุด ฉันคิดว่ามันจะคุ้มค่าการ upvote ถ้าตัวอย่างที่ไม่ดีของ inline asm ได้รับการแก้ไขเป็นตัวอย่าง "ดี" (ฉันไม่ได้ลงคะแนน)
Peter Cordes

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 ย้ายข้อมูลอย่างชัดเจน มันมีตัวถูกดำเนินการปลายทาง หน่วยงาน LEA ไม่ได้คำนวณเสมอไป มันคำนวณว่าที่อยู่ที่มีประสิทธิภาพแสดงในตัวถูกดำเนินการแหล่งที่มาคำนวณ LEA EAX, GLOBALVAR ไม่คำนวณ; มันเพิ่งย้ายที่อยู่ของ GLOBALVAR ไปยัง EAX
Kaz

@ Kaz ขอบคุณสำหรับคำติชมของคุณ แหล่งที่มาของฉันคือ "LEA (ที่อยู่ที่มีประสิทธิภาพในการโหลด) นั้นเป็นคำสั่งทางคณิตศาสตร์ - มันไม่ได้ทำการเข้าถึงหน่วยความจำที่แท้จริง แต่โดยทั่วไปจะใช้สำหรับการคำนวณที่อยู่ (แม้ว่าคุณจะสามารถคำนวณ แบบฟอร์มหนังสือ Eldad-Eilamหน้า 149
นักบัญชี

@Kaz: นั่นคือเหตุผลที่ LEA ซ้ำซ้อนเมื่อที่อยู่เป็นค่าคงที่ของลิงก์เวลาอยู่แล้ว ใช้mov eax, offset GLOBALVARแทน คุณสามารถใช้ LEA แต่มันมีขนาดรหัสที่ใหญ่กว่าmov r32, imm32และทำงานในพอร์ตที่น้อยลงเล็กน้อยเนื่องจากยังคงผ่านกระบวนการคำนวณที่อยู่ lea reg, symbolมีประโยชน์เฉพาะใน 64 บิตสำหรับ LEA ที่สัมพันธ์กับ RIP เมื่อคุณต้องการ PIC และ / หรือที่อยู่นอก 32 บิตต่ำ ในรหัส 32 หรือ 16 บิตไม่มีข้อได้เปรียบ LEA เป็นคำสั่งทางคณิตศาสตร์ที่แสดงถึงความสามารถของ CPU ในการถอดรหัส / คำนวณโหมดการกำหนดแอดเดรส
Peter Cordes

@Kaz: ด้วยเหตุผลเดียวกันคุณสามารถพูดimul eax, edx, 1ได้ว่าไม่ได้คำนวณ: มันแค่คัดลอก edx ไปยัง eax แต่จริงๆแล้วมันจะเรียกใช้ข้อมูลของคุณผ่านตัวคูณด้วยเวลาแฝง 3 รอบ หรือrorx eax, edx, 0เพียงแค่คัดลอก (หมุนเป็นศูนย์)
Peter Cordes

@PeterCordes จุดของฉันคือทั้ง LEA EAX, GLOBALVAL และ MOV EAX, GLOBALVAR เพียงแค่ดึงที่อยู่จากตัวถูกดำเนินการทันที ไม่มีตัวคูณ 1 หรือชดเชยเป็น 0 อาจเป็นเช่นนั้นในระดับฮาร์ดแวร์ แต่ไม่เห็นในภาษาแอสเซมบลีหรือชุดคำสั่ง
Kaz

1

คำแนะนำ "การคำนวณ" ปกติทั้งหมดเช่นการเพิ่มการคูณแบบเอกสิทธิ์เฉพาะบุคคลหรือตั้งค่าสถานะสถานะเช่นศูนย์เครื่องหมาย หากคุณใช้แอดเดรสที่ซับซ้อนAX xor:= mem[0x333 +BX + 8*CX] แฟล็กจะถูกตั้งค่าตามการดำเนินการ xor

ตอนนี้คุณอาจต้องการใช้ที่อยู่หลายครั้ง การโหลดที่อยู่ดังกล่าวไปยังการลงทะเบียนไม่ได้มีจุดประสงค์เพื่อตั้งค่าสถานะและโชคดีที่ไม่มี วลี "ที่อยู่ที่มีประสิทธิภาพในการโหลด" ทำให้โปรแกรมเมอร์รับรู้สิ่งนั้น นั่นคือสิ่งที่การแสดงออกแปลก ๆ มาจาก

เป็นที่ชัดเจนว่าเมื่อโปรเซสเซอร์สามารถใช้ที่อยู่ที่ซับซ้อนในการประมวลผลเนื้อหาได้ก็สามารถคำนวณได้เพื่อวัตถุประสงค์อื่น แน่นอนมันสามารถใช้ในการทำการเปลี่ยนแปลงx <- 3*x+1ในคำสั่งเดียว นี่เป็นกฎทั่วไปในการเขียนโปรแกรมการประกอบ: ใช้คำแนะนำ แต่มันสั่นสะเทือนเรือของคุณ สิ่งเดียวที่นับได้ก็คือการเปลี่ยนแปลงที่เป็นตัวเป็นตนโดยคำสั่งนั้นมีประโยชน์สำหรับคุณหรือไม่

บรรทัดล่าง

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

และ

LEA, AX'| [BX]

มีผลเหมือนกันกับAXแต่ไม่ส่งผลต่อสถานะ (นี่คือสัญกรณ์ciasdis )


"นี่เป็นกฎทั่วไปในการเขียนโปรแกรมแอสเซมบลี: ใช้คำแนะนำ แต่จะเขย่าเรือของคุณ" ฉันจะไม่ส่งคำแนะนำนั้นเป็นการส่วนตัวโดยคำนึงถึงสิ่งต่าง ๆ เช่นcall lbl lbl: pop rax"การทำงาน" ในทางเทคนิคเพื่อให้ได้มาซึ่งคุณค่าripแต่คุณจะทำให้การคาดคะเนสาขาไม่มีความสุข ใช้คำแนะนำที่คุณต้องการ แต่ไม่ต้องแปลกใจถ้าคุณทำอะไรที่ยุ่งยากและมันมีผลตามมาที่คุณไม่คาดคิด
The6P4C

@ The6P4C นั่นเป็นข้อแม้ที่มีประโยชน์ อย่างไรก็ตามหากไม่มีทางเลือกอื่นที่จะทำให้การคาดคะเนสาขาไม่มีความสุขใครก็ต้องไปหามัน มีกฎทั่วไปอื่นในการเขียนโปรแกรมแอสเซมบลี อาจมีวิธีอื่นในการทำบางสิ่งบางอย่างและคุณต้องเลือกอย่างชาญฉลาดจากทางเลือกมีหลายร้อยวิธีในการรับเนื้อหาของ register BL เข้าสู่ register AL หากส่วนที่เหลือของ RAX ไม่จำเป็นต้องเก็บรักษาไว้ LEA อาจเป็นตัวเลือก การไม่ส่งผลกระทบต่อค่าสถานะอาจเป็นความคิดที่ดีสำหรับโปรเซสเซอร์ x86 บางประเภท Groetjes Albert
Albert van der Horst

-1

ยกโทษให้ฉันถ้ามีคนพูดถึงแล้ว แต่ในยุค x86 เมื่อการแบ่งส่วนหน่วยความจำยังคงมีความเกี่ยวข้องคุณอาจไม่ได้ผลลัพธ์เดียวกันจากคำแนะนำสองคำนี้:

LEA AX, DS:[0x1234]

และ

LEA AX, CS:[0x1234]

1
"ที่อยู่ที่มีประสิทธิภาพ" เป็นเพียงส่วน "ชดเชย" ของseg:offคู่ LEA ไม่ได้รับผลกระทบจากฐานกลุ่ม ทั้งคำแนะนำเหล่านั้น (อย่างไม่มีประสิทธิภาพ) จะใส่0x1234ลงใน AX x86 น่าเสียดายที่ไม่มีวิธีง่ายๆในการคำนวณที่อยู่เชิงเส้นแบบเต็ม (ฐาน + ส่วนที่มีประสิทธิภาพ) ลงในทะเบียนหรือคู่
Peter Cordes

@PeterCordes มีประโยชน์มากขอบคุณสำหรับการแก้ไขฉัน
tzoz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.