Was ist der Unterschied zwischen MOV und LEA?

138

Ich würde gerne wissen, was der Unterschied zwischen diesen Anweisungen ist:

MOV AX, [TABLE-ADDR]

und

LEA AX, [TABLE-ADDR]
Naveen
quelle
5
Duplikat: stackoverflow.com/questions/1658294/…
Nick Dandoulakis
8
danke nick. Erstens hätte ich keine Antwort auf diese Frage gefunden, wenn ich mir diesen Link angesehen hätte. Hier habe ich nach einer bestimmten Information gesucht. Die Diskussion in dem von Ihnen bereitgestellten Link ist eher allgemeiner Natur.
Naveen
3
Ich habe @ Nick's Dup vor langer Zeit upvoted, aber vtc'd gerade jetzt. Nachdenklich war ich zu voreilig und jetzt mit naveen, dass a) die andere Frage nicht "was ist der Unterschied" beantwortet und b) dies eine nützliche Frage ist. Entschuldigung für meinen Fehler - wenn ich nur vtc rückgängig machen könnte ...
Ruben Bartelink
1
LEA vs hinzufügen: stackoverflow.com/questions/6323027/lea-or-add-instruction
Ciro Santilli 法轮功 冠状 病. 六四
Verwandte Themen : Verwenden von LEA für Werte, die keine Adressen / Zeiger sind? spricht über andere Verwendungen von LEA für beliebige Mathematik.
Peter Cordes

Antworten:

168
  • LEA bedeutet Laden der effektiven Adresse
  • MOV bedeutet Wert laden

Kurz gesagt, LEAlädt einen Zeiger auf das Element, das Sie adressieren, während MOV den tatsächlichen Wert an dieser Adresse lädt.

Der Zweck von LEAbesteht darin, eine nicht triviale Adressberechnung durchzuführen und das Ergebnis [zur späteren Verwendung] zu speichern.

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

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

Wenn es sich nur um Konstanten handelt MOV(durch die konstanten Berechnungen des Assemblers), kann es manchmal vorkommen, dass sie sich mit den einfachsten Verwendungsfällen von überschneiden LEA. Es ist nützlich, wenn Sie eine mehrteilige Berechnung mit mehreren Basisadressen usw. haben.

Ruben Bartelink
quelle
6
+1 danke für die klare erklärung, hat mir geholfen eine andere frage zu beantworten .
Legends2k
Es verwirrt mich, dass lea "load" im Namen hat und die Leute sagen, dass es eine berechnete Adresse in ein Register "lädt", weil alle Eingaben zur Berechnung des Speicherorts entweder unmittelbare Werte oder Register sind. AFAICT lea führt nur eine Berechnung durch, es wird nichts geladen, wobei Laden bedeutet, den Speicher zu berühren?
Joseph Garvin
2
@josephGarvin IIRC Der Begriff Fetch würde auf diesen Aspekt angewendet. Beim Laden ersetzen Sie den Wert in einem Register durch etwas von Grund auf neu. Beispiel LAHF: Laden Sie FLAGS in das AH-Register . In der CIL der CLR (die eine stapelbasierte abstrakte Maschine auf höherer Ebene ist, bezieht sich der Begriff load auf das Setzen eines Werts auf den fiktiven Stapel und ist normalerweise l... und das s... Äquivalent macht das Inverse). Diese Hinweise: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) legen nahe, dass es tatsächlich Architekturen gibt, für die Ihre Unterscheidung gilt.
Ruben Bartelink
das alles erinnert mich an slidehare.net/pirhilton/… ;)
Ruben Bartelink
45

In der NASM-Syntax:

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

Verwenden Sie OFFSET varin der MASM-Syntax, um anstelle eines Ladevorgangs einen Mov-Instant abzurufen.

Amit Singh Tomar
quelle
3
Nur in NASM-Syntax. In der MASM-Syntax mov eax, varist eine Last dieselbe wie mov eax, [var]und Sie müssen sie verwenden mov eax, OFFSET var, um eine Bezeichnung als unmittelbare Konstante zu verwenden.
Peter Cordes
1
Klar, einfach und zeigt, was ich zu bestätigen versuchte. Vielen Dank.
JayArby
1
Beachten Sie, dass in all diesen Beispielen leadie schlechtere Wahl ist, außer im 64-Bit-Modus für die RIP-relative Adressierung. mov r32, imm32läuft auf mehr Ports. lea eax, [edx*4]ist ein Copy-and-Shift, das sonst nicht in einem Befehl ausgeführt werden kann, aber im selben Register benötigt LEA nur mehr Bytes zum Codieren, da a [eax*4]erforderlich ist disp32=0. (Es läuft jedoch auf anderen Ports als Shifts.) Siehe agner.org/optimize und stackoverflow.com/tags/x86/info .
Peter Cordes
28

Der Befehl MOV reg, addr bedeutet, eine an der Adresse addr gespeicherte Variable in das Register reg zu lesen. Der Befehl LEA reg, addr bedeutet, die Adresse (nicht die an der Adresse gespeicherte Variable) in das Register reg zu lesen.

Eine andere Form des MOV-Befehls ist MOV reg, immdata, was bedeutet, dass die unmittelbaren Daten (dh konstanten) immdata in register reg gelesen werden. Beachten Sie, dass, wenn die Adresse in LEA reg, addr nur eine Konstante ist (dh ein fester Versatz), dieser LEA-Befehl im Wesentlichen genau der gleiche ist wie ein äquivalenter Befehl MOV reg, immdata, der dieselbe Konstante wie unmittelbare Daten lädt.

Bill Forster
quelle
11

Wenn Sie nur ein Literal angeben, gibt es keinen Unterschied. LEA hat jedoch mehr Fähigkeiten und Sie können hier darüber lesen:

http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136

Lars D.
quelle
Ich denke, mit der Ausnahme, dass es in GNU Assembler nicht stimmt, wenn es um Labels im .bss-Segment geht? AFAIR können Sie nicht wirklich, leal TextLabel, LabelFromBssSegmentwenn Sie etw bekommen. wie .bss .lcomm LabelFromBssSegment, 4, müssten Sie movl $TextLabel, LabelFromBssSegment, nicht wahr?
JSmyth
@JSmyth: Das liegt nur daran, leadass ein Registerziel erforderlich ist, aber moveine imm32Quelle und ein Speicherziel haben kann. Diese Einschränkung ist natürlich nicht spezifisch für den GNU-Assembler.
Peter Cordes
1
Außerdem ist diese Antwort grundsätzlich falsch, da die Frage nach MOV AX, [TABLE-ADDR]einer Last gestellt wird. Es gibt also einen großen Unterschied. Die entsprechende Anweisung istmov ax, OFFSET table_addr
Peter Cordes
10

Es kommt auf den verwendeten Assembler an, weil

mov ax,table_addr

in MASM funktioniert als

mov ax,word ptr[table_addr]

Es werden also die ersten Bytes von table_addrund NICHT der Offset von geladen table_addr. Sie sollten stattdessen verwenden

mov ax,offset table_addr

oder

lea ax,table_addr

das funktioniert genauso.

leaVersion funktioniert auch gut, wenn table_addres sich um eine lokale Variable handelt, z

some_procedure proc

local table_addr[64]:word

lea ax,table_addr
Bartosz Wójcik
quelle
Vielen
5
Der Unterschied zwischen den x86-Anweisungen MOV und LEA hängt definitiv NICHT vom Assembler ab.
IJ Kennedy
4

Keine der vorherigen Antworten hat meine eigene Verwirrung auf den Grund gebracht, daher möchte ich meine eigene hinzufügen.

Was mir gefehlt hat, ist, dass leaOperationen die Verwendung von Klammern anders behandeln als wie mov.

Denken Sie an C. Nehmen wir an, ich habe eine Reihe davon long, die ich anrufe array. Jetzt führt der Ausdruck array[i]eine Dereferenzierung durch und lädt den Wert aus dem Speicher an die Adresse array + i * sizeof(long)[1].

Betrachten Sie andererseits den Ausdruck &array[i]. Dieser enthält noch den Unterausdruck array[i], aber es wird keine Dereferenzierung durchgeführt! Die Bedeutung von array[i]hat sich geändert. Es bedeutet nicht mehr, eine Ehrerbietung vorzunehmen, sondern fungiert als eine Art Spezifikation , die angibt, nach &welcher Speicheradresse wir suchen. Wenn Sie möchten, können Sie sich alternativ &vorstellen, dass die Dereferenzierung "aufgehoben" wird.

Da die beiden Anwendungsfälle in vielerlei Hinsicht ähnlich sind, teilen sie die Syntax array[i], aber das Vorhandensein oder Fehlen einer &Änderung ändert die Interpretation dieser Syntax. Ohne &ist es eine Dereferenzierung und liest tatsächlich aus dem Array. Mit &ist es nicht. Der Wert array + i * sizeof(long)wird noch berechnet, aber nicht dereferenziert.

Die Situation ist mit movund sehr ähnlich lea. Mit movtritt eine Dereferenzierung auf, die mit nicht auftritt lea. Dies gilt trotz der Verwendung von Klammern, die in beiden Fällen vorkommen. Zum Beispiel movq (%r8), %r9und leaq (%r8), %r9. Mit movbedeuten diese Klammern "Dereferenzierung"; mit leatun sie nicht. Dies ist ähnlich wie array[i]nur "Dereferenzierung" bedeutet, wenn es keine gibt &.

Ein Beispiel ist in Ordnung.

Betrachten Sie den Code

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

Dies lädt den Wert am Speicherort %rdi + %rsi * 8in das Register %rbp. Das heißt: Holen Sie sich den Wert im Register %rdiund den Wert im Register %rsi. Multiplizieren Sie das letztere mit 8 und fügen Sie es dann dem ersteren hinzu. Suchen Sie den Wert an dieser Stelle und legen Sie ihn in das Register %rbp.

Dieser Code entspricht der C-Linie x = array[i];, in der arraywird %rdiund iwird %rsiund xwird %rbp. Dies 8ist die Länge des im Array enthaltenen Datentyps.

Betrachten Sie nun einen ähnlichen Code, der Folgendes verwendet lea:

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

So wie die Verwendung von movqder Dereferenzierung entsprach, entspricht die Verwendung von leaqhier der Nicht- Dereferenzierung. Diese Linie der Anordnung entspricht der C - Linie x = &array[i];. Denken Sie daran, dass &sich die Bedeutung von array[i]der Dereferenzierung zur einfachen Angabe eines Standorts ändert . Ebenso leaqändert die Verwendung von die Bedeutung von (%rdi, %rsi, 8)der Dereferenzierung zur Angabe eines Standorts.

Die Semantik dieser Codezeile lautet wie folgt: Ermitteln Sie den Wert im Register %rdiund den Wert im Register %rsi. Multiplizieren Sie das letztere mit 8 und fügen Sie es dann dem ersteren hinzu. Tragen Sie diesen Wert in das Register ein %rbp. Es wird kein Speicher geladen, sondern nur arithmetische Operationen [2].

Beachten Sie, dass der einzige Unterschied zwischen meinen Beschreibungen von leaqund darin movqbesteht, dass movqeine Dereferenzierung durchgeführt wird und leaqnicht. Um die leaqBeschreibung zu schreiben , habe ich die Beschreibung von kopiert + eingefügt movqund dann "Finde den Wert an dieser Stelle" entfernt.

Zusammenfassend: movqvs. leaqist schwierig, weil sie die Verwendung von Klammern wie in (%rsi)und (%rdi, %rsi, 8)unterschiedlich behandeln. In movq(und allen anderen Anweisungen außer lea) bezeichnen diese Klammern eine echte Dereferenzierung, während leaqsie in und keine rein bequeme Syntax sind.


[1] Ich habe gesagt, wenn arrayes sich um ein Array von handelt long, array[i]lädt der Ausdruck den Wert von der Adresse array + i * sizeof(long). Dies ist wahr, aber es gibt eine Subtilität, die angesprochen werden sollte. Wenn ich den C-Code schreibe

long x = array[5];

Dies ist nicht dasselbe wie Tippen

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

Es scheint, dass es auf meinen vorherigen Aussagen basieren sollte , aber es ist nicht.

Was los ist, ist, dass das Hinzufügen von C-Zeigern einen Trick hat. Angenommen, ich habe einen Zeiger p, der auf Werte vom Typ zeigt T. Der Ausdruck p + ibedeutet nicht "die Position bei pPlus- iBytes". Stattdessen bedeutet der Ausdruck p + i tatsächlich "die Position bei pPlus- i * sizeof(T)Bytes".

Die Bequemlichkeit ist , dass „der nächste Wert“ erhalten müssen wir nur schreiben p + 1statt p + 1 * sizeof(T).

Dies bedeutet, dass der C-Code long x = array[5];tatsächlich äquivalent zu ist

long x = *(array + 5)

weil C automatisch mehrfach die 5durch sizeof(long).

Wie ist das alles im Kontext dieser StackOverflow-Frage relevant? Wenn ich "die Adresse array + i * sizeof(long)" sage, bedeutet dies nicht , dass " array + i * sizeof(long)" als C-Ausdruck interpretiert wird. Ich mache die Multiplikation sizeof(long)selbst, um meine Antwort deutlicher zu machen, aber ich verstehe, dass dieser Ausdruck aus diesem Grund nicht als C gelesen werden sollte. Genau wie normale Mathematik, die C-Syntax verwendet.

[2] Randnotiz: Da leaes sich lediglich um arithmetische Operationen handelt, müssen sich die Argumente nicht auf gültige Adressen beziehen. Aus diesem Grund wird es häufig verwendet, um reine Arithmetik für Werte durchzuführen, die möglicherweise nicht dereferenziert werden sollen. Zum Beispiel ccmit -O2Optimierung übersetzt

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

in die folgenden (irrelevante Zeilen entfernt):

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret
Quelklef
quelle
1
Ja, gute Erklärung, detaillierter als die anderen Antworten, und ja, der &Operator von C ist eine gute Analogie. Vielleicht ist darauf hinzuweisen, dass LEA der Sonderfall ist, während MOV genau wie jeder andere Befehl ist, der einen Speicher- oder Registeroperanden aufnehmen kann. zB verwendet add (%rdi), %eaxnur den Adressierungsmodus, um den Speicher zu adressieren, genau wie MOV. Ebenfalls verwandt: LEA für Werte verwenden, die keine Adressen / Zeiger sind? führt diese Erklärung weiter: Mit LEA können Sie die HW-Unterstützung der CPU für die Adressmathematik verwenden, um beliebige Berechnungen durchzuführen.
Peter Cordes
"get the value at %rdi" - Dies ist seltsam formuliert. Sie meinen, dass der Wert im Register rdi verwendet werden sollte. Ihre Verwendung von "at" scheint eine Gedächtnisstörung zu bedeuten, bei der es keine gibt.
ecm
@ PeterCordes Danke! Ich habe der Antwort den Punkt hinzugefügt, dass es sich um einen Sonderfall handelt.
Quelklef
1
@ecm Guter Punkt; Das habe ich nicht bemerkt. Ich habe es jetzt geändert, danke! :)
Quelklef
Zu Ihrer Information, kürzere Formulierungen, die das Problem beheben, auf das ecm hingewiesen hat, umfassen: "den Wert von %rdi " oder "den Wert in %rdi ". Ihr "Wert im Register %rdi" ist lang, aber in Ordnung und kann möglicherweise jemandem helfen, der Schwierigkeiten hat, Register und Speicher zu verstehen.
Peter Cordes
2

Grundsätzlich ... "Bewegen Sie sich in REG ... nachdem Sie es berechnet haben ..." scheint es auch für andere Zwecke nett zu sein :)

Wenn Sie nur vergessen, dass der Wert ein Zeiger ist, können Sie ihn für Codeoptimierungen / -minimierungen verwenden ... was auch immer ..

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

ursprünglich wäre es:

MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5
Ostap
quelle
Yup, leaist ein Shift-and-Add-Befehl , der die Codierung und Syntax von Speicheroperandenmaschinen verwendet, da die Hardware bereits weiß, wie ModR / M + SIB + disp0 / 8/32 zu decodieren ist.
Peter Cordes
1

Wie in den anderen Antworten angegeben:

  • MOVgreift auf die Daten an der Adresse in den Klammern zu und platziert diese Daten im Zieloperanden.
  • LEAführt die Berechnung der Adresse in den Klammern durch und platziert diese berechnete Adresse in dem Zieloperanden. Dies geschieht, ohne tatsächlich in den Speicher zu gehen und die Daten abzurufen. Die Arbeit von LEAist in der Berechnung der "effektiven Adresse".

Da der Speicher auf verschiedene Arten adressiert werden kann (siehe Beispiele unten), LEAwird er manchmal verwendet, um Register zu addieren oder zu multiplizieren, ohne eine explizite Anweisung ADDoder eine MULAnweisung (oder eine äquivalente Anweisung) zu verwenden.

Da alle Beispiele in der Intel-Syntax zeigen, sind hier einige in der AT & T-Syntax:

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 */
Sir Random
quelle
Sie möchten niemals lea label, %eaxeinen absoluten [disp32]Adressierungsmodus. Verwenden Sie mov $label, %eaxstattdessen. Ja, es funktioniert, aber es ist weniger effizient (größerer Maschinencode und läuft auf weniger Ausführungseinheiten). Da Sie AT & T erwähnen, LEA für Werte verwenden, die keine Adressen / Zeiger sind? verwendet AT & T, und meine Antwort dort enthält einige andere AT & T-Beispiele.
Peter Cordes
1

Lassen Sie uns dies anhand eines Beispiels verstehen.

mov eax, [ebx] und

lea eax, [ebx] Angenommen, der Wert in ebx ist 0x400000. Dann geht mov zur Adresse 0x400000 und kopiert 4 Byte Daten, die im eax-Register vorhanden sind. Dabei kopiert lea die Adresse 0x400000 in eax. Also, nach der Ausführung jedes Befehls wird der Wert von eax in jedem Fall sein (vorausgesetzt im Speicher 0x400000 enthalten ist 30).

eax = 30 (im Falle von mov) eax = 0x400000 (im Falle von lea) Zur Definition von mov kopieren Sie die Daten von rm32 zum Ziel (mov dest rm32) und lea (effektive Adresse laden) kopiert die Adresse zum Ziel (mov dest rm32) ).

Luftatako
quelle
0

LEA (Load Effective Address) ist eine Shift-and-Add-Anweisung. Es wurde zu 8086 hinzugefügt, da Hardware zum Dekodieren und Berechnen von Adressierungsmodi vorhanden ist.

Jojasicek
quelle
0

MOV kann dasselbe tun wie LEA [label], aber der MOV-Befehl enthält die effektive Adresse innerhalb des Befehls selbst als unmittelbare Konstante (vom Assembler im Voraus berechnet). LEA verwendet PC-relativ, um die effektive Adresse während der Ausführung des Befehls zu berechnen.

Michel Sayde
quelle
Dies gilt nur für den 64-Bit-Modus (bei dem die PC-relative Adressierung neu war). In anderen Modi lea [labelist es eine sinnlose Verschwendung von Bytes im Vergleich zu einer kompakteren mov. Daher sollten Sie die Bedingungen angeben, über die Sie sprechen. Außerdem ist für einige Assembler [label]nicht die richtige Syntax für einen RIP-relativen Adressierungsmodus. Aber ja, das stimmt. Wie die Adresse der Funktion oder des Labels in das Register in GNU Assembler geladen wird, wird ausführlicher erläutert.
Peter Cordes
-1

Der Unterschied ist subtil, aber wichtig. Der MOV-Befehl ist ein 'MOVe', effektiv eine Kopie der Adresse, für die das TABLE-ADDR-Label steht. Der LEA-Befehl ist eine 'Load Effective Address', eine indirekte Anweisung. Dies bedeutet, dass TABLE-ADDR auf einen Speicherort zeigt, an dem sich die zu ladende Adresse befindet.

Die effektive Verwendung von LEA entspricht der Verwendung von Zeigern in Sprachen wie C, da dies eine leistungsstarke Anweisung ist.

Guillermo Phillips
quelle
7
Ich finde diese Antwort bestenfalls verwirrend. "Der LEA-Befehl ist eine 'Load Effective Address', eine indirekte Anweisung, was bedeutet, dass TABLE-ADDR auf einen Speicherort zeigt, an dem sich die zu ladende Adresse befindet." Tatsächlich lädt LEA die Adresse, nicht den Inhalt der Adresse. Ich denke, tatsächlich muss der Fragesteller beruhigt werden, dass sich MOV und LEA unter bestimmten Umständen überschneiden und genau dasselbe tun können
Bill Forster,