Ich würde gerne wissen, was der Unterschied zwischen diesen Anweisungen ist:
MOV AX, [TABLE-ADDR]
und
LEA AX, [TABLE-ADDR]
Ich würde gerne wissen, was der Unterschied zwischen diesen Anweisungen ist:
MOV AX, [TABLE-ADDR]
und
LEA AX, [TABLE-ADDR]
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.
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.
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.
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 var
in der MASM-Syntax, um anstelle eines Ladevorgangs einen Mov-Instant abzurufen.
mov eax, var
ist 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.
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 ist disp32=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.
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
leal TextLabel, LabelFromBssSegment
wenn Sie etw bekommen. wie .bss .lcomm LabelFromBssSegment, 4
, müssten Sie movl $TextLabel, LabelFromBssSegment
, nicht wahr?
lea
dass ein Registerziel erforderlich ist, aber mov
eine imm32
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
mov ax,table_addr
in MASM funktioniert als
mov ax,word ptr[table_addr]
Es werden also die ersten Bytes von table_addr
und NICHT der Offset von geladen table_addr
. Sie sollten stattdessen verwenden
mov ax,offset table_addr
oder
lea ax,table_addr
das funktioniert genauso.
lea
Version funktioniert auch gut, wenn table_addr
es sich um eine lokale Variable handelt, z
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
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 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 mov
und sehr ähnlich lea
. Mit mov
tritt 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), %r9
und leaq (%r8), %r9
. Mit mov
bedeuten diese Klammern "Dereferenzierung"; mit lea
tun 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 * 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 der array
wird %rdi
und i
wird %rsi
und x
wird %rbp
. Dies 8
ist 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 movq
der Dereferenzierung entsprach, entspricht die Verwendung von leaq
hier 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 %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 darin movq
besteht, dass movq
eine Dereferenzierung durchgeführt wird und leaq
nicht. Um die leaq
Beschreibung zu schreiben , habe ich die Beschreibung von kopiert + eingefügt movq
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. In movq
(und allen anderen Anweisungen außer lea
) bezeichnen diese Klammern eine echte Dereferenzierung, während leaq
sie in und keine rein bequeme Syntax sind.
[1] Ich habe gesagt, wenn array
es 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 + i
bedeutet nicht "die Position bei p
Plus- i
Bytes". Stattdessen bedeutet der Ausdruck p + i
tatsächlich "die Position bei p
Plus- i * sizeof(T)
Bytes".
Die Bequemlichkeit ist , dass „der nächste Wert“ erhalten müssen wir nur schreiben p + 1
statt 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 5
durch 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 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 Beispiel cc
mit -O2
Optimierung ü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
&
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), %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 Register rdi
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 ..
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
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 von LEA
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 Anweisung ADD
oder eine MUL
Anweisung (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 */
lea label, %eax
einen absoluten [disp32]
Adressierungsmodus. Verwenden Sie mov $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) ).
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.
lea [label
ist 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.
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.