ฉันต้องการทราบความแตกต่างระหว่างคำแนะนำเหล่านี้:
MOV AX, [TABLE-ADDR]
และ
LEA AX, [TABLE-ADDR]
ฉันต้องการทราบความแตกต่างระหว่างคำแนะนำเหล่านี้:
MOV AX, [TABLE-ADDR]
และ
LEA AX, [TABLE-ADDR]
คำตอบ:
LEA
หมายถึงโหลดที่อยู่ที่มีประสิทธิภาพMOV
หมายถึง Load Valueในระยะสั้นLEA
โหลดตัวชี้ไปยังรายการที่คุณกำลังกล่าวถึงในขณะที่ MOV โหลดค่าจริงตามที่อยู่นั้น
จุดประสงค์LEA
คือเพื่อให้หนึ่งทำการคำนวณที่อยู่ที่ไม่สำคัญและเก็บผลลัพธ์ไว้ [สำหรับการใช้งานในภายหลัง]
LEA ax, [BP+SI+5] ; Compute address of value
MOV ax, [BP+SI+5] ; Load value at that address
ในกรณีที่มีค่าคงที่เพียงแค่มีส่วนร่วมMOV
(ผ่านการคำนวณอย่างต่อเนื่องของผู้ประกอบ) LEA
บางครั้งอาจดูเหมือนจะทับซ้อนกับกรณีที่ง่ายที่สุดของการใช้งานของ มีประโยชน์หากคุณมีการคำนวณหลายส่วนพร้อมที่อยู่ฐานหลายรายการเป็นต้น
LAHF
เป็น: ธงโหลดลงใน AH ลงทะเบียน ใน CIL ของ CLR (ซึ่งเป็นเครื่องนามธรรมที่ใช้สแต็กในระดับที่สูงขึ้นคำว่าloadหมายถึงการใส่ค่าลงในสแต็กเชิงสัญกรณ์และโดยปกติl
... และs
... เทียบเท่าจะผกผัน) หมายเหตุเหล่านี้: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) แนะนำว่ามีสถาปัตยกรรมที่ใช้ความแตกต่างของคุณ
ในไวยากรณ์ NASM:
mov eax, var == lea eax, [var] ; i.e. mov r32, imm32
lea eax, [var+16] == mov eax, var+16
lea eax, [eax*4] == shl eax, 2 ; but without setting flags
ในไวยากรณ์ MASM ใช้OFFSET var
เพื่อรับ mov ทันทีแทนการโหลด
mov eax, var
คือการโหลดเช่นเดียวกับmov eax, [var]
และคุณต้องใช้mov eax, OFFSET var
เพื่อใช้ป้ายกำกับเป็นค่าคงที่ทันที
lea
เป็นตัวเลือกที่แย่กว่ายกเว้นในโหมด 64 บิตสำหรับการกำหนดแอดเดรสแบบสัมพันธ์ RIP mov r32, imm32
ทำงานบนพอร์ตเพิ่มเติม lea eax, [edx*4]
เป็นสำเนาและกะซึ่งไม่สามารถทำได้ในคำสั่งเดียวเป็นอย่างอื่น แต่ในการลงทะเบียนเดียวกัน LEA ใช้เวลาในการเข้ารหัสมากกว่าไบต์เนื่องจาก[eax*4]
ต้องใช้ไฟล์disp32=0
. (มันทำงานบนพอร์ตที่แตกต่างกว่ากะแม้ว่า.) ดูagner.org/optimizeและstackoverflow.com/tags/x86/info
คำสั่ง MOV reg, addr หมายถึงอ่านตัวแปรที่เก็บไว้ที่แอดเดรส addr ลงใน register reg คำสั่ง LEA reg, addr หมายถึงอ่านที่อยู่ (ไม่ใช่ตัวแปรที่เก็บไว้ที่ที่อยู่) ลงใน register reg
อีกรูปแบบหนึ่งของคำสั่ง MOV คือ MOV reg, immdata ซึ่งหมายถึงอ่านข้อมูลทันที (เช่นค่าคงที่) immdata ลงใน register reg โปรดทราบว่าถ้า addr ใน LEA reg, addr เป็นเพียงค่าคงที่ (เช่นออฟเซ็ตคงที่) ดังนั้นคำสั่ง LEA นั้นจะเหมือนกับ MOV reg ที่เท่ากันทุกประการคำสั่ง immdata ที่โหลดค่าคงที่เดียวกันกับข้อมูลทันที
หากคุณระบุเพียงตัวอักษรไม่มีความแตกต่าง อย่างไรก็ตาม LEA มีความสามารถมากกว่านี้และคุณสามารถอ่านเกี่ยวกับพวกเขาได้ที่นี่:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
leal TextLabel, LabelFromBssSegment
เมื่อคุณมี smth เช่น.bss .lcomm LabelFromBssSegment, 4
คุณจะต้องmovl $TextLabel, LabelFromBssSegment
เป็นไม่ได้หรือไม่
lea
ต้องการปลายทางการลงทะเบียน แต่mov
สามารถมีimm32
ต้นทางและปลายทางของหน่วยความจำได้ ข้อ จำกัด นี้ไม่เฉพาะเจาะจงกับแอสเซมเบลอร์ GNU
MOV AX, [TABLE-ADDR]
ซึ่งเป็นการโหลด ดังนั้นจึงมีความแตกต่างที่สำคัญ คำสั่งที่เทียบเท่าคือmov ax, OFFSET table_addr
ขึ้นอยู่กับแอสเซมเบลอร์ที่ใช้เพราะ
mov ax,table_addr
ใน MASM ทำงานเป็นไฟล์
mov ax,word ptr[table_addr]
ดังนั้นมันโหลดไบต์แรกจากและไม่ชดเชยtable_addr
table_addr
คุณควรใช้แทน
mov ax,offset table_addr
หรือ
lea ax,table_addr
ซึ่งใช้งานได้เหมือนกัน
lea
เวอร์ชันยังใช้งานได้ดีหากtable_addr
เป็นตัวแปรท้องถิ่นเช่น
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
ไม่มีคำตอบใดก่อนหน้านี้ที่ทำให้เกิดความสับสนของตัวเองดังนั้นฉันจึงขอเพิ่มคำตอบของฉันเอง
สิ่งที่ผมขาดหายไปก็คือว่าlea
การดำเนินงานถือว่าการใช้งานของวงเล็บที่แตกต่างจากวิธีการที่mov
ไม่
ลองนึกถึง C. สมมุติว่าฉันมีอาร์เรย์long
ที่เรียกarray
ว่า ตอนนี้นิพจน์จะarray[i]
ทำการ dereference โดยโหลดค่าจากหน่วยความจำตามที่อยู่array + i * sizeof(long)
[1]
บนมืออื่น ๆ &array[i]
ให้พิจารณาการแสดงออก สิ่งนี้ยังคงมีนิพจน์ย่อยarray[i]
แต่ไม่มีการยกเลิกการอ้างอิง! ความหมายของarray[i]
มีการเปลี่ยนแปลง ไม่ได้หมายความว่าจะทำการเบี่ยงเบนอีกต่อไป แต่ทำหน้าที่เป็นชนิดของข้อมูลจำเพาะโดยบอก&
ว่าเรากำลังมองหาที่อยู่หน่วยความจำใด หากคุณต้องการคุณสามารถคิดอีกวิธีหนึ่งว่า&
เป็นการ "ยกเลิก" การอ้างสิทธิ์
เนื่องจากกรณีการใช้งานทั้งสองมีความคล้ายคลึงกันในหลาย ๆ ด้านจึงใช้ไวยากรณ์ร่วมarray[i]
กัน แต่การมีอยู่หรือไม่มีการ&
เปลี่ยนแปลงวิธีตีความไวยากรณ์นั้น หากไม่มี&
มันเป็นการหักล้างและอ่านจากอาร์เรย์ ด้วย&
มันไม่ใช่ ค่าarray + i * sizeof(long)
ยังคงคำนวณ แต่ก็ไม่ได้ dereferenced
สถานการณ์จะคล้ายกันมากกับและmov
lea
ด้วยmov
การ dereference lea
เกิดขึ้นที่ไม่ได้เกิดขึ้นกับ แม้จะมีการใช้วงเล็บที่เกิดขึ้นทั้งสองอย่าง ตัวอย่างเช่นmovq (%r8), %r9
และleaq (%r8), %r9
. ด้วยmov
วงเล็บเหล่านี้หมายถึง "dereference"; ด้วยlea
พวกเขาไม่ นี้จะคล้ายกับวิธีarray[i]
หมายถึงเฉพาะ "dereference" &
เมื่อไม่มี
ตัวอย่างเป็นไปตามลำดับ
พิจารณารหัส
movq (%rdi, %rsi, 8), %rbp
นี้โหลดค่าที่ตั้งหน่วยความจำลงในการลงทะเบียน%rdi + %rsi * 8
%rbp
นั่นคือ: ได้รับค่าในการลงทะเบียนและความคุ้มค่าในการลงทะเบียน%rdi
%rsi
คูณหลังด้วย 8 แล้วบวกเข้าไปในอดีต หาค่าที่สถานที่แห่งนี้%rbp
และวางไว้ในการลงทะเบียน
รหัสนี้จะสอดคล้องกับสายซีx = array[i];
ที่array
จะกลายเป็น%rdi
และi
จะกลายเป็น%rsi
และจะกลายเป็นx
คือความยาวของชนิดข้อมูลที่มีอยู่ในอาร์เรย์%rbp
8
ลองพิจารณารหัสที่คล้ายกันที่ใช้lea
:
leaq (%rdi, %rsi, 8), %rbp
เช่นเดียวกับการใช้งานที่movq
สอดคล้องกับการอ้างอิงการใช้leaq
ที่นี่สอดคล้องกับการไม่อ้างอิง x = &array[i];
บรรทัดนี้สอดคล้องประกอบกับสายซี จำได้ว่า&
เปลี่ยนความหมายของarray[i]
จากการอ้างอิงเป็นการระบุตำแหน่ง ในทำนองเดียวกันการใช้leaq
จะเปลี่ยนความหมายของ(%rdi, %rsi, 8)
จากการอ้างอิงเป็นการระบุตำแหน่งที่ตั้ง
ความหมายของบรรทัดของรหัสนี้มีดังนี้: ได้รับค่าในการลงทะเบียนและความคุ้มค่าในการลงทะเบียน%rdi
%rsi
คูณหลังด้วย 8 แล้วบวกเข้าไปในอดีต %rbp
วางค่านี้ลงทะเบียน ไม่มีการโหลดจากหน่วยความจำมีเพียงการคำนวณเลขคณิต [2]
โปรดทราบว่าความแตกต่างเพียงอย่างเดียวระหว่างคำอธิบายของฉันleaq
และmovq
นั่นคือmovq
การหักค่าอ้างอิงและleaq
ไม่ได้ ในความเป็นจริงในการเขียนleaq
คำอธิบายโดยพื้นฐานแล้วฉันจะคัดลอก + วางคำอธิบายจากmovq
นั้นจึงลบ "ค้นหาค่าที่ตำแหน่งนี้"
สรุป: movq
เทียบกับleaq
เป็นเรื่องยุ่งยากเพราะใช้วงเล็บเหมือน(%rsi)
และ(%rdi, %rsi, 8)
ต่างกัน ในmovq
(และคำสั่งอื่น ๆ ทั้งหมดยกเว้นlea
) วงเล็บเหล่านี้แสดงถึง dereference ของแท้ในขณะที่leaq
ไม่มีและเป็นไวยากรณ์ที่สะดวกอย่างแท้จริง
[1] ฉันได้กล่าวว่าเมื่อarray
เป็นอาร์เรย์ของlong
นิพจน์โหลดค่าจากที่อยู่array[i]
array + i * sizeof(long)
นี่เป็นเรื่องจริง แต่มีความละเอียดอ่อนที่ควรแก้ไข ถ้าฉันเขียนรหัส C
long x = array[5];
สิ่งนี้ไม่เหมือนกับการพิมพ์
long x = *(array + 5 * sizeof(long));
ดูเหมือนว่ามันควรจะขึ้นอยู่กับข้อความก่อนหน้าของฉัน แต่มันไม่ใช่
สิ่งที่เกิดขึ้นคือการเพิ่มตัวชี้ C มีเคล็ดลับ ว่าฉันมีตัวชี้ชี้ไปที่ค่าประเภทp
T
นิพจน์p + i
ไม่ได้หมายถึง "ตำแหน่งที่p
บวกi
ไบต์" แต่p + i
จริงๆแล้วนิพจน์จะหมายถึง "ตำแหน่งที่p
บวกi * sizeof(T)
ไบต์"
ความสะดวกในการนี้คือการที่จะได้รับ "ค่าต่อไป" เราก็ต้องมีการเขียนแทนp + 1
p + 1 * sizeof(T)
ซึ่งหมายความว่ารหัส C long x = array[5];
นั้นเทียบเท่ากับ
long x = *(array + 5)
เพราะ C จะคูณ5
ด้วยโดยsizeof(long)
อัตโนมัติ
ดังนั้นในบริบทของคำถาม StackOverflow ทั้งหมดนี้เกี่ยวข้องอย่างไร หมายความว่าเมื่อฉันพูดว่า "ที่อยู่array + i * sizeof(long)
" ฉันไม่ได้หมายถึงให้ " array + i * sizeof(long)
" แปลเป็นนิพจน์ C ฉันกำลังทำการคูณด้วยsizeof(long)
ตัวเองเพื่อให้คำตอบของฉันมีความชัดเจนมากขึ้น แต่เข้าใจว่าด้วยเหตุนี้จึงไม่ควรอ่านนิพจน์นี้เป็น C เช่นเดียวกับคณิตศาสตร์ทั่วไปที่ใช้ไวยากรณ์ C
[2] หมายเหตุด้านข้าง: เนื่องจากทั้งหมดlea
คือการดำเนินการทางคณิตศาสตร์อาร์กิวเมนต์ของมันจึงไม่จำเป็นต้องอ้างถึงที่อยู่ที่ถูกต้อง ด้วยเหตุนี้จึงมักใช้ในการคำนวณทางคณิตศาสตร์บริสุทธิ์กับค่าที่อาจไม่ได้ตั้งใจให้ถูกอ้างถึง ตัวอย่างเช่นcc
ด้วย-O2
การเพิ่มประสิทธิภาพการแปล
long f(long x) {
return x * 5;
}
ในรายการต่อไปนี้ (ลบบรรทัดที่ไม่เกี่ยวข้องออก):
f:
leaq (%rdi, %rdi, 4), %rax # set %rax to %rdi + %rdi * 4
ret
&
ตัวดำเนินการของ C เป็นตัวเปรียบเทียบที่ดี บางทีควรชี้ให้เห็นว่า LEA เป็นกรณีพิเศษในขณะที่ MOV ก็เหมือนกับคำสั่งอื่น ๆ ที่สามารถใช้หน่วยความจำหรือลงทะเบียนตัวถูกดำเนินการได้ เช่นadd (%rdi), %eax
ใช้โหมดกำหนดแอดเดรสเพื่อจัดการกับหน่วยความจำเช่นเดียวกับ MOV ที่เกี่ยวข้อง: การใช้ LEA กับค่าที่ไม่ใช่ที่อยู่ / ตัวชี้? ใช้คำอธิบายเพิ่มเติม: LEA คือวิธีที่คุณสามารถใช้การสนับสนุน HW ของ CPU สำหรับการคำนวณที่อยู่เพื่อทำการคำนวณโดยพลการ
%rdi
" - คำนี้แปลก คุณหมายความว่าควรใช้ค่าในรีจิสเตอร์ rdi
การใช้ "ที่" ของคุณดูเหมือนจะหมายถึงการลดทอนหน่วยความจำโดยที่ไม่มีเลย
%rdi
" หรือ "ค่าใน %rdi
" "ค่าในทะเบียน%rdi
" ของคุณยาว แต่ใช้ได้ดีและบางทีอาจช่วยให้ใครบางคนพยายามทำความเข้าใจกับการลงทะเบียนเทียบกับหน่วยความจำ
ตามที่ระบุไว้ในคำตอบอื่น ๆ :
MOV
จะดึงข้อมูลตามที่อยู่ภายในวงเล็บและวางข้อมูลนั้นลงในตัวถูกดำเนินการปลายทางLEA
จะทำการคำนวณที่อยู่ภายในวงเล็บและวางที่อยู่ที่คำนวณนั้นลงในตัวถูกดำเนินการปลายทาง สิ่งนี้เกิดขึ้นโดยไม่ต้องออกไปที่หน่วยความจำและรับข้อมูล งานที่ทำLEA
อยู่ในการคำนวณ "ที่อยู่ที่มีผล"เนื่องจากหน่วยความจำสามารถแก้ไขได้หลายวิธี (ดูตัวอย่างด้านล่าง) LEA
บางครั้งจึงใช้เพื่อเพิ่มหรือคูณการลงทะเบียนเข้าด้วยกันโดยไม่ต้องใช้คำสั่งADD
หรือMUL
คำสั่งที่ชัดเจน(หรือเทียบเท่า)
เนื่องจากทุกคนกำลังแสดงตัวอย่างในไวยากรณ์ของ Intel นี่คือบางส่วนในไวยากรณ์ของ AT&T:
MOVL 16(%ebp), %eax /* put long at ebp+16 into eax */
LEAL 16(%ebp), %eax /* add 16 to ebp and store in eax */
MOVQ (%rdx,%rcx,8), %rax /* put qword at rcx*8 + rdx into rax */
LEAQ (%rdx,%rcx,8), %rax /* put value of "rcx*8 + rdx" into rax */
MOVW 5(%bp,%si), %ax /* put word at si + bp + 5 into ax */
LEAW 5(%bp,%si), %ax /* put value of "si + bp + 5" into ax */
MOVQ 16(%rip), %rax /* put qword at rip + 16 into rax */
LEAQ 16(%rip), %rax /* add 16 to instruction pointer and store in rax */
MOVL label(,1), %eax /* put long at label into eax */
LEAL label(,1), %eax /* put the address of the label into eax */
lea label, %eax
แบบสัมบูรณ์ [disp32]
ใช้mov $label, %eax
แทน ใช่มันใช้งานได้ แต่มีประสิทธิภาพน้อยกว่า (รหัสเครื่องใหญ่ขึ้นและทำงานบนหน่วยประมวลผลน้อยลง) เนื่องจากคุณพูดถึง AT&T การใช้ LEA กับค่าที่ไม่ใช่ที่อยู่ / ตัวชี้? ใช้ AT&T และคำตอบของฉันมีตัวอย่างอื่น ๆ ของ AT&T
โดยพื้นฐานแล้ว ... "ย้ายไปที่ REG ... หลังจากคำนวณแล้ว ... " ดูเหมือนจะดีสำหรับวัตถุประสงค์อื่นเช่นกัน :)
หากคุณลืมไปว่าค่านั้นเป็นตัวชี้ที่คุณสามารถใช้เพื่อเพิ่มประสิทธิภาพ / ลดขนาดโค้ด ...
MOV EBX , 1
MOV ECX , 2
;//with 1 instruction you got result of 2 registers in 3rd one ...
LEA EAX , [EBX+ECX+5]
EAX = 8
เดิมมันจะเป็น:
MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5
lea
เป็นคำสั่ง shift-and-addที่ใช้การเข้ารหัสและไวยากรณ์ของเครื่องโอเปอแรนด์หน่วยความจำเนื่องจากฮาร์ดแวร์รู้วิธีถอดรหัส ModR / M + SIB + disp0 / 8/32 อยู่แล้ว
มาทำความเข้าใจกับตัวอย่างนี้
mov eax, [ebx] และ
lea eax, [ebx] สมมติว่าค่าใน ebx คือ 0x400000 จากนั้น mov จะไปที่ที่อยู่ 0x400000 และคัดลอกข้อมูล 4 ไบต์ที่นำเสนอไปยัง eax register ในขณะที่ lea จะคัดลอกที่อยู่ 0x400000 ลงใน eax ดังนั้นหลังจากดำเนินการตามค่าคำสั่งของ eax ในแต่ละกรณีจะเป็น (สมมติว่าในหน่วยความจำ 0x400000 มี 30)
eax = 30 (ในกรณีของ mov) eax = 0x400000 (ในกรณีของ lea) สำหรับคำจำกัดความ mov คัดลอกข้อมูลจาก rm32 ไปยังปลายทาง (mov dest rm32) และ lea (โหลดที่อยู่ที่มีประสิทธิภาพ) จะคัดลอกที่อยู่ไปยังปลายทาง (mov dest rm32 ).
MOV สามารถทำสิ่งเดียวกันกับ LEA [label] แต่คำสั่ง MOV มีแอดเดรสที่มีประสิทธิภาพภายในคำสั่งเป็นค่าคงที่ทันที (คำนวณล่วงหน้าโดยแอสเซมเบลอร์) LEA ใช้ PC-สัมพัทธ์ในการคำนวณที่อยู่ที่มีประสิทธิผลระหว่างการดำเนินการตามคำสั่ง
lea [label
เป็นการสิ้นเปลืองไบต์อย่างไร้จุดหมายเมื่อเทียบกับขนาดกะทัดรัดmov
กว่าดังนั้นคุณควรระบุเงื่อนไขที่คุณกำลังพูดถึง นอกจากนี้สำหรับแอสเซมเบลอร์บางตัว[label]
ไม่ใช่ไวยากรณ์ที่ถูกต้องสำหรับโหมดการกำหนดแอดเดรสแบบสัมพันธ์ RIP แต่ใช่ว่าจะถูกต้อง วิธีโหลดที่อยู่ของฟังก์ชันหรือเลเบลลงในรีจิสเตอร์ใน GNU Assemblerอธิบายรายละเอียดเพิ่มเติม
LEA (Load Effective Address) คือคำสั่ง shift-and-add เพิ่มเข้าไปใน 8086 เนื่องจากฮาร์ดแวร์อยู่ที่นั่นเพื่อถอดรหัสและคำนวณโหมดการอยู่อาศัย
ความแตกต่างนั้นบอบบาง แต่สำคัญ คำสั่ง MOV คือ 'MOVe' ซึ่งเป็นสำเนาของที่อยู่ที่ป้าย TABLE-ADDR หมายถึง คำสั่ง LEA คือ 'Load Effective Address' ซึ่งเป็นคำสั่งทางอ้อมซึ่งหมายความว่า TABLE-ADDR ชี้ไปยังตำแหน่งหน่วยความจำที่พบแอดเดรสที่จะโหลด
การใช้ LEA อย่างมีประสิทธิภาพนั้นเทียบเท่ากับการใช้พอยน์เตอร์ในภาษาเช่น C ดังนั้นจึงเป็นคำสั่งที่มีประสิทธิภาพ