Chciałbym wiedzieć, jaka jest różnica między tymi instrukcjami:
MOV AX, [TABLE-ADDR]
i
LEA AX, [TABLE-ADDR]
Chciałbym wiedzieć, jaka jest różnica między tymi instrukcjami:
MOV AX, [TABLE-ADDR]
i
LEA AX, [TABLE-ADDR]
Odpowiedzi:
LEA oznacza Załaduj efektywny adresMOV oznacza wartość obciążeniaKrótko mówiąc, LEAładuje wskaźnik do adresowanego elementu, podczas gdy MOV ładuje rzeczywistą wartość pod tym adresem.
Celem LEAjest umożliwienie wykonania nietrywialnego obliczenia adresu i zapisanie wyniku [do późniejszego wykorzystania]
LEA ax, [BP+SI+5] ; Compute address of value
MOV ax, [BP+SI+5] ; Load value at that address
Tam, gdzie w grę wchodzą tylko stałe MOV(poprzez stałe obliczenia asemblera), czasami może się wydawać, że nakładają się na najprostsze przypadki użycia LEA. Jest to przydatne, jeśli masz wieloczęściowe obliczenia z wieloma adresami podstawowymi itp.
LAHFto: Załaduj FLAGI do rejestru AH . W CIL CLR (który jest maszyną abstrakcyjną opartą na stosie wyższego poziomu, termin load odnosi się do umieszczenia wartości na stosie pojęciowym i jest normalnie l..., a sodpowiednik ... odwrotnie). Te uwagi: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) sugerują, że rzeczywiście istnieją architektury, w których twoje rozróżnienie ma zastosowanie.
W składni 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
W składni MASM użyj, OFFSET varaby uzyskać natychmiastowe mov zamiast ładowania.
mov eax, varjest to obciążenie, to samo co mov eax, [var]i musisz mov eax, OFFSET varużyć etykiety jako natychmiastowej stałej.
leajest to gorszy wybór, z wyjątkiem trybu 64-bitowego dla adresowania względnego RIP. mov r32, imm32działa na większej liczbie portów. lea eax, [edx*4]jest kopiowaniem i przesuwaniem, którego nie można wykonać w jednej instrukcji w inny sposób, ale w tym samym rejestrze LEA zajmuje po prostu więcej bajtów do kodowania, ponieważ [eax*4]wymaga rozszerzenia disp32=0. (Działa jednak na innych portach niż zmiany.) Zobacz agner.org/optimize i stackoverflow.com/tags/x86/info .
Instrukcja MOV reg, addr oznacza odczyt zmiennej przechowywanej pod adresem addr do rejestru reg. Instrukcja LEA reg, addr oznacza odczyt adresu (a nie zmiennej przechowywanej pod adresem) do rejestru reg.
Inną formą instrukcji MOV jest MOV reg, immdata, co oznacza wczytanie natychmiastowych danych (tj. Stałych) immdata do rejestru reg. Zauważ, że jeśli addr w LEA reg, addr jest po prostu stałą (tj. Stałym przesunięciem), to ta instrukcja LEA jest zasadniczo dokładnie taka sama, jak równoważna instrukcja MOV reg, immdata, która ładuje tę samą stałą, co dane bezpośrednie.
Jeśli podasz tylko literał, nie ma różnicy. LEA ma jednak więcej umiejętności, o których możesz przeczytać tutaj:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
leal TextLabel, LabelFromBssSegmentmożesz, kiedy coś masz. jakbyś .bss .lcomm LabelFromBssSegment, 4musiał movl $TextLabel, LabelFromBssSegment, prawda?
leawymaga miejsca docelowego rejestru, ale movmoże mieć imm32źródło i miejsce docelowe pamięci. To ograniczenie oczywiście nie jest specyficzne dla asemblera GNU.
MOV AX, [TABLE-ADDR]obciążenia. Jest więc zasadnicza różnica. Równoważna instrukcja brzmimov ax, OFFSET table_addr
To zależy od używanego asemblera, ponieważ
mov ax,table_addr
w MASM działa jako
mov ax,word ptr[table_addr]
Więc ładuje pierwsze bajty zi table_addrNIE przesunięcie do table_addr. Zamiast tego powinieneś użyć
mov ax,offset table_addr
lub
lea ax,table_addr
który działa tak samo.
leawersja działa również dobrze, jeśli table_addrjest zmienną lokalną np
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
Żadna z poprzednich odpowiedzi nie doprowadziła do końca mojego zamieszania, więc chciałbym dodać własną.
Brakowało mi tego, że leaoperacje traktują użycie nawiasów inaczej niż w jaki movsposób.
Pomyśl o C. Powiedzmy, że mam tablicę long, którą nazywam array. Teraz wyrażenie array[i]wykonuje dereferencję, ładując wartość z pamięci pod adresem array + i * sizeof(long)[1].
Z drugiej strony rozważ wyrażenie &array[i]. To nadal zawiera wyrażenie podrzędne array[i], ale nie jest wykonywane żadne wyłuskiwanie! Znaczenie się array[i]zmieniło. Nie oznacza już szacunku, ale działa jako rodzaj specyfikacji , mówiącej, &jakiego adresu pamięci szukamy. Jeśli chcesz, możesz alternatywnie pomyśleć o &„anulowaniu” wyłuskiwania.
Ponieważ te dwa przypadki użycia są podobne pod wieloma względami, mają wspólną składnię array[i], ale istnienie lub brak &zmiany zmienia sposób interpretacji tej składni. Bez &tego jest to dereferencja i faktycznie czyta z tablicy. Tak &nie jest. Wartość array + i * sizeof(long)jest nadal obliczana, ale nie jest odwoływana.
Sytuacja jest bardzo podobna w przypadku movi lea. W movprzypadku występuje dereferencja, która nie ma miejsca w przypadku lea. Dzieje się tak pomimo użycia nawiasów, które występują w obu przypadkach. Na przykład movq (%r8), %r9i leaq (%r8), %r9. W przypadku movtych nawiasów oznacza „wyłuskiwanie”; z lea, nie robią. Jest to podobne do sposobu, w jaki array[i]oznacza „wyłuskiwanie” tylko wtedy, gdy nie ma &.
Przykład jest w porządku.
Rozważ kod
movq (%rdi, %rsi, 8), %rbp
Spowoduje to załadowanie wartości z lokalizacji pamięci %rdi + %rsi * 8do rejestru %rbp. To znaczy: pobierz wartość w rejestrze %rdii wartość w rejestrze %rsi. Pomnóż tę ostatnią przez 8, a następnie dodaj ją do pierwszej. Znajdź wartość w tej lokalizacji i umieść ją w rejestrze %rbp.
Ten kod odpowiada linii C x = array[i];, gdzie arraystaje się %rdii istaje się %rsii xstaje się %rbp. Jest 8to długość typu danych zawartego w tablicy.
Teraz rozważ podobny kod, który używa lea:
leaq (%rdi, %rsi, 8), %rbp
Podobnie jak wykorzystaniu movqodzwierciedlał dereferencing, użycie leaqtutaj odpowiada nie dereferencji. Ta linia montażowa odpowiada linii C x = &array[i];. Przypomnijmy, że &zmienia to znaczenie array[i]z wyłuskiwania odwołań do prostego określenia lokalizacji. Podobnie użycie leaqzmiany zmienia znaczenie (%rdi, %rsi, 8)z wyłuskiwania odwołań do określania lokalizacji.
Semantyka tego wiersza kodu jest następująca: pobierz wartość w rejestrze %rdii wartość w rejestrze %rsi. Pomnóż tę ostatnią przez 8, a następnie dodaj ją do pierwszej. Umieść tę wartość w rejestrze %rbp. Nie jest wymagane żadne obciążenie z pamięci, tylko operacje arytmetyczne [2].
Zauważ, że jedyna różnica między moimi opisami opcji leaqi movqpolega na tym, że movqdokonuje wyłuskiwania, a leaqnie. Właściwie, aby napisać leaqopis, po prostu skopiowałem + wkleiłem opis movq, a następnie usunąłem „Znajdź wartość w tej lokalizacji”.
Podsumowując: movqvs. leaqjest trudne, ponieważ traktują użycie nawiasów tak jak w (%rsi)i (%rdi, %rsi, 8), inaczej. W movq(i we wszystkich innych instrukcjach z wyjątkiem lea) te nawiasy oznaczają autentyczną dereferencję, podczas gdy w leaqnich nie mają i są czysto wygodną składnią.
[1] Powiedziałem, że kiedy arrayjest tablicą long, wyrażenie array[i]ładuje wartość z adresu array + i * sizeof(long). To prawda, ale należy się zająć pewną subtelnością. Jeśli napiszę kod C.
long x = array[5];
to nie to samo, co pisanie
long x = *(array + 5 * sizeof(long));
Wydaje się, że powinno być oparte na moich wcześniejszych wypowiedziach, ale tak nie jest.
Chodzi o to, że dodawanie wskaźnika C ma w sobie sztuczkę. Powiedzmy, że mam wskaźnik pwskazujący na wartości typu T. Wyrażenie p + ima nie znaczyć „pozycja na pplus ibajtów”. Zamiast tego wyrażenie p + i faktycznie oznacza „pozycję z pplusem i * sizeof(T)bajtów”.
Wygoda polega na tym, że aby uzyskać „następną wartość”, po prostu musimy p + 1zamiast tego pisać p + 1 * sizeof(T).
Oznacza to, że kod C long x = array[5];jest faktycznie odpowiednikiem
long x = *(array + 5)
ponieważ C automatycznie pomnoży 5przez sizeof(long).
Więc w kontekście tego pytania StackOverflow, jak to wszystko ma znaczenie? Oznacza to, że kiedy mówię „adres array + i * sizeof(long)”, ja nie myśli o „ array + i * sizeof(long)” należy interpretować jako wyrażenie C. Mnożę sizeof(long)samodzielnie, aby uściślić moją odpowiedź, ale rozumiem, że z tego powodu wyrażenie to nie powinno być odczytywane jako C. Tak jak zwykła matematyka, która używa składni C.
[2] Uwaga dodatkowa: ponieważ wszystko learobi jest operacjami arytmetycznymi, jego argumenty nie muszą w rzeczywistości odnosić się do poprawnych adresów. Z tego powodu jest często używany do wykonywania czystej arytmetyki na wartościach, które mogą nie być wyłuskiwane. Na przykład ccz -O2optymalizacją przekłada się
long f(long x) {
return x * 5;
}
do następującego (usunięto nieistotne wiersze):
f:
leaq (%rdi, %rdi, 4), %rax # set %rax to %rdi + %rdi * 4
ret
&operator C to dobra analogia. Być może warto zauważyć, że LEA jest przypadkiem specjalnym, podczas gdy MOV jest jak każda inna instrukcja, która może zająć operand pamięci lub rejestr. np. add (%rdi), %eaxpo prostu używa trybu adresowania do adresowania pamięci, tak samo jak MOV. Również powiązane: Używanie LEA na wartościach, które nie są adresami / wskaźnikami? kontynuuje to wyjaśnienie: LEA to sposób, w jaki można wykorzystać wsparcie sprzętowe procesora dla matematyki adresowej do wykonywania dowolnych obliczeń.
%rdi” - to dziwnie sformułowane. Masz na myśli, że należy użyć wartości w rejestrze rdi . Twoje użycie „at” wydaje się oznaczać wyłuskiwanie pamięci tam, gdzie jej nie ma.
%rdi ” lub „wartości w %rdi ”. Twoja „wartość w rejestrze %rdi” jest długa, ale w porządku i może pomóc komuś, kto ma problemy ze zrozumieniem rejestrów i pamięci.
Zasadniczo ... "Przenieś się do REG ... po obliczeniu ..." wydaje się być również przydatne do innych celów :)
jeśli po prostu zapomnisz, że wartość jest wskaźnikiem, możesz jej użyć do optymalizacji / minimalizacji kodu ... cokolwiek ...
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
pierwotnie byłoby to:
MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5
leajest to instrukcja przesuwania i dodawania, która używa maszynowego kodowania i składni operandów pamięci, ponieważ sprzęt już wie, jak dekodować ModR / M + SIB + disp0 / 8/32.
Jak stwierdzono w innych odpowiedziach:
MOVprzechwyci dane na adres wewnątrz wsporników i miejscu, że dane do docelowego argumentu.LEAwykona obliczenie adresu wewnątrz nawiasów i umieści obliczony adres w operandzie docelowym. Dzieje się to bez wychodzenia do pamięci i pobierania danych. Praca wykonywana przez program LEApolega na obliczaniu „efektywnego adresu”.Ponieważ pamięć może być adresowana na kilka różnych sposobów (patrz przykłady poniżej), LEAczasami jest używana do dodawania lub mnożenia rejestrów razem bez użycia jawnej instrukcji ADDlub MULinstrukcji (lub równoważnej).
Ponieważ wszyscy pokazują przykłady w składni Intela, oto kilka w składni 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, %eaxabsolutnego [disp32]trybu adresowania. Użyj mov $label, %eaxzamiast tego. Tak, działa, ale jest mniej wydajne (większy kod maszynowy i działa na mniejszej liczbie jednostek wykonawczych). Skoro wspomniałeś o AT&T, używaniu LEA na wartościach, które nie są adresami / wskaźnikami? używa AT&T, a moja odpowiedź zawiera kilka innych przykładów AT&T.
Zrozummy to na przykładzie.
mov eax, [ebx] i
lea eax, [ebx] Załóżmy, że wartość w ebx to 0x400000. Następnie mov przejdzie na adres 0x400000 i przekopiuje 4 bajty prezentowanych danych do rejestru eax, przy czym lea skopiuje adres 0x400000 do eax. Czyli po wykonaniu każdej instrukcji wartość eax w każdym przypadku będzie (zakładając, że w pamięci 0x400000 zawiera 30).
eax = 30 (w przypadku mov) eax = 0x400000 (w przypadku lea) W celu zdefiniowania mov skopiuj dane z rm32 do celu (mov dest rm32) a lea (załaduj efektywny adres) skopiuje adres do celu (mov dest rm32 ).
MOV może zrobić to samo co LEA [etykieta], ale instrukcja MOV zawiera efektywny adres wewnątrz samej instrukcji jako stałą natychmiastową (obliczoną z góry przez asemblera). LEA używa względem PC do obliczenia efektywnego adresu podczas wykonywania instrukcji.
lea [labeljest to bezcelowe marnowanie bajtów w porównaniu z bardziej zwartym mov, więc powinieneś określić warunki, o których mówisz. Ponadto dla niektórych asemblerów [label]składnia nie jest odpowiednia dla trybu adresowania względnego w protokole RIP. Ale tak, to prawda. Jak załadować adres funkcji lub etykiety do rejestru w GNU Assembler wyjaśnia bardziej szczegółowo.
Różnica jest subtelna, ale ważna. Instrukcja MOV jest „MOVe” w rzeczywistości kopią adresu, który reprezentuje etykieta TABLE-ADDR. Instrukcja LEA jest „Load Effective Address”, która jest instrukcją pośrednią, co oznacza, że TABLE-ADDR wskazuje miejsce w pamięci, w którym znajduje się adres do załadowania.
Skuteczne używanie LEA jest równoważne używaniu wskaźników w językach takich jak C, ponieważ jest to potężna instrukcja.