В чем разница между инструкциями x86 NOP и FNOP?

StackOverflow https://stackoverflow.com//questions/25008772

  •  20-12-2019
  •  | 
  •  

Вопрос

Я читал статью Руководство по эксплуатации Intel и заметил, что есть инструкция 'NOP', которая ничего не делает на основном процессоре, и инструкция 'FNOP', которая ничего не делает на FPU.Почему существуют две отдельные инструкции ничего не делать?

Единственное, что отличалось, что я видел, это то, что они генерируют разные исключения, поэтому вы можете следить за исключением из FNOP, чтобы определить, доступен ли FPU.Но разве нет других механизмов, таких как CPUID, для обнаружения этого?Какая практическая причина иметь две отдельные инструкции NOP?

Это было полезно?

Решение

Развивая комментарии Раймонда Чена и Ханса Пассанта, можно сказать, что существуют исторические причины существования двух отдельных инструкций и почему они не совсем дают одинаковый эффект.

Ни одна из этих двух инструкций, NOP и FNOP, первоначально были разработаны как явная инструкция без операции.Тот Самый NOP инструкция на самом деле является просто псевдонимом для инструкции XCHG AX,AX.(Или в 32-разрядном режиме XCHG EAX, EAX.) На ранних процессорах Intel это фактически ничего не делало.Хотя внешне это не имело видимого эффекта, внутренне это было выполнено точно так же, как XCHG инструкция, требующая столько циклов для выполнения.486-й был первым процессором Intel, который обработал его особым образом, он мог выполнять NOP за 1 цикл, в то время как для выполнения любого другого перехода от регистра к регистру потребовалось 3 цикла XCHG инструкция.

Лечащий XCHG AX,AX инструкции особенно становятся очень важными в современных процессорах Intel.Если бы он все еще фактически обменивался одним и тем же регистром с самим собой, это могло бы привести к остановке конвейера, если бы соседняя инструкция также использовала AX Зарегистрироваться.Обрабатывая его особым образом, центральный процессор в конечном итоге не думает о NOP необходимо дождаться предыдущей инструкции, которая устанавливает AX или что следующая инструкция должна дождаться NOP.

Это поднимает тот факт, что существует множество различных инструкций, которые, однако, ничего не делают XCHG AX,AX является единственным, который состоит из одного байта (как частный случай обмен-регистра-с-накопителем одним байтом XCHG кодировки).Часто эти инструкции используются как замена одной инструкции последовательной NOP инструкции, например, при выравнивании начала цикла по соображениям производительности.Например, если вам нужен 6-байтовый NOP, вы могли бы использовать LEA EAX,[EAX + 00000000].В конце концов Intel добавила явную многобайтовую инструкцию NOP.(Ну, не столько добавлена, сколько официально задокументирована инструкция, которая была там со времен Pentium Pro.) Однако специально обрабатывается только однобайтовая форма;многобайтовые NOP-операции приведут к остановкам, если соседние команды используют одни и те же регистры.

Когда AMD добавила поддержку 64-разрядной версии в свои процессоры, они пошли еще дальше. NOP больше не является эквивалентом XCHG EAX,EAX в 64-битном режиме.Одна из проблем с набором инструкций Intel заключается в том, что существует множество инструкций, которые изменяют только часть register .Например MOV BX,AX изменяет только младшие 16 бит EBX оставив верхние 16 бит неизмененными.Эти частичные модификации затрудняют процессору предотвращение зависаний, поэтому AMD решила предотвратить это при использовании 32-разрядных инструкций в 64-разрядном режиме.Всякий раз, когда результат 32-разрядной операции сохраняется в (64-разрядном) регистре, значение равно нулю, расширенное до 64 бит, так что изменяется весь регистр целиком.Это означает XCHG EAX,EAX больше не является NOP, так как он очищает верхние 32 бита EAX (и, следовательно, если вы явно напишете XCHG EAX,EAX, он не может собраться до 0x90 и должен использовать 87 C0 кодирование).В 64-битном режиме NOP теперь это явный NOP без какой-либо другой интерпретации.


Что касается FNOP инструкция, на оригинальном 8087 не совсем ясно, как FPU обработал эту инструкцию, но я почти уверен, что она также не обрабатывалась как явное отсутствие операции.По крайней мере, одно старое руководство Intel, Руководство по повторной защите языка ASM86 документирует как выполнение чего-либо без эффекта ("сохраняет стек сверху донизу").Судя по его положению на карте кода операции, похоже, что это может быть псевдоним для любого из них FST ST или FLD ST, оба из которых копировали бы верхнюю часть стека в верхнюю часть стека.Однако он получил некоторую специальную обработку, он выполнялся в среднем за 13 циклов вместо средних 18 или 20 циклов для стека в стек FST или FLD инструкция соответственно.Если бы это рассматривалось как инструкция без операции, я бы ожидал, что это будет еще быстрее, поскольку существует несколько инструкций 8087, которые могут выполняться в два раза быстрее.

Что еще более важно, FNOP инструкция ведет себя иначе, чем NOP из-за того, как инструкции FPU раньше реализовывались на процессорах Intel.Сам центральный процессор не поддерживал арифметику с плавающей запятой, вместо этого эти обязанности были перенесены на дополнительный сопроцессор с плавающей запятой, первоначально 8087.Одной из приятных особенностей сопроцессора было то, что он выполнял инструкции параллельно с центральным процессором.Однако это означает, что процессору иногда приходится ждать, пока FPU завершит операцию.Центральный процессор автоматически ожидает завершения выполнения предыдущей инструкции, прежде чем передать ему другую инструкцию, но программе потребуется явно подождать (используя WAIT инструкция), прежде чем он смог прочитать результат, который сопроцессор записал в память.

Поскольку сопроцессор работал параллельно, это также означало, что если бы команда FPU сгенерировала исключение с плавающей запятой, к моменту обнаружения этого процессор уже перешел бы к выполнению следующей команды.Обычно, когда команда генерирует исключение в CPU, оно обрабатывается, пока эта команда все еще выполняется, но когда команда FPU генерирует исключение, CPU уже завершил выполнение этой инструкции, передав ее FPU.Вместо прерывания работы центрального процессора и асинхронной доставки исключения с плавающей запятой центральный процессор получает уведомление только тогда, когда он ожидает сопроцессора, явно или неявно.

В современных процессорах FPU больше не является сопроцессором, это неотъемлемая часть центрального процессора.Это означает, что программам больше не нужно ждать, пока FPU запишет значения в память.Однако способ обработки исключений FPU не изменился.(Оказывается, немедленную доставку исключений сложно реализовать на современных процессорах, поэтому они воспользовались единственным случаем, когда в этом не было необходимости.) Таким образом, если предыдущая команда FPU сгенерировала недоставленное исключение с плавающей запятой, a NOP оставьте исключение недоставленным, в то время как FNOP, поскольку это инструкция FPU, будет выполнять неявное "ожидание", которое приводит к доставке исключения с плавающей запятой.

Этот пример демонстрирует разницу:

FLD1       ; push 1.0 onto the FPU stack
FLDZ       ; push 0.0
FDIV       ; divide 1.0 by 0.0
NOP        ; does nothing
NOP        ; does nothing
FNOP       ; signals a FP zero-divide exception and then does nothing
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top