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]
và
LEA AX, [TABLE-ADDR]
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]
và
LEA AX, [TABLE-ADDR]
Câu trả lời:
LEA có nghĩa là tải địa chỉ hiệu quảMOV có nghĩa là giá trị tảiNó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.
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.
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.
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.
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/optizes và stackoverflow.com/tags/x86/info .
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.
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:
leal TextLabel, LabelFromBssSegmentkhi bạn có smth. như .bss .lcomm LabelFromBssSegment, 4, bạn sẽ phải movl $TextLabel, LabelFromBssSegment, phải không?
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.
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
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
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 movvà lea. 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), %r9và leaq (%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 %rdivà itrở thành %rsivà xtrở 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ề leaqvà movqđó 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)và (%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
&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 ý.
%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ó.
%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ớ.
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
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.
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 */
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.
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ờ) ).
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.
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.
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ẽ.