Я хотел бы знать, в чем разница между этими инструкциями:
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 (который представляет собой абстрактную машину на основе стека более высокого уровня, термин load относится к помещению значения в условный стек и обычно равен _2 _..., а эквивалент _3 _... обратное). Эти примечания: cs.umd.edu/class/sum2003 /cmsc311/Notes/Mips/load.html) свидетельствует о том, что действительно существуют архитектуры, в которых применяется ваше различие.
- person Ruben Bartelink; 25.06.2017
В синтаксисе 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 eax, var
- это загрузка, то же самое, что и mov eax, [var]
, и вы должны использовать mov eax, OFFSET var
, чтобы использовать метку как непосредственную константу.
- person Peter Cordes; 27.05.2016
lea
- худший выбор, за исключением 64-битного режима для относительной адресации RIP. mov r32, imm32
работает на большем количестве портов. lea eax, [edx*4]
- это копирование и сдвиг, которое не может быть выполнено в одной инструкции иначе, но в том же регистре LEA просто требуется больше байтов для кодирования, потому что [eax*4]
требует disp32=0
. (Однако он работает на разных портах, а не на сменах.) См. agner.org/optimize и stackoverflow.com/tags/x86/info.
- person Peter Cordes; 16.04.2018
Инструкция MOV reg, addr означает чтение переменной, хранящейся по адресу addr, в регистр reg. Инструкция LEA reg, addr означает чтение адреса (а не переменной, хранящейся по адресу) в регистр reg.
Другой формой инструкции MOV является MOV reg, immdata, что означает чтение немедленных данных (т.е. константы) immdata в регистр reg. Обратите внимание, что если addr в LEA reg, addr является просто константой (то есть фиксированным смещением), то эта инструкция LEA по существу точно такая же, как эквивалентная инструкция MOV reg, immdata, которая загружает ту же константу, что и непосредственные данные.
Ни один из предыдущих ответов не разобрал мою путаницу, поэтому я хотел бы добавить свой собственный.
Мне не хватало того, что 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
.
Этот код соответствует строке C 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)
. Это правда, но есть одна тонкость, которую следует решить. Если я напишу код C
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
&
- хорошая аналогия. Возможно, стоит отметить, что LEA - это особый случай, а MOV, как и любая другая инструкция, может принимать операнд памяти или регистр. например add (%rdi), %eax
просто использует режим адресации для адресации памяти, как и MOV. Также по теме: Использование LEA для значений, которые не являются адресами / указателями? продолжает это объяснение: LEA - это то, как вы можете использовать поддержку аппаратного обеспечения процессора для адресной математики для произвольных вычислений.
- person Peter Cordes; 18.02.2020
%rdi
- это странная формулировка. Вы имеете в виду, что следует использовать значение в регистре rdi
. Ваше использование at, похоже, означает разыменование памяти там, где ее нет.
- person ecm; 20.02.2020
%rdi
или значение in %rdi
. Ваше значение в регистре %rdi
длинное, но прекрасное и, возможно, может помочь кому-то, кто пытается понять регистры и память.
- person Peter Cordes; 21.02.2020
array + i * sizeof(long)
в C не &array[i]
. Математика указателя C масштабируется по ширине типа неявно: array[i]
буквально определено в стандарте C как эквивалент *(array + i)
. т.е. array + i
- правильное выражение C. Говоря об asm, вы хотите показать явное масштабирование индекса по ширине типа для получения байтового смещения (если вы не упростите использование char
), но для этого вам, вероятно, следует избегать использования синтаксиса C. В противном случае вы показываете &array[i*sizeof(*array)]
- person Peter Cordes; 21.02.2020
array + i
или array + i * sizeof(long)
. Я решил сделать последнее, поскольку мы находимся в контексте asm и поскольку я никогда не использую выражение array + i
в операторе C. Однако, как вы отметили, это все еще действующий синтаксис C. Добавлю сноску.
- person Quelklef; 21.02.2020
Если вы укажете только литерал, разницы нет. Однако у 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
, не так ли?
- person JSmyth; 18.02.2013
lea
требует адресата регистра, но mov
может иметь imm32
источник и адресат памяти. Это ограничение, конечно, не относится к ассемблеру GNU.
- person Peter Cordes; 28.02.2018
MOV AX, [TABLE-ADDR]
, что является нагрузкой. Так что есть большая разница. Эквивалентная инструкция mov ax, OFFSET table_addr
- person Peter Cordes; 28.02.2018
Это зависит от используемого ассемблера, потому что
mov ax,table_addr
в MASM работает как
mov ax,word ptr[table_addr]
Таким образом, он загружает первые байты из table_addr
, а НЕ смещение до table_addr
. Вместо этого вы должны использовать
mov ax,offset table_addr
or
lea ax,table_addr
который работает так же.
Версия lea
также отлично работает, если table_addr
- локальная переменная, например.
some_procedure proc
local table_addr[64]:word
lea ax,table_addr
Как указано в других ответах:
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.
- person Peter Cordes; 22.11.2019
В основном ... "Переместитесь в 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.
- person Peter Cordes; 28.02.2018
Давайте разберемся с этим на примере.
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 [метка], но инструкция MOV содержит эффективный адрес внутри самой инструкции как непосредственную константу (заранее рассчитанную ассемблером). LEA использует PC-relative для вычисления действующего адреса во время выполнения инструкции.
lea [label
- это бессмысленная трата байтов по сравнению с более компактным mov
, поэтому вам следует указать условия, о которых вы говорите. Кроме того, для некоторых ассемблеров [label]
неправильный синтаксис для режима адресации, относящейся к RIP. Но да, это так. Как загрузить адрес функции или метку в регистр в GNU Assembler объясняется более подробно.
- person Peter Cordes; 22.06.2020
LEA (Загрузить эффективный адрес) - это инструкция сдвига и сложения. Он был добавлен в 8086, потому что там есть оборудование для декодирования и расчета режимов адресации.
Разница небольшая, но важная. Инструкция MOV - это фактически «MOVe», копия адреса, обозначенного меткой TABLE-ADDR. Инструкция LEA - это «Load Effective Address», которая является косвенной инструкцией, что означает, что TABLE-ADDR указывает на ячейку памяти, в которой находится адрес для загрузки.
Эффективное использование LEA эквивалентно использованию указателей в таких языках, как C, поэтому это мощная инструкция.