Яка різниця між MOV та LEA?


138

Я хотів би знати, в чому різниця між цими інструкціями:

MOV AX, [TABLE-ADDR]

і

LEA AX, [TABLE-ADDR]


8
дякую Ніку. Перш за все, я не знайшов би відповіді на це запитання, переглянувши це посилання. Тут я шукав конкретну інформацію, обговорення в наданому вами посиланні носить більш загальний характер.
naveen

3
Я підтримав копію @ Ніка років тому, але vtc'd просто зараз. Поміркувавши, я був надто поспішним, і тепер з навієним, що а) інше питання не відповідає "в чому різниця" і б) це корисне питання. Вибачте, що навіен за свою помилку - якби тільки я міг скасувати vtc ...
Рубен Бартелінк,


Пов'язане: Використання LEA для значень, які не є адресами / покажчиками? розповідає про інші способи використання LEA для довільної математики.
Пітер Кордес

Відповіді:


168
  • LEA означає Завантажити ефективну адресу
  • MOV означає Значення навантаження

Коротше кажучи, LEAзавантажує вказівник на елемент, до якого ви звертаєтесь, тоді як MOV завантажує фактичне значення за цією адресою.

Мета LEA- дозволити виконувати нетривіальний обчислення адреси та зберігати результат [для подальшого використання]

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

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

Там, де беруть участь лише константи, MOV(через постійні розрахунки асемблера) іноді може виявитися, що вони перетинаються з найпростішими випадками використання LEA. Його корисно, якщо у вас є багатоскладовий розрахунок з кількома базовими адресами тощо.


6
+1 дякую за чітке пояснення, допомогло мені відповісти на інше запитання.
legends2k

Мене бентежить те, що у лейя "завантажується" ім'я, і ​​люди кажуть, що "завантажує" обчислену адресу в реєстр, тому що всі входи для обчислення місця пам'яті є або безпосередніми значеннями, або регістрами. AFAICT lea виконує лише обчислення, він нічого не завантажує, де завантаження означає торкання пам'яті?
Джозеф Гарвін

2
@josephGarvin IIRC, термін отримання буде застосовано до цього аспекту; Завантаження - це те, як ви замінюєте значення в регістрі чимось з нуля. Наприклад LAHF: Завантажте ФЛАГИ в регістр AH . У CIL CLR (яка є абстрактною машиною, що базується на стеках вищого рівня, термін " навантаження" означає ставлення значення на умовний стек і зазвичай l..., а s... еквівалент робить зворотним). Ці примітки: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) дозволяють припустити, що дійсно існують архітектури, де застосовується ваше розрізнення.
Рубен Бартелінк,


45

У синтаксисі NASM:

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

У синтаксисі MASM використовуйте OFFSET varдля отримання негайного mov замість навантаження.


3
тільки в синтаксисі NASM У синтаксисі MASM mov eax, var- це навантаження, те саме mov eax, [var], що вам потрібно використовувати, mov eax, OFFSET varщоб використовувати мітку як безпосередню константу.
Пітер Кордес

1
Чітко, просто і демонструє те, що я намагався підтвердити. Дякую.
JayArby

1
Зауважте, що у всіх цих прикладах leaє гіршим вибором, за винятком 64-бітного режиму для RIP-відносної адреси. mov r32, imm32працює на більшості портів. lea eax, [edx*4]це копіювання та зсув, який неможливо зробити в одній інструкції інакше, але в тому ж регістрі LEA просто займає більше байтів, щоб кодувати, тому що [eax*4]потрібно a disp32=0. (Він працює на різних портах , ніж зрушення, хоча.) Див agner.org/optimize і stackoverflow.com/tags/x86/info .
Пітер Кордес

29

Інструкція MOV reg, addr означає зчитування змінної, що зберігається за адресою addr, в регістр reg. Інструкція LEA reg, addr означає зчитувати адресу (а не змінну, що зберігається за адресою) в регістр reg.

Іншою формою інструкції MOV є MOV reg, імпульсні дані, що означає зчитування негайних даних (тобто постійних) імпдатів в регістр reg. Зауважте, що якщо addr в LEA reg, addr є просто константою (тобто фіксованим зміщенням), то ця інструкція LEA по суті точно така ж, як і еквівалентний MOV reg, інструкція imdata, що завантажує ту ж константу, що і безпосередні дані.


11

Якщо вказати лише буквальне, різниці немає. Однак LEA має більше здібностей, і про них ви можете прочитати тут:

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


Я думаю, за винятком того, що в асемблері GNU це неправда, коли мова йде про мітки в сегменті .bss? ПОПЕРЕДЖЕННЯ ти насправді не можеш, leal TextLabel, LabelFromBssSegmentколи у тебе є щось. як .bss .lcomm LabelFromBssSegment, 4, тобі доведеться movl $TextLabel, LabelFromBssSegment, чи не так?
JSmyth

@JSmyth: Це лише тому, що leaвимагає призначення регістру, але movможе мати imm32джерело та призначення пам'яті. Це обмеження, звичайно, не стосується асемблера GNU.
Пітер Кордес

1
Крім того, ця відповідь в основному неправильна, тому що питання, про MOV AX, [TABLE-ADDR]яке навантаження, є. Тож є основна різниця. Еквівалентна інструкціяmov ax, OFFSET table_addr
Пітер Кордес

10

Це залежить від використовуваного асемблера, оскільки

mov ax,table_addr

в MASM працює як

mov ax,word ptr[table_addr]

Таким чином, він завантажує перші байти з, table_addrа НЕ зсув до table_addr. Ви повинні використовувати замість цього

mov ax,offset table_addr

або

lea ax,table_addr

яка працює так само.

leaверсія також добре працює, якщо table_addrє локальною змінною, наприклад

some_procedure proc

local table_addr[64]:word

lea ax,table_addr

велике спасибі, це просто те, що я не можу відзначити більше ніж один як відповідь :(
naveen

5
Різниця між інструкціями x86 MOV та LEA, безумовно, НЕ залежить від асемблера.
IJ Kennedy

4

Жодна з попередніх відповідей зовсім не дійшла до моєї власної плутанини, тому я хотів би додати свою власну.

Чого я бракував, це те, що leaоперації стосуються використання дужок різними способами, ніж як mov.

Подумайте C. Скажімо, у мене є масив, longякий я називаю array. Тепер вираз array[i]виконує розвідку, завантажуючи значення з пам'яті за адресоюarray + i * sizeof(long) [1].

З іншого боку, врахуйте вираз &array[i]. Це все ще містить підвираз array[i], але не проводиться перенаправлення! Сенс array[i]змінився. Це більше не означає виконувати затримку, а натомість виступає як своєрідна специфікація , вказуючи, &яку адресу пам'яті ми шукаємо. Якщо вам подобається, ви можете подумати про те& , що "скасовує" відвантаження.

Оскільки два випадки використання багато в чому схожі, вони поділяють синтаксис array[i], але існування або відсутність &змін змінюється в інтерпретації цього синтаксису. Без &цього - це дереференція і насправді читається з масиву. З &, це не так. Значенняarray + i * sizeof(long) все ще обчислюється, але воно не відмежовується.

Ситуація дуже схожа з movі lea. З mov, відбувається дереференція, якої не відбувається lea. Це незважаючи на використання дужок, що трапляються в обох. Наприклад, movq (%r8), %r9і leaq (%r8), %r9. З mov, ці круглі дужки означають "повалення"; з lea, вони не роблять. Це схоже на те, як array[i]означає лише «повагу», коли її немає& .

Приклад - порядок.

Розглянемо код

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

Це завантажує значення в %rdi + %rsi * 8регістр пам'яті в регістр %rbp. Тобто: отримайте значення в регістрі %rdiта значення в регістрі %rsi. Помножте останнє на 8, а потім додайте його до першого. Знайдіть значення в цьому місці та помістіть його до реєстру %rbp.

Цей код відповідає рядку С x = array[i];, де arrayстає %rdiі iстає, %rsiі xстає %rbp. 8Довжина типу даних , що містяться в масиві.

Тепер розглянемо подібний код, який використовує lea:

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

Так само, як використання movqвідповідного перенаправлення, використання leaqтут відповідає не перенаправлення. Ця лінія збірки відповідає лінії C x = &array[i];. Нагадаємо, що &змінює сенс array[i]від перенаправлення до просто вказання місця. Так само вживання leaqзмінює значення змісту(%rdi, %rsi, 8) від перенаправлення до визначення місця.

Семантика цього рядка коду така: отримати значення в регістрі %rdiта значення в регістрі %rsi. Помножте останнє на 8, а потім додайте його до першого. Помістіть це значення в реєстр%rbp . Жодне навантаження з пам'яті не бере участь, а лише арифметичні операції [2].

Зауважте, що єдина відмінність між моїми описами leaqта movqполягає в тому, що movqвона має відношення, а leaqні. Насправді, щоб написати leaqопис, я в основному копіюю + вставляю описmovq , а потім видаляв "Знайти значення в цьому місці".

Підводячи підсумок: movqпроти досить leaqскладно, оскільки вони ставляться до використання дужок як по (%rsi)- (%rdi, %rsi, 8)різному. У movq(і всі інші інструкції, крім lea) ці дужки позначають справжню повагу, тоді як у leaqних немає і є суто зручним синтаксисом.


[1] Я говорив, що коли arrayце масив long, вираз array[i]завантажує значення з адреси array + i * sizeof(long). Це правда, але є тонкощі, на які слід звернути увагу. Якщо я напишу код С

long x = array[5];

це не те саме, що вводити текст

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

Здається, що так і повинно базуватися на моїх попередніх твердженнях, але це не так.

Що відбувається, це те, що додаток на вказівник C має на цьому хитрість. Скажіть, у мене є вказівник, який pвказує на значення типу T. Вираз p + iже НЕ середнє «положення в pплюс iбайт». Натомість вираз p + i насправді означає "позиція в pплюсіi * sizeof(T) байтів".

Зручність цього полягає в тому, що для отримання "наступного значення" нам просто потрібно писати p + 1замістьp + 1 * sizeof(T) .

Це означає, що код C long x = array[5];фактично еквівалентний

long x = *(array + 5)

тому що C автоматично Помножте 5на sizeof(long).

Отже, в контексті цього питання StackOverflow, як це все актуально? Це означає, що коли я кажу "адресу array + i * sizeof(long)", я не маю на увазі, що " array + i * sizeof(long)" трактуватись як вираз C. Я роблю множення sizeof(long)самостійно, щоб зробити свою відповідь більш чіткою, але розумію, що завдяки цьому цей вираз не слід читати як C. Так само, як звичайну математику, яка використовує синтаксис C.

[2] Бічна примітка: оскільки все leaце арифметичні операції, його аргументи насправді не повинні посилатися на дійсні адреси. З цієї причини його часто використовують для виконання чистої арифметики на значеннях, які не можуть бути призначені для відміни. Наприклад, ccз -O2оптимізацією перекладає

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

в наступне (нерелевантні рядки видалено):

f:
  leaq (%rdi, %rdi, 4), %rax  # set %rax to %rdi + %rdi * 4
  ret

1
Так, добре пояснення, більш детально, ніж інші відповіді, і так, &оператор C - це хороша аналогія. Можливо, варто зазначити, що LEA - це особливий випадок, тоді як MOV - це як і будь-яка інша інструкція, яка може зайняти пам'ять або зареєструвати операнд. наприклад, add (%rdi), %eaxпросто використовує режим адресації для адресної пам'яті, як MOV. Також пов'язано: Використання LEA для значень, які не є адресами / покажчиками? надалі пояснює це: LEA - це те, як можна використовувати підтримку HW CPU для математики адреси для довільних обчислень.
Пітер Кордес

"отримати значення у %rdi" - Це дивним чином. Ви маєте на увазі, що значення в реєстрі rdi має бути використане. Використання "at", здається, означає затримку пам'яті там, де її немає.
ecm

@PeterCordes Дякую! Я додав, що у відповідь це особливий випадок.
Quelklef

1
@ecm Хороша точка; Я цього не помічав. Я змінив це зараз, дякую! :)
Quelklef

FYI, коротше фразування , що усуває проблему ЄСМ зазначив, включає в себе: «значення з %rdi » або «значення в %rdi ». Ваше "значення в реєстрі %rdi" довге, але добре, і, можливо, може допомогти комусь, хто намагається зрозуміти регістри проти пам'яті.
Пітер Кордес

2

В основному ... "Перемістіться в REG ... після того, як обчислите його ...", здається, це добре і для інших цілей :)

якщо ви просто забудете, що значення є вказівником, ви можете використовувати його для оптимізації коду / мінімізації ... що коли-небудь ..

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

оригінально було б:

MOV EAX, EBX
ADD EAX, ECX
ADD EAX, 5

Так, leaце інструкція "перемикання та додавання", що використовує машинне кодування і синтаксис машинного кодування, оскільки апаратне обладнання вже знає, як розшифрувати ModR / M + SIB + disp0 / 8/32.
Пітер Кордес

1

Як зазначено в інших відповідях:

  • MOVзахопить дані за адресою всередині дужок і помістить ці дані в операнд призначення.
  • LEAвиконає обчислення адреси всередині дужок і помістить обчислену адресу в операнд призначення. Це відбувається без фактичного виходу в пам'ять і отримання даних. Робота виконана LEAв обчисленні "ефективної адреси".

Оскільки пам'ять може бути адресована декількома різними способами (див. Приклади нижче), LEAіноді використовується для додавання або множення регістрів разом, не використовуючи явного ADDчи MULінструкції (або еквівалента).

Оскільки всі показують приклади в синтаксисі Intel, ось деякі з синтаксису AT&T:

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абсолютного [disp32]режиму адресації. Використовуйте mov $label, %eaxзамість цього. Так, це працює, але це менш ефективно (більший машинний код та працює на меншій кількості одиниць виконання). Оскільки ви згадуєте AT&T, використовуючи LEA для значень, які не є адресами / покажчиками? використовує AT&T, і моя відповідь містить деякі інші приклади AT&T.
Пітер Кордес

1

Давайте зрозуміємо це на прикладі.

mov eax, [ebx] і

lea eax, [ebx] Припустимо, значення в ebx дорівнює 0x400000. Тоді mov перейде на адресу 0x400000 і скопіює 4 байти даних, подавши їх до реєстру eax. Тому lea скопіює адресу 0x400000 в eax. Отже, після виконання кожної інструкції значення eax в кожному випадку буде (якщо при пам'яті 0x400000 містити дорівнює 30).

eax = 30 (у випадку mov) eax = 0x400000 (у випадку lea) Для визначення mov скопіюйте дані з rm32 в пункт призначення (mov dest rm32), а lea (ефективна адреса завантаження) скопіює адресу до місця призначення (mov dest rm32 ).


0

LEA (Ефективна адреса завантаження) - це інструкція, що змінює та додає. Він був доданий до 8086, тому що є апаратне обладнання для декодування та обчислення режимів адреси.


0

MOV може робити те саме, що і LEA [label], але інструкція MOV містить ефективну адресу всередині самої інструкції як безпосередню константу (заздалегідь обчислюється асемблером). LEA використовує відношення ПК для обчислення ефективної адреси під час виконання інструкції.


Це справедливо лише для 64-бітного режиму (де відносна ПК-адресація була новою); в інших режимах lea [label- безглуздо марно байти порівняно з більш компактним mov, тому вам слід вказати умови, про які ви говорите. Крім того, для деяких асемблерів [label]не є правильним синтаксисом для режиму адресації, пов'язаного з RIP. Але так, це точно. Як завантажити адресу функції або мітки в реєстр в GNU Assembler пояснюється більш докладно.
Пітер Кордес

-1

Різниця тонка, але важлива. Інструкція MOV - це фактично "MOVe" - копія адреси, якою позначається мітка TABLE-ADDR. Інструкція LEA - це "Ефективна адреса завантаження", яка є непрямою інструкцією, що означає, що TABLE-ADDR вказує на місце пам'яті, в якому знайдена адреса для завантаження.

Ефективне використання LEA еквівалентно використанню покажчиків на таких мовах, як C, як така - це потужна інструкція.


7
Я думаю, що ця відповідь у кращому випадку бентежить. "Інструкція LEA - це" Ефективна адреса завантаження ", яка є непрямою інструкцією. Це означає, що TABLE-ADDR вказує на місце пам'яті, в якому знайдена адреса для завантаження." Насправді LEA завантажить адресу, а не зміст адреси. Я думаю, що насправді запитувача потрібно запевнити в тому, що MOV та LEA за певних обставин можуть перекриватись і робити саме те саме
Білл Форстер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.