J'aimerais savoir quelle est la différence entre ces instructions:
MOV AX, [TABLE-ADDR]
et
LEA AX, [TABLE-ADDR]
assembly
x86
instruction-set
Naveen
la source
la source
Réponses:
LEA
signifie charger l'adresse effectiveMOV
signifie valeur de chargeEn bref,
LEA
charge un pointeur vers l'élément que vous adressez tandis que MOV charge la valeur réelle à cette adresse.Le but de
LEA
est de permettre d'effectuer un calcul d'adresse non trivial et de stocker le résultat [pour une utilisation ultérieure]Là où il n'y a que des constantes impliquées,
MOV
(grâce aux calculs de constantes de l'assembleur) peuvent parfois sembler se chevaucher avec les cas les plus simples d'utilisation deLEA
. C'est utile si vous avez un calcul en plusieurs parties avec plusieurs adresses de base, etc.la source
LAHF
: charger les FLAGS dans le registre AH . Dans le CIL du CLR (qui est une machine abstraite basée sur une pile de plus haut niveau, le terme charge fait référence à la mise d'une valeur sur la pile notionnelle et est normalementl
..., et l's
équivalent ... fait l'inverse). Ces notes: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) suggèrent qu'il existe en effet des architectures où votre distinction s'applique.Dans la syntaxe NASM:
Dans la syntaxe MASM, utilisez
OFFSET var
pour obtenir un mov-immédiat au lieu d'une charge.la source
mov eax, var
est une charge, identique àmov eax, [var]
, et vous devez utilisermov eax, OFFSET var
pour utiliser une étiquette comme constante immédiate.lea
c'est le pire choix, sauf en mode 64 bits pour l'adressage relatif RIP.mov r32, imm32
fonctionne sur plus de ports.lea eax, [edx*4]
est un copier-déplacer qui ne peut pas être fait dans une instruction autrement, mais dans le même registre LEA prend juste plus d'octets à encoder car il[eax*4]
nécessite undisp32=0
. (Il fonctionne sur des ports différents de ceux des shifts, cependant.) Voir agner.org/optimize et stackoverflow.com/tags/x86/info .L'instruction MOV reg, addr signifie lire une variable stockée à l'adresse addr dans le registre reg. L'instruction LEA reg, addr signifie lire l'adresse (et non la variable stockée à l'adresse) dans le registre reg.
Une autre forme de l'instruction MOV est MOV reg, immdata qui signifie lire les données immédiates (c'est-à-dire constantes) immdata dans le registre reg. Notez que si l'adr dans LEA reg, addr est juste une constante (c'est-à-dire un offset fixe) alors cette instruction LEA est essentiellement exactement la même qu'une instruction MOV reg, immdata équivalente qui charge la même constante que les données immédiates.
la source
Si vous ne spécifiez qu'un littéral, il n'y a aucune différence. LEA a plus de capacités, cependant, et vous pouvez en savoir plus ici:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
la source
leal TextLabel, LabelFromBssSegment
quand vous en avez. comme.bss .lcomm LabelFromBssSegment, 4
, tu devrais le fairemovl $TextLabel, LabelFromBssSegment
, n'est-ce pas?lea
nécessite une destination de registre, maismov
peut avoir uneimm32
source et une destination mémoire. Cette limitation n'est bien entendu pas spécifique à l'assembleur GNU.MOV AX, [TABLE-ADDR]
, ce qui est une charge. Il y a donc une différence majeure. L'instruction équivalente estmov ax, OFFSET table_addr
Cela dépend de l'assembleur utilisé, car
dans MASM fonctionne comme
Donc, il charge les premiers octets de
table_addr
et PAS le décalage verstable_addr
. Vous devriez utiliser à la placeou
qui fonctionne de la même manière.
lea
la version fonctionne également très bien sitable_addr
est une variable locale, par exemplela source
Aucune des réponses précédentes n'est allée au fond de ma propre confusion, alors j'aimerais ajouter la mienne.
Ce qui me manquait, c'est que les
lea
opérations traitent l'utilisation des parenthèses différemment de la façon dontmov
.Pensez à C. Disons que j'ai un tableau de
long
ce que j'appellearray
. Maintenant, l'expressionarray[i]
effectue un déréférencement, chargeant la valeur de la mémoire à l'adressearray + i * sizeof(long)
[1].D'un autre côté, considérez l'expression
&array[i]
. Celui-ci contient toujours la sous-expressionarray[i]
, mais aucun déréférencement n'est effectué! Le sens dearray[i]
a changé. Cela ne signifie plus effectuer une déférence mais agit plutôt comme une sorte de spécification , indiquant&
quelle adresse mémoire nous recherchons. Si vous le souhaitez, vous pouvez également penser&
à "annuler" la déréférence.Parce que les deux cas d'utilisation sont similaires à bien des égards, ils partagent la syntaxe
array[i]
, mais l'existence ou l'absence d'un&
change la façon dont cette syntaxe est interprétée. Sans&
, c'est une déréférence et lit réellement à partir du tableau. Avec&
, ce n'est pas le cas. La valeurarray + i * sizeof(long)
est toujours calculée, mais elle n'est pas déréférencée.La situation est très similaire avec
mov
etlea
. Avecmov
, une déréférence se produit qui ne se produit pas aveclea
. Ceci malgré l'utilisation de parenthèses qui se produit dans les deux. Par exemple,movq (%r8), %r9
etleaq (%r8), %r9
. Avecmov
, ces parenthèses signifient "déréférencer"; aveclea
, ils ne le font pas. Ceci est similaire à la façonarray[i]
dont signifie "déréférencer" uniquement lorsqu'il n'y a pas&
.Un exemple s'impose.
Considérez le code
Cela charge la valeur à l'emplacement de mémoire
%rdi + %rsi * 8
dans le registre%rbp
. C'est-à-dire: obtenir la valeur dans le registre%rdi
et la valeur dans le registre%rsi
. Multipliez ce dernier par 8, puis ajoutez-le au premier. Trouvez la valeur à cet endroit et placez-la dans le registre%rbp
.Ce code correspond à la ligne C
x = array[i];
, oùarray
devient%rdi
eti
devient%rsi
etx
devient%rbp
. La8
est la longueur du type de données contenu dans le tableau.Considérons maintenant un code similaire qui utilise
lea
:Tout comme l'utilisation de
movq
correspondait à un déréférencement, l'utilisation deleaq
ici correspond à ne pas déréférencer. Cette ligne d'assemblage correspond à la ligne Cx = &array[i];
. Rappelez-vous que cela&
change la significationarray[i]
du déréférencement à la simple spécification d'un emplacement. De même, l'utilisation deleaq
change la signification de(%rdi, %rsi, 8)
du déréférencement à la spécification d'un emplacement.La sémantique de cette ligne de code est la suivante: obtenir la valeur dans le registre
%rdi
et la valeur dans le registre%rsi
. Multipliez ce dernier par 8, puis ajoutez-le au premier. Placez cette valeur dans le registre%rbp
. Aucune charge mémoire n'est impliquée, juste des opérations arithmétiques [2].Notez que la seule différence entre mes descriptions de
leaq
etmovq
est que celamovq
fait un déréférencement etleaq
non. En fait, pour écrire laleaq
description, j'ai essentiellement copié + collé la description demovq
, puis supprimé "Trouver la valeur à cet emplacement".Pour résumer:
movq
vs.leaq
est délicat car ils traitent l'utilisation des parenthèses, comme dans(%rsi)
et(%rdi, %rsi, 8)
, différemment. Dansmovq
(et toutes les autres instructions à l'exception delea
), ces parenthèses désignent un véritable déréférencement, alors que dansleaq
elles ne le sont pas et sont une syntaxe purement pratique.[1] J'ai dit que quand
array
est un tableau delong
, l'expressionarray[i]
charge la valeur de l'adressearray + i * sizeof(long)
. C'est vrai, mais il y a une subtilité qui doit être abordée. Si j'écris le code Cce n'est pas la même chose que de taper
Il semble que cela devrait être basé sur mes déclarations précédentes, mais ce n'est pas le cas.
Ce qui se passe, c'est que l'ajout de pointeur C a une astuce. Disons que j'ai un pointeur
p
pointant vers des valeurs de typeT
. L'expressionp + i
ne signifie pas "la position àp
plus d'i
octets". Au lieu de cela, l'expression signifiep + i
en fait "la position àp
plus d'i * sizeof(T)
octets".La commodité de ceci est que pour obtenir "la valeur suivante", il suffit d'écrire
p + 1
au lieu dep + 1 * sizeof(T)
.Cela signifie que le code C
long x = array[5];
est en fait équivalent àcar C multipliera automatiquement le
5
parsizeof(long)
.Donc, dans le contexte de cette question StackOverflow, en quoi tout cela est-il pertinent? Cela signifie que lorsque je dis "l'adresse
array + i * sizeof(long)
", je ne veux pas dire que "array + i * sizeof(long)
" doit être interprété comme une expression C. Je fais la multiplication parsizeof(long)
moi-même afin de rendre ma réponse plus explicite, mais je comprends que pour cette raison, cette expression ne doit pas être lue comme C. Tout comme les mathématiques normales qui utilisent la syntaxe C.[2] Note latérale: parce que tout ce que
lea
fait ce sont des opérations arithmétiques, ses arguments n'ont pas à faire référence à des adresses valides. Pour cette raison, il est souvent utilisé pour effectuer de l'arithmétique pure sur des valeurs qui peuvent ne pas être destinées à être déréférencées. Par exemple,cc
avec l'-O2
optimisation se traduitdans ce qui suit (lignes non pertinentes supprimées):
la source
&
opérateur de C est une bonne analogie. Il vaut peut-être la peine de souligner que LEA est le cas particulier, alors que MOV est comme toute autre instruction qui peut prendre une mémoire ou enregistrer un opérande. par exemple,add (%rdi), %eax
utilise simplement le mode d'adressage pour adresser la mémoire, identique à MOV. Aussi lié: Utiliser LEA sur des valeurs qui ne sont pas des adresses / pointeurs? va plus loin dans cette explication: LEA est la façon dont vous pouvez utiliser la prise en charge matérielle du CPU pour le calcul d'adresse pour effectuer des calculs arbitraires.%rdi
" - Ceci est formulé de façon étrange. Vous voulez dire que la valeur du registrerdi
doit être utilisée. Votre utilisation de «at» semble signifier un déréférencement de mémoire là où il n'y en a pas.%rdi
" ou "la valeur dans%rdi
". Votre «valeur dans le registre%rdi
» est longue mais correcte, et pourrait peut-être aider quelqu'un qui a du mal à comprendre les registres par rapport à la mémoire.Fondamentalement ... "Passez à REG ... après l'avoir calculé ..." cela semble être aussi bien pour d'autres fins :)
si vous oubliez simplement que la valeur est un pointeur, vous pouvez l'utiliser pour les optimisations / minimisation du code ... quoi que ce soit ..
EAX = 8
à l'origine, ce serait:
la source
lea
est une instruction shift-and-add qui utilise le codage et la syntaxe de la machine à opérande mémoire, car le matériel sait déjà décoder ModR / M + SIB + disp0 / 8/32.Comme indiqué dans les autres réponses:
MOV
va saisir les données à l'adresse entre crochets et placer ces données dans l'opérande de destination.LEA
effectuera le calcul de l'adresse entre crochets et placera cette adresse calculée dans l'opérande de destination. Cela se produit sans réellement sortir de la mémoire et obtenir les données. Le travail effectué parLEA
est dans le calcul de "l'adresse effective".Parce que la mémoire peut être adressée de plusieurs manières différentes (voir les exemples ci-dessous),
LEA
est parfois utilisée pour ajouter ou multiplier des registres ensemble sans utiliser une instruction expliciteADD
ouMUL
(ou équivalent).Puisque tout le monde montre des exemples dans la syntaxe Intel, en voici quelques-uns dans la syntaxe AT&T:
la source
lea label, %eax
d'un[disp32]
mode d'adressage absolu . Utilisezmov $label, %eax
plutôt. Oui, cela fonctionne, mais c'est moins efficace (code machine plus gros et fonctionne sur moins d'unités d'exécution). Depuis que vous mentionnez AT&T, utiliser LEA sur des valeurs qui ne sont pas des adresses / pointeurs? utilise AT&T, et ma réponse a quelques autres exemples AT&T.Permet de comprendre cela avec un exemple.
mov eax, [ebx] et
lea eax, [ebx] Supposons que la valeur dans ebx soit 0x400000. Ensuite, mov ira à l'adresse 0x400000 et copiera 4 octets de données dans leur registre eax, tandis que lea copiera l'adresse 0x400000 dans eax. Ainsi, après l'exécution de chaque instruction, la valeur de eax sera dans chaque cas (en supposant que la mémoire 0x400000 contient 30).
eax = 30 (dans le cas de mov) eax = 0x400000 (dans le cas de lea) Pour la définition mov copiez les données de rm32 vers la destination (mov dest rm32) et lea (load effective address) copiera l'adresse vers la destination (mov dest rm32 ).
la source
LEA (Load Effective Address) est une instruction shift-and-add. Il a été ajouté à 8086 car le matériel est là pour décoder et calculer les modes d'adressage.
la source
MOV peut faire la même chose que LEA [étiquette], mais l'instruction MOV contient l'adresse effective à l'intérieur de l'instruction elle-même sous forme de constante immédiate (calculée à l'avance par l'assembleur). LEA utilise PC-relative pour calculer l'adresse effective pendant l'exécution de l'instruction.
la source
lea [label
est un gaspillage inutile d'octets par rapport à un plus compactmov
, vous devez donc spécifier les conditions dont vous parlez. En outre, pour certains assembleurs,[label]
la syntaxe n'est pas appropriée pour un mode d'adressage relatif à RIP. Mais oui, c'est exact. Comment charger l'adresse de la fonction ou de l'étiquette dans le registre dans GNU Assembler explique plus en détail.La différence est subtile mais importante. L'instruction MOV est un «MOVe» en fait une copie de l'adresse que représente l'étiquette TABLE-ADDR. L'instruction LEA est une 'adresse effective de chargement' qui est une instruction indirecte, ce qui signifie que TABLE-ADDR pointe vers un emplacement mémoire où l'adresse à charger est trouvée.
Utiliser efficacement LEA équivaut à utiliser des pointeurs dans des langages tels que C, en tant que tel, c'est une instruction puissante.
la source