assembly - adresse - lea assembleur



Quel est le but de l'instruction LEA? (10)

Comme d'autres l'ont souligné, LEA (load effective address) est souvent utilisé comme un «truc» pour faire certains calculs, mais ce n'est pas son objectif principal. L'ensemble d'instructions x86 a été conçu pour prendre en charge des langages de haut niveau tels que Pascal et C, où les tableaux, en particulier les tableaux d'entiers ou de petites structures, sont courants. Considérons, par exemple, une structure représentant les coordonnées (x, y):

struct Point
{
     int xcoord;
     int ycoord;
};

Maintenant, imaginez une déclaration comme:

int y = points[i].ycoord;

points[] est un tableau de Point . En supposant que la base du tableau est déjà dans EBX , et que la variable i est dans EAX , et que xcoord et ycoord ont chacun 32 bits (donc ycoord est à offset 4 octets dans la structure), cette instruction peut être compilée pour:

MOV EDX, [EBX + 8*EAX + 4]    ; right side is "effective address"

qui atterrira dans EDX . Le facteur d'échelle de 8 est parce que chaque Point taille de 8 octets. Considérons maintenant la même expression utilisée avec l'opérateur "address of" &:

int *p = &points[i].ycoord;

Dans ce cas, vous ne voulez pas la valeur de ycoord , mais son adresse. C'est là LEA (load effective address). Au lieu d'un MOV , le compilateur peut générer

LEA ESI, [EBX + 8*EAX + 4]

qui va charger l'adresse dans ESI .

https://src-bin.com

Pour moi, il semble juste comme un MOV funky. Quel est son but et quand devrais-je l'utiliser?


Answer #1

Comme les réponses existantes mentionnées, LEA a l'avantage d'effectuer de l'arithmétique d'adressage mémoire sans accéder à la mémoire, en sauvegardant le résultat arithmétique dans un registre différent au lieu de la simple forme d'ajout d'instruction. Le réel avantage sous-jacent des performances est que le processeur moderne dispose d'une unité LEA ALU et d'un port pour la génération d'adresses (y compris LEA et autres adresses de référence mémoire), ce qui signifie que l'opération arithmétique dans LEA et d'autres opérations arithmétiques normales peuvent être effectuées en parallèle. dans un noyau.

Consultez cet article de l'architecture Haswell pour plus de détails sur l'unité LEA: http://www.realworldtech.com/haswell-cpu/4/

Un autre point important qui n'est pas mentionné dans les autres réponses est LEA REG, [MemoryAddress] instruction LEA REG, [MemoryAddress] est PIC (code indépendant de la position) qui code l'adresse relative du PC dans cette instruction pour référencer MemoryAddress . Ceci est différent de MOV REG, MemoryAddress qui code l'adresse virtuelle relative et nécessite de relocaliser / correctif dans les systèmes d'exploitation modernes (comme ASLR est une caractéristique commune). Donc LEA peut être utilisé pour convertir un non PIC en PIC.


Answer #2

L'instruction LEA (Load Effective Address) est un moyen d'obtenir l'adresse qui provient de n'importe lequel des modes d'adressage mémoire du processeur Intel.

C'est-à-dire, si nous avons un mouvement de données comme ceci:

MOV EAX, <MEM-OPERAND>

il déplace le contenu de l'emplacement de mémoire désigné dans le registre cible.

Si nous remplaçons le MOV par LEA , l'adresse de l'emplacement mémoire est calculée exactement de la même manière par l'expression d'adressage <MEM-OPERAND> . Mais au lieu du contenu de l'emplacement de la mémoire, nous obtenons l'emplacement lui-même dans la destination.

LEA n'est pas une instruction arithmétique spécifique; c'est une manière d'intercepter l'adresse effective issue de l'un quelconque des modes d'adressage mémoire du processeur.

Par exemple, nous pouvons utiliser LEA sur une simple adresse directe. Aucune arithmétique n'est impliquée du tout:

MOV EAX, GLOBALVAR   ; fetch the value of GLOBALVAR into EAX
LEA EAX, GLOBALVAR   ; fetch the address of GLOBALVAR into EAX.

Ceci est valide nous pouvons le tester à l'invite de Linux:

$ as
LEA 0, %eax
$ objdump -d a.out

a.out:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
   0:   8d 04 25 00 00 00 00    lea    0x0,%eax

Ici, il n'y a pas d'ajout d'une valeur mise à l'échelle, et pas de décalage. Zero est déplacé dans EAX. Nous pourrions le faire en utilisant MOV avec un opérande immédiat aussi.

C'est la raison pour laquelle les gens qui pensent que les parenthèses dans LEA sont superflues se trompent gravement; les crochets ne sont pas une syntaxe LEA mais font partie du mode d'adressage.

LEA est réel au niveau du matériel. L'instruction générée code le mode d'adressage réel et le processeur l'exécute jusqu'au point de calcul de l'adresse. Ensuite, il déplace cette adresse vers la destination au lieu de générer une référence de mémoire. (Étant donné que le calcul d'adresse d'un mode d'adressage dans une autre instruction n'a aucun effet sur les indicateurs de CPU, le LEA n'a aucun effet sur les indicateurs de CPU.)

Contraste avec le chargement de la valeur de l'adresse zéro:

$ as
movl 0, %eax
$ objdump -d a.out | grep mov
   0:   8b 04 25 00 00 00 00    mov    0x0,%eax

C'est un encodage très similaire, voyez? Juste le 8d de LEA a changé en 8b .

Bien sûr, ce codage LEA est plus long que de déplacer un zéro immédiat dans EAX :

$ as
movl $0, %eax
$ objdump -d a.out | grep mov
   0:   b8 00 00 00 00          mov    $0x0,%eax

Il n'y a aucune raison pour que LEA exclue cette possibilité, simplement parce qu'il existe une alternative plus courte; c'est juste une combinaison orthogonale avec les modes d'adressage disponibles.


Answer #3

L'instruction LEA peut être utilisée pour éviter des calculs fastidieux d'adresses effectives par la CPU. Si une adresse est utilisée à plusieurs reprises, il est plus efficace de la stocker dans un registre au lieu de calculer l'adresse effective chaque fois qu'elle est utilisée.


Answer #4

La principale raison pour laquelle vous utilisez LEA sur un MOV est si vous devez effectuer une opération arithmétique sur les registres que vous utilisez pour calculer l'adresse. Effectivement, vous pouvez effectuer ce qui équivaut à arithmétique pointeur sur plusieurs des registres en combinaison efficace pour "libre".

Ce qui est vraiment déroutant, c'est que vous écrivez typiquement un LEA comme un MOV mais vous ne dérérez pas la mémoire. En d'autres termes:

MOV EAX, [ESP+4]

Cela va déplacer le contenu de ce que ESP+4 points dans EAX .

LEA EAX, [EBX*8]

Cela déplacera l'adresse effective EBX * 8 dans EAX, pas ce qui se trouve dans cet emplacement. Comme vous pouvez le voir, il est également possible de multiplier par des facteurs de deux (scaling) alors qu'un MOV est limité à l'addition / soustraction.


Answer #5

Le 8086 a une grande famille d'instructions qui accepte un opérande de registre et une adresse effective, effectue des calculs pour calculer la partie de décalage de cette adresse effective, et effectue une opération impliquant le registre et la mémoire référencés par l'adresse calculée. Il était assez simple d'avoir l'une des instructions de cette famille se comporter comme ci-dessus, sauf pour sauter cette opération de mémoire réelle. Ceci, les instructions:

mov ax,[bx+si+5]
lea ax,[bx+si+5]

ont été mis en œuvre presque à l'identique en interne. La différence est une étape sautée. Les deux instructions fonctionnent quelque chose comme:

temp = fetched immediate operand (5)
temp += bx
temp += si
address_out = temp  (skipped for LEA)
trigger 16-bit read  (skipped for LEA)
temp = data_in  (skipped for LEA)
ax = temp

Quant à savoir pourquoi Intel pensait que cette instruction valait la peine d'être comprise, je ne suis pas tout à fait sûr, mais le fait que ce soit peu coûteux à implémenter aurait été un facteur important. Un autre facteur aurait été le fait que l'assembleur d'Intel permettait de définir des symboles relatifs au registre BP. Si fnord était défini comme un symbole relatif à BP (par exemple BP + 8), on pourrait dire:

mov ax,fnord  ; Equivalent to "mov ax,[BP+8]"

Si l'on voulait utiliser quelque chose comme stosw pour stocker des données à une adresse relative BP, être capable de dire

mov ax,0 ; Data to store
mov cx,16 ; Number of words
lea di,fnord
rep movs fnord  ; Address is ignored EXCEPT to note that it's an SS-relative word ptr

était plus pratique que:

mov ax,0 ; Data to store
mov cx,16 ; Number of words
mov di,bp
add di,offset fnord (i.e. 8)
rep movs fnord  ; Address is ignored EXCEPT to note that it's an SS-relative word ptr

Notez qu'oublier le "décalage" du monde entraînerait l'ajout du contenu de l'emplacement [BP + 8], plutôt que la valeur 8, à DI. Oops.


Answer #6

Peut-être juste une autre chose à propos de l'instruction LEA. Vous pouvez également utiliser LEA pour des registres multiplicateurs rapides de 3, 5 ou 9.

LEA EAX, [EAX * 2 + EAX]   ;EAX = EAX * 3
LEA EAX, [EAX * 4 + EAX]   ;EAX = EAX * 5
LEA EAX, [EAX * 8 + EAX]   ;EAX = EAX * 9

Answer #7

Une autre caractéristique importante de l'instruction LEA est qu'elle ne modifie pas les codes de condition tels que CF et ZF , tout en calculant l'adresse par des instructions arithmétiques comme ADD ou MUL . Cette fonctionnalité diminue le niveau de dépendance entre les instructions et laisse donc la place à une optimisation supplémentaire par le compilateur ou le planificateur matériel.


Answer #8

parce qu'à la place vous écrivez le code

mov dx,offset something

vous pouvez simplement écrire

lea dx,something

Answer #9

lea est l'abréviation de "load effective address". Il charge l'adresse de la référence de localisation par l'opérande source à l'opérande de destination. Par exemple, vous pouvez l'utiliser pour:

lea ebx, [ebx+eax*8]

pour déplacer plus loin les éléments eax pointeur ebx (dans un tableau de 64 bits / éléments) avec une seule instruction. Fondamentalement, vous bénéficiez de modes d'adressage complexes pris en charge par l'architecture x86 pour manipuler efficacement les pointeurs.





x86-16