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, LEA
tả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 LEA
việ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, var
là 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.
lea
là 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, imm32
chạ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, LabelFromBssSegment
khi bạn có smth. như .bss .lcomm LabelFromBssSegment, 4
, bạn sẽ phải movl $TextLabel, LabelFromBssSegment
, phải không?
lea
yêu cầu đích đăng ký, nhưng mov
có thể có imm32
nguồ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_addr
và 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.
lea
phiên bản cũng hoạt động tốt nếu table_addr
là 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 lea
thao tác xử lý việc sử dụng dấu ngoặc đơn khác với cách mov
thực hiện.
Hãy nghĩ về C. Hãy nói rằng tôi có một mảng long
mà 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 mov
và 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), %r9
và 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 * 8
vào thanh ghi %rbp
. Đó là: lấy giá trị trong thanh ghi %rdi
và 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 array
trở thành %rdi
và i
trở thành %rsi
và x
trở thành %rbp
. Độ 8
dà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 movq
tươ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 leaq
thay đổ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 %rdi
và 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ề leaq
và movq
đó là movq
một sự trung thành, và leaq
không. Trong thực tế, để viết leaq
mô tả, về cơ bản tôi đã sao chép + dán mô tả movq
và sau đó xóa "Tìm giá trị tại vị trí này".
Tóm lại: movq
vs. leaq
là 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 đó leaq
chúng không và hoàn toàn là cú pháp thuận tiện.
[1] Tôi đã nói rằng khi array
là 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ỏ p
trỏ đến các giá trị của loại T
. Các biểu hiện p + i
làm không có nghĩa là "vị trí tại p
cộng i
byte". Thay vào đó, biểu thức p + i
thực sự có nghĩa là "vị trí tại các byte p
cộ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 + 1
thay 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ự 5
bằ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 lea
là 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ụ, cc
với -O2
tố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), %eax
chỉ 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
lea
là 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:
MOV
sẽ 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.LEA
sẽ 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 LEA
là 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 ADD
hoặc MUL
hướ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, %eax
cho một [disp32]
chế độ địa chỉ tuyệt đối . Sử dụng mov $label, %eax
thay 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 [label
là 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ẽ.