x86 - это машина регистра, где не более 1 операнда для любой инструкции может быть явным адресом памяти вместо регистра, используя режим адресации, такой как [rdi + rax*4]
.(Существуют инструкции, которые могут иметь 2 операнда памяти, причем один или оба являются неявными, однако: Какие инструкции x86 принимают два (или более) операнда памяти? )
Типичное целое число x86инструкции имеют 2 операнда, оба явных, например add eax, edx
, что делает eax+=edx
.
В устаревшем коде x87 FP используются инструкции с 1 операндом со стеком x87как faddp st1
, где вершина стека x87 (st0
) является неявным операндом.SSE2 является базовой для x86-64, поэтому он больше не используется.
Современный код FP использует SSE / SSE2 с 2-операндными инструкциями, такими как addsd xmm0,xmm1
или 3-операндными кодировками AVX, такими как vaddsd xmm2, xmm0, xmm1
Существуют инструкции x86 с 0, 1, 2, 3 и даже 4 явными операндами.
Существует несколько форматов команд, но явный reg / memoryоперанды обычно кодируются в байте ModR / M, который следует за байтом (-ами) кода операции.Имеет 3 поля:
- 2-битный режим для операнда r / m (регистр прямой
reg
, регистр косвенный [reg]
, [reg + disp8], [reg+disp32]
).Режимы с битами смещения сигнализируют, что эти байты следуют за байтом ModR / M. - 3-битное поле r / m (регистр, используемый для этого операнда, или для режимов адресации памяти, код выхода, который означает, что естьмасштабный / индексный / базовый байт после ModRM, который может кодировать режимы адресации с масштабированным индексом для операнда r / m).См. rbp не разрешен в качестве базы SIB? для подробностей особых случаев / escape-кодов.
- 3-битное поле reg, всегда регистр.
Большинство инструкций доступно как минимум в 2 кодировках: reg / memory destination или reg / memory source.Если вы хотите, чтобы оба операнда были регистрами, вы можете использовать либо код операции, либо add r/m32, r32
, либо add r32, r/m32
.
В общих инструкциях также есть другие коды операций для форм непосредственного источника, но обычно они используют reg
поле в ModR / M в качестве дополнительных битов кода операции, поэтому вы все равно получите только 2 операнда, например add eax, 123
Исключением является немедленная форма imul
с добавлением 286, например, imul eax, [rdi + rbx*4], 12345
.Вместо того, чтобы делить пространство кодирования с другими непосредственными инструкциями, он имеет регистр dst и ar / m source в ModR / M плюс непосредственный операнд, подразумеваемый кодом операции.
Некоторые инструкции с одним операндомиспользуйте тот же прием использования поля reg
в качестве дополнительных битов кода операции, но без немедленного.например, neg r/m32
, not r/m32
, inc r/m32
или shl
/ shr
/ кодирования поворота, которые сдвигаются на неявную 1 (не на cl
или сразу).Так что, к сожалению, вы не можете копировать и сдвигать (до BMI2).
Существуют некоторые специальные кодировки для улучшения плотности кода, такие как однобайтовые кодировки для push rax
/ push rdx
, которые упаковываютreg
поле в младшие 3 бита байта кода операции.А в 16/32-битном режиме однобайтовые кодировки для inc
/ dec
любого регистра.Но в 64-битном режиме эти 0x4?
коды используются в качестве префиксов REX для расширения полей reg
и r/m
для предоставления 16 архитектурных регистров.
Есть также инструкции снекоторые или все неявные операнды , например movsb
, которые копируют байт из [rsi]
в [rdi]
и могут использоваться с префиксом rep
для повторения этого rcx
раз.
Или mul ecx
делает edx:eax = eax * ecx
.Один явный исходный операнд, один неявный источник и 2 неявных регистра назначения.div
/ idiv
аналогичны.
Инструкции с хотя бы одним явным операндом reg / mem используют для него кодировку ModR / M, но инструкции с нулевыми явными операндами (например, movsb
или cdq
) не имеют байта ModR / M.У них просто есть код операции.В некоторых инструкциях вообще нет операндов, даже неявных, как mfence
.
Непосредственные операнды не могут быть переданы через ModR / M, только самим кодом операции, поэтому push imm32
или push imm8
имеют свои собственные коды операции.Неявные места назначения (память на [rsp]
и само RSP обновляются до rsp-=8
).
LEA - это обходной путь, который дает x86 с 3 операндами shift-and-add , как lea eax, [rdi + rdi*2 + 123]
, чтобы сделать eax = rdi*3 + 123
в одной инструкции.См. Использование LEA для значений, которые не являются адресами / указателями? Регистр назначения кодируется в поле reg
ModR / M, а два регистра источника кодируются в режиме адресации.(Включая байт SIB, о наличии которого сигнализирует байт ModR / M, используя кодировку, которая в противном случае означала бы base = RSP).
Префиксы VEX (представленные в AVX) обеспечиваютИнструкции с 3 операндами, такие как bzhi eax, [rsi], edx
или vaddps ymm0, ymm1, [rsi]
. (Для многих инструкций 2-й источник является необязательным, но для некоторых это первый источник.)
3-й операнд закодирован в 2- или 3-байтовом префиксе VEX.
Существует несколько 3-операндных инструкций, не относящихся к VEX, таких как переменные SSE4.1, например vpblendvb xmm1, xmm2/m128, <XMM0>
где XMM0 - неявный операнд, использующий этот регистр.
Версия AVX делает его неразрушающим (с отдельным назначением, закодированным в префиксе VEX), и делаетявный операнд управления смешиванием (закодированный в старших 4 битах 1-байтового немедленного). Это дает нам инструкцию с 4 явными операндами, VPBLENDVB xmm1, xmm2, xmm3/m128, xmm4
.
x86 довольно дикий и много раз расширялся, но типичный целочисленный код использует в основном 2-операндные инструкции, с хорошим количеством LEA, добавленным, чтобы сохранить инструкции.