Почему для использования xor требуется меньше байтов, чем для mov?

При установке x равным нулю (x = 0) в моей книге csapp указаны два способа.

Первый:

xorq %rcx, %rcx

Второй:

movq $0, %rcx

Это также говорит о том, что первый занимает всего 3 байта, а второй - 7 байт.

Как работают два способа? Почему первый занимает меньше байтов, чем второй?


person Jinwoo Park    schedule 25.09.2019    source источник
comment
Разные инструкции в x86 имеют разный размер. xor требует меньше байтов для кодирования.   -  person Shawn    schedule 25.09.2019
comment
Проверьте справку по набору инструкций для кодировок этих инструкций. Тем не менее, xor %eax, %eax еще короче — всего два байта.   -  person fuz    schedule 25.09.2019


Ответы (2)


Потому что mov требуется больше места для кодирования своего 32-битного непосредственного исходного операнда.
xor требуется только байт ModRM для кодирования своих операндов.

Ни одному из них не нужен префикс REX, поэтому вы должны сравнивать 2-байтовый xor %ecx,%ecx с 5-байтовым mov $0, %ecx. Почему x86- 64 инструкции в 32-битных регистрах обнуляют верхнюю часть полного 64-битного регистра? GAS не выполняет эту оптимизацию за вас, и movq дает вам кодировку mov $sign_extended_imm32, %r/m64 вместо специальной 5-байтовой кодировки mov $imm32, %r32 в котором отсутствует байт ModRM.

(Как отмечено в в примере CS:APP используется idivq с двумя операндами? , в CS:APP полно ассемблерных ошибок. Это не недопустимая синтаксическая ошибка, а просто пропущенная оптимизация.)


К сожалению, нет кодирования mov с 8-битным непосредственным расширением знака, иначе мы могли бы иметь 3-байтовое mov reg, imm8. (https://www.felixcloutier.com/x86/mov). (Я удивлен, что никакая итерация x86-64 не переназначила один из байтов кода операции, который он освободил, для такой хорошей кодировки mov, возможно, смешанной с BMI1 или чем-то еще.)

Для получения более подробной информации о кодировании инструкций x86 прочитайте руководство Intel vol.2 и посмотрите на дизассемблирование.

См. также Как лучше всего обнулить регистр в ассемблере x86: xor, mov или and? подробнее о том, почему xor-zeroing оптимален: на некоторых процессорах, особенно в семействе P6 и Sandybridge-family, он имеет микроархитектурные преимущества перед mov помимо просто размера кода.

person Peter Cordes    schedule 25.09.2019

Почему первый занимает меньше байтов, чем второй?

Хотя ответ Питера Кордеса уже касается технических деталей, я хотел бы сосредоточиться на математическом фоне:

Процессор x86s явно не различает большие числа (например, 12345789) и нулевое значение: для хранения такого значения требуется 4 байта.

Однако нулевое значение — это особое значение:

Его можно записать как (а-а) или как (исключающее ИЛИ а), а «а» может быть любым целым числом!

Это означает, что вы можете выполнить трюк:

Вы выполняете операцию subq %rcx, %rcx для вычисления значения (rcx - rcx). Неважно, какое значение имеет rcx: если вы вычтете это значение из самого себя, результат будет нулевым (поскольку (a-a)=0).

Это означает, что после этой операции rcx будет равно 0.

Операция xorq %rcx, %rcx имеет тот же эффект, потому что (XOR a) также всегда равно 0.

person Martin Rosenau    schedule 25.09.2019
comment
1-байтовые немедленные операции поддерживаются для большинства инструкций, отличных, кроме mov. например add $4, %ecx — 3 байта (код операции + modrm + sign_extended_imm8), а add $4096, %ecx — 6 байт (код операции + modrm + imm32). Таким образом, ваш 2-й абзац о том, что x86 не обрабатывает специально небольшие значения, должен, возможно, ограничиться mov. Но да, +1 за объяснение a^a = a-a = 0. Забыл написать что-нибудь об этом в своем ответе. - person Peter Cordes; 25.09.2019