Сборка cltq и разность movslq
Глава 3 " Компьютерные системы. Перспектива программиста" (2-е издание) упоминает, что cltq
эквивалентно movslq %eax, %rax
,
Почему они создали новую инструкцию ( cltq
) вместо того, чтобы просто использовать movslq %eax,%rax
? Разве это не избыточно?
1 ответ
TL; DR: использовать cltq
когда это возможно, потому что он на один байт короче точно эквивалентного movslq %eax, %rax
, Это очень незначительное преимущество (поэтому не жертвуйте чем-либо другим, чтобы это произошло), но выберите eax
если вы хотите подписать-продлить его много.
Это в основном относится к авторам компиляторов (компиляция индексированных массивов счетчиков циклов со знаком со знаком); такие вещи, как расширение знака счетчика цикла, каждая итерация происходит только тогда, когда компиляторы не могут воспользоваться преимуществами переполнения со знаком, являющегося неопределенным поведением, чтобы избежать его. Программисты-люди просто решат, что подписано, а что нет, чтобы сохранить инструкции.
Связанный: полный переход на мнемонику Intel против AT&T для различных размеров инструкций, которые расширяются в RAX (cltq
) или из EAX в EDX: EAX (cltd
), с эквивалентным movsx
/ movs?t?
: Что делает cltq в сборке?,
История
На самом деле, 32->64-битная форма MOVSX, называемая movslq
в AT&T синтаксис, новый, новый с AMD64. Мнемоника синтаксиса Intel на самом деле MOVSXD. Код операции 63 /r
(таким образом, это 3 байта, включая необходимый префикс REX, по сравнению с 4 байтами для 8->64 или 16->64 MOVSX). AMD перепрофилировала код операции из ARPL, которого нет в 64-битном режиме.
Чтобы понять историю, помните, что текущий x86 не был разработан сразу. Сначала был 16-битный 8086, вообще не MOVSZ/MOVZX, только CBW и CWD. Затем 386 добавил MOVS/ZX (и более широкие версии CBW/CWD для расширения знака внутри eax или в edx). Затем AMD расширила все это до 64-битной.
Версии REX существующих кодов операций MOVSX по-прежнему имеют 8- или 16-битный источник, но знак расширяется до 64 бит, а не просто до 32. Префикс размера операнда позволяет вам кодировать movsbw
ака movsx r16, r/m8
, IDK, что произойдет, если вы используете префикс размера операнда и REX.W одновременно. Или что произойдет, если вы используете префикс размера операнда с 16-битной исходной формой MOVSX. Возможно, это просто дорогой способ кодирования MOV, например, использование 63 /r
без префикса REX (против чего рекомендует руководство по установке insn от Intel).
cltq
( он же CDQE) - это просто очевидный способ расширить существующие cwtl
(он же CWDE) с префиксом REX.W для увеличения размера операнда до 64 бит. Первоначальная форма этого, cbtw
(он же CBW), был в 8086 году, предшествовал MOVSX, и был единственным разумным способом подписать что-либо. Поскольку сдвиги с немедленным счетом>1 были функцией 286, наименее плохим вариантом, по-видимому, является mov ah, al
/ mov cl, 7
/ sar ah, cl
транслировать бит знака на все позиции.
Кроме того, не путайте cwtl
с cwtd
( он же CWD: знак, расширяющий ax в dx:ax, например, чтобы настроить для idiv).
Мнемоника AT&T здесь довольно ужасная. l
против d
, действительно? Мнемоника Intel у всех есть e
в конце для тех, которые простираются в rax, а не для тех, которые простираются в (часть) rdx. За исключением CBW, но, конечно, это расширяет al в ax, потому что даже 8086 имел 16-битные регистры, поэтому никогда не нужно было хранить 16-битные значения в dl:al. idiv r/m8
использует ax как источник reg, а не dl: al (и помещает результаты в ah, al)).
увольнения
Да, это одна из многих избыточностей в языке ассемблера x86. например sub eax,eax
против нуля против xor eax,eax
, (mov eax,0
не является полностью избыточным, потому что это не влияет на флаги. Если вы включите небольшие различия, такие как избыточные, или даже инструкции, которые выполняются на разных портах выполнения, есть много способов сделать некоторые вещи.).
Если бы у меня была возможность изменить ISA x86-64, я бы, вероятно, дал бы однобайтовые коды операций MOVZX и MOVSX (вместо 0F XX
двухбайтовые экранированные коды операций, по крайней мере, 8-битные версии. Так movsx eax, byte [mem]
так же компактен, как mov al, [mem]
, (Они уже имеют одинаковую производительность на процессорах Intel: обрабатываются полностью в загрузочном порту, без ALU uop). Большинство реального кода не в состоянии воспользоваться [u]int16_t
массивы для более высокой плотности кэша, поэтому я думаю, что movs/zx от слова к слову или слову встречается реже. Или, может быть, достаточно широкоформатный код вокруг, чтобы оправдать короткие коды для MOVZX r32/r64, r/m16
, Чтобы освободить место, мы можем полностью удалить код операции CBW / CWDE / CDQE. Я мог бы сохранить CWD / CDQ / CQO в качестве полезной настройки для idiv, который не имеет эквивалента в одной инструкции.
В действительности, вероятно, было бы намного полезнее иметь меньше однобайтовых кодов операций и больше управляющих префиксов (например, поэтому обычные insne SSE2 могут иметь 2 байта кода операции + ModRM вместо обычных 3 или 4 байтов кода операции). Декодирование команд не является узким местом с более короткими инструкциями в высокопроизводительных циклах. Но если машинный код x86-64 слишком отличается от 32-битного, нам нужны дополнительные декодирующие транзисторы. Это может быть нормально, теперь, когда ограничения по мощности сделали темный кремний делом, потому что ядру никогда не потребуется включение 32-битного декодера одновременно с его 64-битным декодером. Это не тот случай, когда AMD разрабатывает AMD64.
Вместо CDQ мы могли бы сделать инструкции с двумя операндами с неразрушающим назначением, поэтому sar edx, eax, 31
будет делать CDQ в 3 байта. Отбрасывание однобайтовых опкодов xchg-with-eax (кроме 0x90 xchg eax,eax
NOP) освободил бы много места для кодирования sar, shr, shl без необходимости использовать поле Reg в ModRM в качестве дополнительных битов кода операции. И, конечно, удалите специальный случай не-влияет-флаги для shift_count=0, чтобы убить входную зависимость от FLAGS). У AMD была возможность (частично) сделать это с AMD64, но она предпочла быть консервативной и использовать как можно больше транзисторов с декодированием команд. (На самом деле они не могут винить их за это, но, к сожалению, политические / экономические обстоятельства привели к тому, что x86 упустила свой единственный шанс в обозримом будущем отказаться от своего унаследованного багажа.) Это также означало меньше работы по модификации программного обеспечения для генерации / анализа кода, но это единовременная стоимость и небольшие затраты по сравнению с тем, что каждый процессор x86-64 может работать быстрее и иметь меньшие двоичные файлы.
См. Также вики-тег x86 для получения дополнительных ссылок, включая это старое приложение из руководства NASM, документирующего, когда были представлены все формы каждой инструкции.