Ich würde gerne wissen, was der Unterschied zwischen diesen Anweisungen ist:
MOV AX, [TABLE-ADDR]
und
LEA AX, [TABLE-ADDR]
assembly
x86
instruction-set
Naveen
quelle
quelle
Antworten:
LEA
bedeutet Laden der effektiven AdresseMOV
bedeutet Wert ladenKurz gesagt,
LEA
lädt einen Zeiger auf das Element, das Sie adressieren, während MOV den tatsächlichen Wert an dieser Adresse lädt.Der Zweck von
LEA
besteht darin, eine nicht triviale Adressberechnung durchzuführen und das Ergebnis [zur späteren Verwendung] zu speichern.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 überschneidenLEA
. Es ist nützlich, wenn Sie eine mehrteilige Berechnung mit mehreren Basisadressen usw. haben.quelle
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 normalerweisel
... und dass
... Ä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.In der NASM-Syntax:
Verwenden Sie
OFFSET var
in der MASM-Syntax, um anstelle eines Ladevorgangs einen Mov-Instant abzurufen.quelle
mov eax, var
ist eine Last dieselbe wiemov eax, [var]
und Sie müssen sie verwendenmov eax, OFFSET var
, um eine Bezeichnung als unmittelbare Konstante zu verwenden.lea
die schlechtere Wahl ist, außer im 64-Bit-Modus für die RIP-relative Adressierung.mov r32, imm32
lä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 istdisp32=0
. (Es läuft jedoch auf anderen Ports als Shifts.) Siehe agner.org/optimize und stackoverflow.com/tags/x86/info .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.
quelle
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
quelle
leal TextLabel, LabelFromBssSegment
wenn Sie etw bekommen. wie.bss .lcomm LabelFromBssSegment, 4
, müssten Siemovl $TextLabel, LabelFromBssSegment
, nicht wahr?lea
dass ein Registerziel erforderlich ist, abermov
eineimm32
Quelle und ein Speicherziel haben kann. Diese Einschränkung ist natürlich nicht spezifisch für den GNU-Assembler.MOV AX, [TABLE-ADDR]
einer Last gestellt wird. Es gibt also einen großen Unterschied. Die entsprechende Anweisung istmov ax, OFFSET table_addr
Es kommt auf den verwendeten Assembler an, weil
in MASM funktioniert als
Es werden also die ersten Bytes von
table_addr
und NICHT der Offset von geladentable_addr
. Sie sollten stattdessen verwendenoder
das funktioniert genauso.
lea
Version funktioniert auch gut, wenntable_addr
es sich um eine lokale Variable handelt, zquelle
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
lea
Operationen die Verwendung von Klammern anders behandeln als wiemov
.Denken Sie an C. Nehmen wir an, ich habe eine Reihe davon
long
, die ich anrufearray
. Jetzt führt der Ausdruckarray[i]
eine Dereferenzierung durch und lädt den Wert aus dem Speicher an die Adressearray + i * sizeof(long)
[1].Betrachten Sie andererseits den Ausdruck
&array[i]
. Dieser enthält noch den Unterausdruckarray[i]
, aber es wird keine Dereferenzierung durchgeführt! Die Bedeutung vonarray[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 Wertarray + i * sizeof(long)
wird noch berechnet, aber nicht dereferenziert.Die Situation ist mit
mov
und sehr ähnlichlea
. Mitmov
tritt eine Dereferenzierung auf, die mit nicht auftrittlea
. Dies gilt trotz der Verwendung von Klammern, die in beiden Fällen vorkommen. Zum Beispielmovq (%r8), %r9
undleaq (%r8), %r9
. Mitmov
bedeuten diese Klammern "Dereferenzierung"; mitlea
tun sie nicht. Dies ist ähnlich wiearray[i]
nur "Dereferenzierung" bedeutet, wenn es keine gibt&
.Ein Beispiel ist in Ordnung.
Betrachten Sie den Code
Dies lädt den Wert am Speicherort
%rdi + %rsi * 8
in das Register%rbp
. Das heißt: Holen Sie sich den Wert im Register%rdi
und 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 derarray
wird%rdi
undi
wird%rsi
undx
wird%rbp
. Dies8
ist die Länge des im Array enthaltenen Datentyps.Betrachten Sie nun einen ähnlichen Code, der Folgendes verwendet
lea
:So wie die Verwendung von
movq
der Dereferenzierung entsprach, entspricht die Verwendung vonleaq
hier der Nicht- Dereferenzierung. Diese Linie der Anordnung entspricht der C - Liniex = &array[i];
. Denken Sie daran, dass&
sich die Bedeutung vonarray[i]
der Dereferenzierung zur einfachen Angabe eines Standorts ändert . Ebensoleaq
ä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
%rdi
und 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
leaq
und darinmovq
besteht, dassmovq
eine Dereferenzierung durchgeführt wird undleaq
nicht. Um dieleaq
Beschreibung zu schreiben , habe ich die Beschreibung von kopiert + eingefügtmovq
und dann "Finde den Wert an dieser Stelle" entfernt.Zusammenfassend:
movq
vs.leaq
ist schwierig, weil sie die Verwendung von Klammern wie in(%rsi)
und(%rdi, %rsi, 8)
unterschiedlich behandeln. Inmovq
(und allen anderen Anweisungen außerlea
) bezeichnen diese Klammern eine echte Dereferenzierung, währendleaq
sie in und keine rein bequeme Syntax sind.[1] Ich habe gesagt, wenn
array
es sich um ein Array von handeltlong
,array[i]
lädt der Ausdruck den Wert von der Adressearray + i * sizeof(long)
. Dies ist wahr, aber es gibt eine Subtilität, die angesprochen werden sollte. Wenn ich den C-Code schreibeDies ist nicht dasselbe wie Tippen
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 zeigtT
. Der Ausdruckp + i
bedeutet nicht "die Position beip
Plus-i
Bytes". Stattdessen bedeutet der Ausdruckp + i
tatsächlich "die Position beip
Plus-i * sizeof(T)
Bytes".Die Bequemlichkeit ist , dass „der nächste Wert“ erhalten müssen wir nur schreiben
p + 1
stattp + 1 * sizeof(T)
.Dies bedeutet, dass der C-Code
long x = array[5];
tatsächlich äquivalent zu istweil C automatisch mehrfach die
5
durchsizeof(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 Multiplikationsizeof(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
lea
es 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 Beispielcc
mit-O2
Optimierung übersetztin die folgenden (irrelevante Zeilen entfernt):
quelle
&
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 verwendetadd (%rdi), %eax
nur 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.%rdi
" - Dies ist seltsam formuliert. Sie meinen, dass der Wert im Registerrdi
verwendet werden sollte. Ihre Verwendung von "at" scheint eine Gedächtnisstörung zu bedeuten, bei der es keine gibt.%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.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 ..
EAX = 8
ursprünglich wäre es:
quelle
lea
ist 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.Wie in den anderen Antworten angegeben:
MOV
greift auf die Daten an der Adresse in den Klammern zu und platziert diese Daten im Zieloperanden.LEA
fü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 vonLEA
ist in der Berechnung der "effektiven Adresse".Da der Speicher auf verschiedene Arten adressiert werden kann (siehe Beispiele unten),
LEA
wird er manchmal verwendet, um Register zu addieren oder zu multiplizieren, ohne eine explizite AnweisungADD
oder eineMUL
Anweisung (oder eine äquivalente Anweisung) zu verwenden.Da alle Beispiele in der Intel-Syntax zeigen, sind hier einige in der AT & T-Syntax:
quelle
lea label, %eax
einen absoluten[disp32]
Adressierungsmodus. Verwenden Siemov $label, %eax
stattdessen. 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.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) ).
quelle
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.
quelle
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.
quelle
lea [label
ist es eine sinnlose Verschwendung von Bytes im Vergleich zu einer kompakterenmov
. 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.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.
quelle