Я хотів би знати, в чому різниця між цими інструкціями:
MOV AX, [TABLE-ADDR]
і
LEA AX, [TABLE-ADDR]
Я хотів би знати, в чому різниця між цими інструкціями:
MOV AX, [TABLE-ADDR]
і
LEA AX, [TABLE-ADDR]
Відповіді:
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
. Його корисно, якщо у вас є багатоскладовий розрахунок з кількома базовими адресами тощо.
LAHF
: Завантажте ФЛАГИ в регістр AH . У CIL CLR (яка є абстрактною машиною, що базується на стеках вищого рівня, термін " навантаження" означає ставлення значення на умовний стек і зазвичай l
..., а s
... еквівалент робить зворотним). Ці примітки: cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/load.html ) дозволяють припустити, що дійсно існують архітектури, де застосовується ваше розрізнення.
У синтаксисі 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 замість навантаження.
mov eax, var
- це навантаження, те саме mov eax, [var]
, що вам потрібно використовувати, mov eax, OFFSET var
щоб використовувати мітку як безпосередню константу.
lea
є гіршим вибором, за винятком 64-бітного режиму для RIP-відносної адреси. mov r32, imm32
працює на більшості портів. lea eax, [edx*4]
це копіювання та зсув, який неможливо зробити в одній інструкції інакше, але в тому ж регістрі LEA просто займає більше байтів, щоб кодувати, тому що [eax*4]
потрібно a disp32=0
. (Він працює на різних портах , ніж зрушення, хоча.) Див agner.org/optimize і stackoverflow.com/tags/x86/info .
Інструкція MOV reg, addr означає зчитування змінної, що зберігається за адресою addr, в регістр reg. Інструкція LEA reg, addr означає зчитувати адресу (а не змінну, що зберігається за адресою) в регістр reg.
Іншою формою інструкції MOV є MOV reg, імпульсні дані, що означає зчитування негайних даних (тобто постійних) імпдатів в регістр reg. Зауважте, що якщо addr в LEA reg, addr є просто константою (тобто фіксованим зміщенням), то ця інструкція LEA по суті точно така ж, як і еквівалентний MOV reg, інструкція imdata, що завантажує ту ж константу, що і безпосередні дані.
Якщо вказати лише буквальне, різниці немає. Однак LEA має більше здібностей, і про них ви можете прочитати тут:
http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-1.html#HEADING1-136
leal TextLabel, LabelFromBssSegment
коли у тебе є щось. як .bss .lcomm LabelFromBssSegment, 4
, тобі доведеться movl $TextLabel, LabelFromBssSegment
, чи не так?
lea
вимагає призначення регістру, але mov
може мати imm32
джерело та призначення пам'яті. Це обмеження, звичайно, не стосується асемблера GNU.
MOV AX, [TABLE-ADDR]
яке навантаження, є. Тож є основна різниця. Еквівалентна інструкціяmov ax, OFFSET table_addr
Це залежить від використовуваного асемблера, оскільки
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
Жодна з попередніх відповідей зовсім не дійшла до моєї власної плутанини, тому я хотів би додати свою власну.
Чого я бракував, це те, що 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
&
оператор C - це хороша аналогія. Можливо, варто зазначити, що LEA - це особливий випадок, тоді як MOV - це як і будь-яка інша інструкція, яка може зайняти пам'ять або зареєструвати операнд. наприклад, add (%rdi), %eax
просто використовує режим адресації для адресної пам'яті, як MOV. Також пов'язано: Використання LEA для значень, які не є адресами / покажчиками? надалі пояснює це: LEA - це те, як можна використовувати підтримку HW CPU для математики адреси для довільних обчислень.
%rdi
" - Це дивним чином. Ви маєте на увазі, що значення в реєстрі rdi
має бути використане. Використання "at", здається, означає затримку пам'яті там, де її немає.
%rdi
» або «значення в %rdi
». Ваше "значення в реєстрі %rdi
" довге, але добре, і, можливо, може допомогти комусь, хто намагається зрозуміти регістри проти пам'яті.
В основному ... "Перемістіться в 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.
Як зазначено в інших відповідях:
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.
Давайте зрозуміємо це на прикладі.
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 ).
MOV може робити те саме, що і LEA [label], але інструкція MOV містить ефективну адресу всередині самої інструкції як безпосередню константу (заздалегідь обчислюється асемблером). LEA використовує відношення ПК для обчислення ефективної адреси під час виконання інструкції.
lea [label
- безглуздо марно байти порівняно з більш компактним mov
, тому вам слід вказати умови, про які ви говорите. Крім того, для деяких асемблерів [label]
не є правильним синтаксисом для режиму адресації, пов'язаного з RIP. Але так, це точно. Як завантажити адресу функції або мітки в реєстр в GNU Assembler пояснюється більш докладно.
Різниця тонка, але важлива. Інструкція MOV - це фактично "MOVe" - копія адреси, якою позначається мітка TABLE-ADDR. Інструкція LEA - це "Ефективна адреса завантаження", яка є непрямою інструкцією, що означає, що TABLE-ADDR вказує на місце пам'яті, в якому знайдена адреса для завантаження.
Ефективне використання LEA еквівалентно використанню покажчиків на таких мовах, як C, як така - це потужна інструкція.