Использование 8-битных регистров в режимах индексированной адресации x86-64

Можно ли использовать 8-разрядные регистры (al, ah, bl, bh, r8b) в режимах индексированной адресации в x86-64? Например:

mov ecx, [rsi + bl]
mov edx, [rdx + dh * 2]

В частности, это позволит использовать нижние 8-битные регистры в качестве смещения 0-255, что может быть полезно для некоторых ядер.

Я налил в руководства Intel, и они не являются явными в этом вопросе, но все примеры, которые они приводят, имеют только 32-разрядные или 64-разрядные базовые и индексные регистры. В 32-битном коде я видел только 16 или 32-битные регистры. Глядя на детали кодирования mod-r/m и SIB, также кажется, что он указывает на "нет", но достаточно сложный с достаточным количеством угловых случаев, что я не уверен, что правильно понял.

Меня больше всего интересует поведение x86-64, но, конечно, если это возможно в 32-битном режиме, я бы только хотел узнать.

Как дополнительный вопрос слишком мал и связан с тем, чтобы заслужить еще одну запись - могут использоваться 16-битные регистры для базы или индекса? Например, mov rax, [rbx + cx]. Мое расследование указывало на тот же ответ, что и выше: вероятно, нет.

Ответ 1

Нет, вы не можете использовать 8-битные или 16-разрядные регистры для адресации вычислений в 64-битном режиме, а также не можете использовать 8-битные регистры в 32-битном режиме. Вы можете использовать 16-разрядные регистры в 32-битном режиме и 32-разрядные регистры в 64-битном режиме с использованием префиксного байта размера 0x67.

В этой таблице хорошо представлены различные варианты размеров операндов и адресов. Общий шаблон состоит в том, что размер адреса по умолчанию такой же, как и текущий режим (т.е. 32-битный в 32-битном режиме, 64-битный в 64-битном режиме) 1 а затем, если Включен префикс 0x67, размер адреса изменен на половину обычного размера (т.е. 16 бит в 32-битном режиме, 32 бит в 64-битном режиме).

Здесь выдержка из полной таблицы, связанной выше, показывающая только 64-битное поведение в режиме длинного режима, для различных значений операндов REX.W, 0x66 и 0x67:

╔════════╦═════════════╦═════════════╦═══════════════╦══════════════╗
║ REX.W  ║ 0x66 prefix ║ 0x67 prefix ║ Operand size  ║ Address size ║
║        ║ (operand)   ║ (address)   ║ (footnote 2)  ║              ║
╠════════╬═════════════╬═════════════╬═══════════════╬══════════════╣
║      0 ║ No          ║ No          ║ 32-bit        ║ 64-bit       ║
║      0 ║ No          ║ Yes         ║ 32-bit        ║ 32-bit       ║
║      0 ║ Yes         ║ No          ║ 16-bit        ║ 64-bit       ║
║      0 ║ Yes         ║ Yes         ║ 16-bit        ║ 32-bit       ║
║      1 ║ Ignored     ║ No          ║ 64-bit        ║ 64-bit       ║
║      1 ║ ignored     ║ Yes         ║ 64-bit        ║ 32-bit       ║
╚════════╩═════════════╩═════════════╩═══════════════╩══════════════╝

1 Это может показаться очевидным, но это противоположно тому, как размеры операндов работают в 64-битном режиме: большинство по умолчанию - 32 бита, даже в 64-битном режиме, и префикс REX необходимо для их продвижения до 64 бит.

2Некоторые инструкции по умолчанию имеют размер 64-битного операнда без префикса REX, особенно push, pop, call и условные переходы, и, как указывает Питер ниже, это приводит к нечетной ситуации, когда по крайней мере некоторые из этих инструкций (push и pop включены) не могут быть закодированы для использования 32 -битные операнды, но могут использовать 16-битные операнды (с префиксом 0x66).