Когда мы используем команду mov в сборке, исходный и целевой операнды должны быть одного размера. Если я напишу:
mov rax, 1
Преобразован ли операнд 1 в соответствии с размером регистра rax?
Например, если rax 16 бит, мы получим:
0000000000000001
?
@MargaretBloom: немедленные значения всегда расширяются знаком до размера операнда для кодов операций, которые используют узкие непосредственные. По крайней мере, я не могу придумать ни одного места, где они были бы нулевыми. Если вы хотите нулевое расширение, вы должны использовать mov eax, imm32
, который имеет 32-битный размер операнда и следует обычному правилу записи 32-битного регистра с расширением нуля для заполнения 64-битного регистра. Почему инструкции x86-64 для 32-битных регистров обнуляют верхнюю часть полного 64-битного регистра?. (Я предполагаю, что это то, что вы имели в виду, но если мы рассмотрим более узкий размер операнда, то 16 и 8 бит будут
Существует специальная инструкция movabs rax,<64b immediate>
, которая будет содержать закодированное значение «1» как целое число 64b, но обычный современный ассемблер NASM, например, будет собирать mov rax,1
в инструкцию mov eax,1
с немедленным 32b (машинный код b8 01 00 00 00
), которая установит окончательный контент rax
в точном так же, но кодировка намного короче. .. В любом случае, если в инструкции есть rax
в качестве целевого регистра, то вы можете сделать ставку, что любая выполняемая операция будет нацелена на все 64 бита целевого регистра. Как / если операнд расширен, зависит от конкретной инструкции и операнда.
В любом случае, @koinos: см. Также Разница между movq и movabsq в x86-64 и В чем разница между инструкциями movq и movabsq x86-64 AT&T?, чтобы узнать больше о MOV, потому что MOV особенный; это единственная инструкция, которая может использовать 64-битную версию немедленно, поэтому существует несколько способов, которыми ассемблер может выбрать кодирование этого исходного кода asm в машинный код.
@PeterCordes Да, но большинство ассемблеров будут кодировать mov rax, X
как mov eax, X
, если это возможно, поэтому полезно думать, что они расширены нулем (например, mov rax, 0xf0000000
составляет всего 5 байтов), хотя это технически не на 100% правильно.
@MargaretBloom: в целом да. Я понимаю, что я просто педантичен, но, похоже, вопрос, возможно, касался технических особенностей размера операнда. Неясно, о чем он на самом деле пытается спросить. Но я бы не сказал «большинство». NASM будет, но YASM - нет, и GAS .intel_syntax
тоже. Я не знаю о FASM или MASM, и я не проверял clang / LLVM .intel_syntax
, чтобы узнать, выполняет ли он оптимизацию во время сборки для другого размера операнда, поэтому полученный asm больше не ссылается явно на RAX.
@PeterCordes О, я не знал, что это в основном функция NASM.
@MargaretBloom: Я думаю, что YASM считает это пропущенной оптимизацией, но я не уверен, что разработчики GAS примут патч, если кто-нибудь его отправит. Я не смотрел, хотят они этого или нет. Я предполагаю, что, возможно, нет, потому что возможность получить больше разных кодировок (для целей выравнивания кода) - это функция, и если они не добавят способ переопределить ее обратно в 7-байтовую кодировку, вы потеряете это. Теперь мне интересно узнать о MASM и FASM, потому что, если они это сделают, было бы несправедливо сказать, что это в основном функция NASM.
@PeterCordes FASM, кажется, делает это, позвольте мне посмотреть, смогу ли я достать копию MASM.
Нет, извините @Peter, FASM этого не делает, а MASM делает.
Есть 2 языка. Первый - это язык ассемблера, где у вас может быть строка символов вроде "mov rax, 1". Второй - машинный язык, где у вас будет набор байтов.
Эти языки родственные, но разные. Например, инструкция mov
на языке ассемблера на самом деле представляет собой несколько различных кодов операций на машинном языке (один для перемещения байтов в / из регистров общего назначения, один для перемещения слов / двойных слов / слов в регистры общего назначения, один для перемещения двойных слов / слов для управления регистры, один для перемещения двойных слов / слов в регистры отладки и т. д.). Ассемблер использует инструкцию и ее операнды для выбора соответствующего кода операции (например, если вы выполняете mov dr6,eax
, то ассемблер выберет код операции для перемещения dwords / qwords в регистры отладки, потому что ни один из других кодов операции не подходит).
Таким же образом могут быть разные операнды. Например, для языка ассемблера константа 1
имеет тип «целое число» и не имеет никакого размера (ее размер подразумевается из того, как / где она использовалась); но в машинном коде непосредственный операнд должен быть каким-то образом закодирован, и размер кодировки будет зависеть от того, какой код операции (и какие префиксы) используется для mov
.
Например, если mov rax,1
преобразован в байты 0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00; тогда вы могли бы сказать, что операнд - это «64 бита, закодированные в 4 байта (с использованием расширения знака)»; или вы могли бы сказать, что операнд 32 бита, закодированный в 4 байта (и что инструкция перемещает только 32 бита в RAX
, а затем знак распространяется на верхние 32 бита RAX вместо того, чтобы перемещать что-либо в них). Несмотря на то, что эти вещи звучат по-разному (и хотя большинство людей сказали бы, что последнее «более правильное»), поведение точно такое же, и единственные различия - это поверхностные различия в том, как машинный код (другой язык, не являющийся языком ассемблера) описан. На ассемблере 1
по-прежнему является («подразумеваемым из контекста») 64-битным операндом, независимо от того, что происходит на машинном языке.
Есть два кода операции для перемещения байтов в / из регистров GP (по одному для каждого направления). Или третий, если вы включаете mov reg, imm
: немедленное перемещение байтов. И на самом деле вдвое больше, потому что есть другой код операции для 8-битного размера операнда по сравнению с 16/32/64 (тот же код операции с разными префиксами). Но в любом случае «один код операции для перемещения в / из регистров GP» определенно не совсем правильный. Ссылка на felixcloutier.com/x86/MOV.html здесь - хорошая идея.
Ваш последний абзац читается так, как будто он описывает кодировку b8 01 00 00 00
(mov eax,imm32
). В 7-байтовой кодировке, которую вы показываете, используется немедленное 32-битное расширение со знаком и определенно записывается полный RAX с 64-битным размером операнда, не оставляя их для неявного нулевого расширения. Хотел бы +1 к этому ответу, как только вы исправите эту ошибку, я думаю, вы точно определили путаницу OP между asm и машинным кодом.
@PeterCordes: вы можете описать его как 2 разных 8-битных кода операции, или вы можете описать его как один 7-битный код операции, который имеет дополнительный параметр «направления», вставленный в (самый низкий) бит, или вы можете описать его как 32 разных 16 -битовые коды операций (по одному для каждого регистра, для каждого направления) или ... Все равно, независимо от того, как это кто-то описывает.
Это честно; Intel определяет это так, как я описал, но ваш взгляд на это тоже имеет смысл. Я предполагаю, что вы работаете над обновлением других пунктов, особенно последнего абзаца.
@PeterCordes: Для кодировки, которую я использовал в примере, я просто поместил "mov rax, 1" в онлайн-ассемблер (в defuse.ca/online-x86-assembler.htm) и скопировал то, что он сказал. Ассемблер может выбрать разные способы кодирования одной и той же инструкции, и на самом деле не имеет большого значения, что используется в примере. Обратите внимание, что это может иметь практическое применение - например, Я знаю по крайней мере один ассемблер (a86 и a386), который использует его для борьбы с пиратством (поэтому автор ассемблера может сказать, использовался ли его ассемблер / не использовался, основываясь на том, какие кодировки были выбраны).
У вас получилась кодировка mov r/m64, sign_extended_imm32
. Ваше описание неверно для этого, но идеально подходит для оптимизации, которую делают NASM и MASM, оптимизации его до mov eax, imm32
@PeterCordes: А, теперь я понимаю, о чем вы говорите (извините, теперь исправлено!)
RAX 64-битный. В 64-битном режиме, как правило, непосредственные значения имеют либо 32 бита (расширенный знак, либо ноль), либо 64 бита.