Sự khác biệt giữa MOV và LEA là gì?


138

Tôi muốn biết sự khác biệt giữa các hướng dẫn này là gì:

MOV AX, [TABLE-ADDR]

LEA AX, [TABLE-ADDR]


8
cảm ơn nick Trước hết, tôi sẽ không tìm thấy câu trả lời cho câu hỏi này bằng cách xem qua liên kết đó. Ở đây tôi đang tìm kiếm một thông tin cụ thể, các cuộc thảo luận trong liên kết mà bạn cung cấp có bản chất hơn.
naveen

3
Tôi đã nâng cấp @ Nick's dup từ lâu nhưng vtc'd vừa rồi. Khi suy ngẫm, tôi đã quá vội vàng và bây giờ với naveen rằng a) câu hỏi khác không trả lời "sự khác biệt" và b) đây là một câu hỏi hữu ích. Xin lỗi để naveen cho lỗi lầm của tôi - nếu tôi có thể hoàn tác vtc ...
Ruben Bartelink


Liên quan: Sử dụng LEA trên các giá trị không có địa chỉ / con trỏ? nói về những cách sử dụng khác của LEA, đối với toán học tùy ý.
Peter Cordes

Câu trả lời:


168
  • LEA có nghĩa là tải địa chỉ hiệu quả
  • MOV có nghĩa là giá trị tải

Nói tóm lại, LEAtải một con trỏ đến mục bạn đang xử lý trong khi MOV tải giá trị thực tại địa chỉ đó.

Mục đích của LEAviệc cho phép một người thực hiện phép tính địa chỉ không tầm thường và lưu trữ kết quả [để sử dụng sau này]

LEA ax, [BP+SI+5] ; Compute address of value

MOV ax, [BP+SI+5] ; Load value at that address

Trong trường hợp chỉ có các hằng số liên quan, MOV(thông qua các phép tính hằng số của bộ lắp ráp) đôi khi có thể xuất hiện trùng lặp với các trường hợp sử dụng đơn giản nhất LEA. Nó hữu ích nếu bạn có một phép tính nhiều phần với nhiều địa chỉ cơ sở, v.v.


6
+1 cảm ơn vì lời giải thích rõ ràng, đã giúp tôi trả lời một câu hỏi khác.
huyền thoại2k

Nó làm tôi bối rối rằng chữ "có" tải "trong tên và mọi người nói nó" tải "một địa chỉ được tính vào một thanh ghi, bởi vì tất cả các đầu vào để tính toán vị trí bộ nhớ đều là giá trị ngay lập tức hoặc thanh ghi. AFAICT chỉ thực hiện một tính toán, nó không tải bất cứ thứ gì, trong đó tải có nghĩa là chạm vào bộ nhớ?
Joseph Garvin

2
@josephGarvin IIRC thuật ngữ tìm nạp sẽ được áp dụng cho khía cạnh đó; Tải chỉ là cách bạn thay thế giá trị trong một thanh ghi bằng một cái gì đó từ đầu. ví dụ LAHF: Tải FLAGS vào thanh ghi AH . Trong CIL của CLR (là máy trừu tượng dựa trên ngăn xếp cấp cao hơn, thuật ngữ tải có nghĩa là đặt một giá trị lên ngăn xếp công chứng và thường là l..., và s... tương đương làm ngược lại). Những lưu ý này: cs.umd.edu/ class / s200200 / cmsc311 / Notes / Mexico / load.html ) đề xuất rằng có những kiến ​​trúc thực sự áp dụng sự khác biệt của bạn.
Ruben Bartelink

tất cả làm tôi nhớ đến sl slideshoware.net/pirhilton/ ,;)
Ruben Bartelink

45

Trong cú pháp 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

Trong cú pháp MASM, sử dụng OFFSET varđể lấy Mov-tức thì thay vì tải.


3
chỉ trong cú pháp NASM. Trong cú pháp MASM, mov eax, varlà một tải, giống như mov eax, [var]và bạn phải sử dụng mov eax, OFFSET varđể sử dụng nhãn làm hằng số ngay lập tức.
Peter Cordes

1
Rõ ràng, đơn giản và chứng minh những gì tôi đã cố gắng xác nhận. Cảm ơn.
JayArby

1
Lưu ý rằng trong tất cả các ví dụ này, lealà sự lựa chọn tồi tệ hơn ngoại trừ trong chế độ 64 bit cho địa chỉ liên quan đến RIP. mov r32, imm32chạy trên nhiều cổng hơn. lea eax, [edx*4]là một bản sao và dịch chuyển không thể được thực hiện trong một lệnh khác, nhưng trong cùng một thanh ghi, LEA chỉ mất nhiều byte hơn để mã hóa vì [eax*4]yêu cầu a disp32=0. (Tuy nhiên, nó chạy trên các cổng khác nhau so với ca.) Xem agner.org/optizesstackoverflow.com/tags/x86/info .
Peter Cordes

29

Lệnh MOV reg, addr có nghĩa là đọc một biến được lưu trữ tại địa chỉ addr vào reg reg. Lệnh LEA reg, addr có nghĩa là đọc địa chỉ (không phải biến được lưu trữ tại địa chỉ) vào reg reg.

Một dạng khác của lệnh MOV là MOV reg, infdata có nghĩa là đọc dữ liệu tức thời (tức là hằng số) vào thanh ghi reg. Lưu ý rằng nếu addr trong LEA reg, addr chỉ là hằng số (nghĩa là phần bù cố định) thì lệnh LEA về cơ bản giống hệt như một reg reg tương đương, lệnh imdata tải cùng hằng số như dữ liệu tức thời.


11

Nếu bạn chỉ xác định một nghĩa đen, không có sự khác biệt. LEA có nhiều khả năng hơn, tuy nhiên, và bạn có thể đọc về chúng ở đây:

http://www.oopweb.com/Assugging/Document/ArtOfAssugging/Volume/Ch Module_6 / CH06-1.html#HEADING1-136


Tôi đoán, ngoại trừ rằng trong trình biên dịch GNU, điều đó không đúng khi nói đến các nhãn trong phân đoạn .bss? AFAIR bạn không thể thực sự leal TextLabel, LabelFromBssSegmentkhi bạn có smth. như .bss .lcomm LabelFromBssSegment, 4, bạn sẽ phải movl $TextLabel, LabelFromBssSegment, phải không?
JSmyth

@JSmyth: Điều đó chỉ bởi vì leayêu cầu đích đăng ký, nhưng movcó thể có imm32nguồn và đích bộ nhớ. Giới hạn này tất nhiên là không dành riêng cho trình biên dịch GNU.
Peter Cordes

1
Ngoài ra, câu trả lời này về cơ bản là sai bởi vì câu hỏi đang hỏi về MOV AX, [TABLE-ADDR], đó là một tải. Vì vậy, có một sự khác biệt lớn. Hướng dẫn tương đương làmov ax, OFFSET table_addr
Peter Cordes

10

Nó phụ thuộc vào trình biên dịch được sử dụng, bởi vì

mov ax,table_addr

trong MASM hoạt động như

mov ax,word ptr[table_addr]

Vì vậy, nó tải các byte đầu tiên từ table_addrvà KHÔNG bù vào table_addr. Bạn nên sử dụng thay thế

mov ax,offset table_addr

hoặc là

lea ax,table_addr

mà hoạt động như nhau.

leaphiên bản cũng hoạt động tốt nếu table_addrlà một biến cục bộ, vd

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

cảm ơn rất nhiều, chỉ là tôi không thể đánh dấu nhiều hơn một câu trả lời :(
naveen

5
Sự khác biệt giữa các lệnh x86 MOV và LEA chắc chắn KHÔNG phụ thuộc vào trình biên dịch.
IJ Kennedy

4

Không có câu trả lời nào trước đây hoàn toàn lọt vào đáy của sự nhầm lẫn của riêng tôi, vì vậy tôi muốn thêm câu trả lời của riêng mình.

Điều tôi còn thiếu là các leathao tác xử lý việc sử dụng dấu ngoặc đơn khác với cách movthực hiện.

Hãy nghĩ về C. Hãy nói rằng tôi có một mảng longmà tôi gọi array. Bây giờ biểu thức array[i]thực hiện một sự bổ nhiệm, tải giá trị từ bộ nhớ tại địa chỉ array + i * sizeof(long)[1].

Mặt khác, hãy xem xét biểu thức &array[i]. Điều này vẫn chứa biểu thức phụ array[i], nhưng không có hội thảo nào được thực hiện! Ý nghĩa của array[i]đã thay đổi. Nó không còn có nghĩa là thực hiện một sự trì hoãn mà thay vào đó hoạt động như một loại đặc tả , cho &biết địa chỉ bộ nhớ mà chúng ta đang tìm kiếm. Nếu bạn thích, bạn có thể nghĩ khác &là "hủy bỏ" sự quy định.

Bởi vì hai trường hợp sử dụng giống nhau theo nhiều cách, chúng chia sẻ cú pháp array[i], nhưng sự tồn tại hoặc vắng mặt của một &thay đổi về cách cú pháp đó được diễn giải. Không &, đó là một sự trung thành và thực sự đọc từ mảng. Với &, không phải vậy. Giá trị array + i * sizeof(long)vẫn được tính, nhưng nó không bị hủy bỏ.

Tình hình rất giống với movlea. Với mov, một sự thiếu sót xảy ra mà không xảy ra với lea. Điều này là bất chấp việc sử dụng dấu ngoặc đơn xảy ra trong cả hai. Ví dụ, movq (%r8), %r9leaq (%r8), %r9. Với mov, các dấu ngoặc đơn này có nghĩa là "sự bổ nhiệm"; với lea, họ không. Điều này tương tự như cách array[i]chỉ có nghĩa là "sự thiếu tôn trọng" khi không có &.

Một ví dụ là theo thứ tự.

Xem xét mã

movq (%rdi, %rsi, 8), %rbp

Điều này tải giá trị tại vị trí bộ nhớ %rdi + %rsi * 8vào thanh ghi %rbp. Đó là: lấy giá trị trong thanh ghi %rdivà giá trị trong thanh ghi %rsi. Nhân cái sau với 8, rồi thêm nó vào cái trước. Tìm giá trị tại vị trí này và đặt nó vào thanh ghi %rbp.

Mã này tương ứng với dòng C x = array[i];, nơi arraytrở thành %rdiitrở thành %rsixtrở thành %rbp. Độ 8dài của kiểu dữ liệu chứa trong mảng.

Bây giờ hãy xem xét mã tương tự sử dụng lea:

leaq (%rdi, %rsi, 8), %rbp

Cũng giống như việc sử dụng movqtương ứng với hội nghị, việc sử dụng leaqở đây tương ứng với việc không tham gia hội nghị. Dòng này của hội đồng tương ứng với dòng C x = &array[i];. Nhớ lại rằng &thay đổi ý nghĩa của array[i]từ hội nghị để chỉ định một vị trí. Tương tự như vậy, việc sử dụng leaqthay đổi ý nghĩa của (%rdi, %rsi, 8)từ hội nghị đến việc chỉ định một địa điểm.

Các ngữ nghĩa của dòng mã này như sau: lấy giá trị trong thanh ghi %rdivà giá trị trong thanh ghi %rsi. Nhân cái sau với 8, rồi thêm nó vào cái trước. Đặt giá trị này vào thanh ghi %rbp. Không có tải từ bộ nhớ có liên quan, chỉ hoạt động số học [2].

Lưu ý rằng sự khác biệt duy nhất giữa các mô tả của tôi về leaqmovqđó là movqmột sự trung thành, và leaqkhông. Trong thực tế, để viết leaqmô tả, về cơ bản tôi đã sao chép + dán mô tả movqvà sau đó xóa "Tìm giá trị tại vị trí này".

Tóm lại: movqvs. leaqlà khó khăn vì họ đối xử với việc sử dụng dấu ngoặc đơn, như trong (%rsi)(%rdi, %rsi, 8), khác nhau. Trong movq(và tất cả các hướng dẫn khác ngoại trừ lea), các dấu ngoặc đơn này biểu thị một sự quy định chính đáng, trong khi đó leaqchúng không và hoàn toàn là cú pháp thuận tiện.


[1] Tôi đã nói rằng khi arraylà một mảng long, biểu thức array[i]sẽ tải giá trị từ địa chỉ array + i * sizeof(long). Điều này là đúng, nhưng có một sự tinh tế cần được giải quyết. Nếu tôi viết mã C

long x = array[5];

đây là không giống như đánh máy

long x = *(array + 5 * sizeof(long));

Có vẻ như nó nên dựa trên những tuyên bố trước đây của tôi, nhưng thực tế không phải vậy.

Điều gì đang xảy ra là bổ sung con trỏ C có một mẹo đối với nó. Nói rằng tôi có một con trỏ ptrỏ đến các giá trị của loại T. Các biểu hiện p + ilàm không có nghĩa là "vị trí tại pcộng ibyte". Thay vào đó, biểu thức p + i thực sự có nghĩa là "vị trí tại các byte pcộng i * sizeof(T)".

Sự tiện lợi của điều này là để có được "giá trị tiếp theo", chúng ta chỉ cần viết p + 1thay vì p + 1 * sizeof(T).

Điều này có nghĩa là mã C long x = array[5];thực sự tương đương với

long x = *(array + 5)

vì C sẽ tự động nhân sự 5bằng sizeof(long).

Vì vậy, trong bối cảnh của câu hỏi StackOverflow này, làm thế nào tất cả có liên quan? Điều đó có nghĩa là khi tôi nói "địa chỉ array + i * sizeof(long)", tôi không có nghĩa là " array + i * sizeof(long)" được hiểu là biểu thức C. Tôi đang thực hiện phép nhân một sizeof(long)mình để làm cho câu trả lời của tôi rõ ràng hơn, nhưng hiểu rằng do đó, biểu thức này không nên được đọc là C. Giống như toán học thông thường sử dụng cú pháp C.

[2] Lưu ý bên lề: bởi vì tất cả đều lealà các phép toán số học, nên các đối số của nó không thực sự phải tham chiếu đến các địa chỉ hợp lệ. Vì lý do này, nó thường được sử dụng để thực hiện số học thuần túy trên các giá trị có thể không được quy định. Ví dụ, ccvới -O2tối ưu hóa dịch

long f(long x) {
  return x * 5;
}

vào các mục sau (loại bỏ các dòng không liên quan):

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret

1
Yup, giải thích tốt, chi tiết hơn các câu trả lời khác, và có &toán tử C là một sự tương tự tốt. Có lẽ đáng để chỉ ra rằng LEA là trường hợp đặc biệt, trong khi MOV cũng giống như mọi hướng dẫn khác có thể lấy bộ nhớ hoặc đăng ký toán hạng. ví dụ: add (%rdi), %eaxchỉ sử dụng chế độ địa chỉ để ghi nhớ bộ nhớ, giống như MOV. Cũng liên quan: Sử dụng LEA trên các giá trị không có địa chỉ / con trỏ? đưa giải thích này thêm: LEA là cách bạn có thể sử dụng hỗ trợ CTNH của CPU cho toán địa chỉ để thực hiện các phép tính tùy ý.
Peter Cordes

"Nhận giá trị tại %rdi" - Đây là từ lạ lùng. Bạn có nghĩa là giá trị trong thanh ghi rdi nên được sử dụng. Việc bạn sử dụng "tại" dường như có nghĩa là một sự điều tiết bộ nhớ trong đó không có.
ecm

@PeterCordes Cảm ơn! Tôi đã thêm điểm về nó là một trường hợp đặc biệt cho câu trả lời.
Quelklef

1
@ecm Điểm tốt; Tôi đã không nhận thấy điều đó. Tôi đã thay đổi nó bây giờ, cảm ơn bạn! :)
Quelklef

FYI, cụm từ ngắn hơn giúp khắc phục vấn đề ecm đã chỉ ra bao gồm: "giá trị của %rdi " hoặc "giá trị trong %rdi ". "Giá trị trong sổ đăng ký %rdi" của bạn dài nhưng tốt, và có lẽ có thể giúp ai đó đấu tranh để hiểu các thanh ghi so với bộ nhớ.
Peter Cordes

2

Về cơ bản ... "Chuyển sang REG ... sau khi tính toán nó ..." có vẻ cũng tốt cho các mục đích khác :)

nếu bạn quên rằng giá trị là một con trỏ, bạn có thể sử dụng nó để tối ưu hóa / tối thiểu hóa mã ... những gì đã từng ..

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

nguyên bản nó sẽ là:

MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5

Yup, lealà một hướng dẫn thay đổi và sử dụng cú pháp và mã hóa máy toán tử bộ nhớ, bởi vì phần cứng đã biết cách giải mã ModR / M + SIB + dist0 / 8/32.
Peter Cordes

1

Như đã nêu trong các câu trả lời khác:

  • MOVsẽ lấy dữ liệu tại địa chỉ bên trong ngoặc và đặt dữ liệu đó vào toán hạng đích.
  • LEAsẽ thực hiện tính toán địa chỉ bên trong dấu ngoặc và đặt địa chỉ được tính toán đó vào toán hạng đích. Điều này xảy ra mà không thực sự đi ra bộ nhớ và nhận dữ liệu. Công việc được thực hiện LEAlà tính toán "địa chỉ hiệu quả".

Bởi vì bộ nhớ có thể được xử lý theo nhiều cách khác nhau (xem ví dụ bên dưới), LEAđôi khi được sử dụng để thêm hoặc nhân các thanh ghi với nhau mà không sử dụng một lệnh rõ ràng ADDhoặc MULhướng dẫn (hoặc tương đương).

Vì mọi người đều hiển thị các ví dụ theo cú pháp Intel, nên đây là một số cú pháp 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 */

Bạn không bao giờ muốn lea label, %eaxcho một [disp32]chế độ địa chỉ tuyệt đối . Sử dụng mov $label, %eaxthay thế. Có, nó hoạt động, nhưng nó kém hiệu quả hơn (mã máy lớn hơn và chạy trên ít đơn vị thực thi hơn). Vì bạn đề cập đến AT & T, Sử dụng LEA trên các giá trị không có địa chỉ / con trỏ? sử dụng AT & T và câu trả lời của tôi có một số ví dụ khác về AT & T.
Peter Cordes

1

Hãy hiểu điều này với một ví dụ.

Mov eax, [ebx] và

lea eax, [ebx] Giả sử giá trị trong ebx là 0x400000. Sau đó, Mov sẽ chuyển đến địa chỉ 0x400000 và sao chép 4 byte dữ liệu hiện tại vào thanh ghi eax. Trong khi đó, cờ sẽ sao chép địa chỉ 0x400000 vào eax. Vì vậy, sau khi thực hiện từng giá trị lệnh của eax trong mỗi trường hợp sẽ là (giả sử tại bộ nhớ 0x400000 chứa là 30).

eax = 30 (trong trường hợp của Mov) eax = 0x400000 (trong trường hợp của cờ) ).


0

LEA (Tải địa chỉ hiệu quả) là một hướng dẫn thay đổi và thêm. Nó đã được thêm vào 8086 vì phần cứng ở đó để giải mã và tính toán các chế độ địa chỉ.


0

MOV có thể làm tương tự như LEA [nhãn], nhưng lệnh MOV chứa địa chỉ hiệu quả bên trong chính lệnh đó là hằng số tức thời (được tính toán trước bởi trình biên dịch chương trình). LEA sử dụng PC-Rel để tính địa chỉ hiệu quả trong quá trình thực hiện lệnh.


Điều đó chỉ đúng với chế độ 64 bit (trong đó địa chỉ liên quan đến PC là mới); trong các chế độ khác lea [labellà sự lãng phí vô nghĩa của byte so với nhỏ gọn hơn mov, vì vậy bạn nên chỉ định các điều kiện bạn đang nói đến. Ngoài ra, đối với một số trình biên dịch [label]không phải là cú pháp đúng cho chế độ địa chỉ liên quan đến RIP. Nhưng vâng, điều đó chính xác. Cách tải địa chỉ của hàm hoặc nhãn vào thanh ghi trong GNU Assembler giải thích chi tiết hơn.
Peter Cordes

-1

Sự khác biệt là tinh tế nhưng quan trọng. Lệnh MOV là 'MOVe' một cách hiệu quả bản sao của địa chỉ mà nhãn TABLE-ADDR là viết tắt của. Lệnh LEA là một 'Tải địa chỉ hiệu quả', là một hướng dẫn không xác định, có nghĩa là TABLE-ADDR trỏ đến một vị trí bộ nhớ nơi tìm thấy địa chỉ tải.

Hiệu quả sử dụng LEA tương đương với việc sử dụng các con trỏ trong các ngôn ngữ như C, vì vậy đây là một hướng dẫn mạnh mẽ.


7
Tôi nghĩ rằng câu trả lời này là khó hiểu nhất. "Lệnh LEA là một 'Địa chỉ hiệu quả tải', là một hướng dẫn không xác định, có nghĩa là TABLE-ADDR trỏ đến một vị trí bộ nhớ mà tại đó tìm thấy địa chỉ tải." Trên thực tế LEA sẽ tải địa chỉ, không phải nội dung của địa chỉ. Tôi nghĩ rằng thực sự người hỏi cần phải yên tâm rằng MOV và LEA có thể trùng nhau, và thực hiện chính xác điều tương tự, trong một số trường hợp
Bill Forster
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.