Я хотел бы знать, в чем разница между этими инструкциями:
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 относится к размещению значения в условном стеке и обычно 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, immdata, что означает считывание непосредственных данных (т.е. постоянных) immdata в регистр reg. Обратите внимание, что если addr в reg LEA, addr является просто константой (то есть с фиксированным смещением), то эта инструкция LEA по существу точно такая же, что и эквивалентная команда MOV reg, immdata, которая загружает ту же константу, что и непосредственные данные.
Если вы укажете только литерал, разницы нет. У 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
.
Подумайте о С. Допустим, у меня есть массив, 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));
Кажется, это должно быть основано на моих предыдущих заявлениях, но это не так.
То, что происходит, заключается в том, что добавление указателя С имеет хитрость. Скажем, у меня есть указатель, 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 - это то, как вы можете использовать поддержку 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 [метка], но инструкция MOV содержит эффективный адрес внутри самой инструкции как непосредственную константу (вычисляемую заранее ассемблером). LEA использует PC-относительный для вычисления эффективного адреса во время выполнения инструкции.
lea [label
это бессмысленная трата байтов против более компактных mov
, поэтому вы должны указать условия, о которых вы говорите. Кроме того, для некоторых ассемблеров [label]
неправильный синтаксис для режима RIP-относительной адресации. Но да, это точно. Как загрузить адрес функции или метки в регистр в GNU Assembler, объясняется более подробно.
Разница тонкая, но важная. Инструкция MOV является «MOVe», по сути, копией адреса, обозначаемого меткой TABLE-ADDR. Инструкция LEA - это «Эффективный адрес загрузки», который является косвенной инструкцией, что означает, что TABLE-ADDR указывает на область памяти, в которой находится адрес для загрузки.
Эффективное использование LEA эквивалентно использованию указателей на таких языках, как C, поэтому это мощная инструкция.