иллюстрировать код, выражающий как опасность для данных, так и опасность для контроля? - PullRequest
0 голосов
/ 29 февраля 2020

Это пример опасности для данных:

MOV AL , 25 
MOV BL , 10 
ADD CL , AL , BL
LOAD DL , CL

В этом коде ассемблера мы видим, что инструкция LOAD DL, CL может быть выполнена, только если инструкция ADD CL, AL, BL казнены. Следовательно, эти две инструкции зависят от регистра CL. Чтобы справиться с проблемой опасности данных, мы можем также уменьшить количество инструкций за цикл. Это позволит избежать зависимости между регистрами.

Это пример конфликтной опасности (опасности ветвления):

MOV AL , 25  
MOV BL , 10 
CALL branchement
LOAD DL , BL
ADD DL , AL

В этом коде ассемблера две инструкции LOAD AD, BL и ADD DL , AL уже находятся в конвейере еще до выполнения инструкции CALL. В этом случае не выполняются условия для подключения, что создает опасность управления. Чтобы предотвратить повторение этого типа конфликта, рекомендуется очистить конвейер перед загрузкой новых инструкций

Я хотел бы увидеть пример обеих опасностей (управляющая опасность + опасность для данных) в тот же код

РЕДАКТИРОВАТЬ :

Смешивание 2 дает мне это:

MOV AL , 25 ; 
MOV BL , 10 ; 
CALL branchement
ADD DL , AL
LOAD DL , BL

1 Ответ

1 голос
/ 01 марта 2020

Фактический 8086 вообще не конвейеризован (кроме предварительной выборки); это микрокодировано Он завершает обратную запись одной инструкции перед началом декодирования следующей; единственный опасный эффект - отбрасывание буфера предварительной выборки после ветвлений.

x86 инструкции могут быть трудно переданы (особенно в память-назначение); это было до 486 / Pentium, когда это действительно было сделано, и тогда сложные инструкции остановили конвейер порядка (в основном, опасность внутри одной инструкции, например add [edx], eax или pop eax). Только в Pentium Pro (микроархитектура P6) даже такие инструкции можно было эффективно обрабатывать (декодируя до 1 или более мопов и обрабатывая их через exe-order exe c). См. Руководство по микроархитектуре Agner Fog https://agner.org/optimize/

(Реальное семейство P6 и другие вышедшие из строя exe c x86 скрывают опасности WAW и WAR путем переименования регистров. См. Почему mulss занимает всего 3 цикла в Haswell, в отличие от таблиц инструкций Агнера? (Развертывание циклов FP с несколькими аккумуляторами) и Оптимизация программы для конвейера в процессорах семейства Intel Sandybridge .)

Код, который вы показали, не строго x86; здесь нет мнемоники; Чистая инструкция загрузки x86 называется mov. Кроме того, LOAD DL, BL не имеет смысла; ни один из операндов не может обращаться к памяти; это только 8-битные регистры. Если вы имели в виду копирование между регистрами, это также mov dl, bl.


Я хотел бы видеть пример обеих опасностей (управляющая опасность + опасность для данных) в одном и том же коде

Простым примером будет косвенная ветвь (управляющая опасность), цель которой была недавно написана (истинная зависимость от RAW-данных).

например, если мы примем 16-битный режим (так как вы упомянули 8086):

   push  offset target     ; modifies SP (the stack pointer), then stores to memory at SS:SP
   ret                     ; ordinary near return = pop ip

target:
   push  123

ret имеет 2 входа:

  • регистр SP (только что записанный pop: опасность RAW)
  • память, на которую указывает SP ( также только что написано pop, также опасно для памяти в формате RAW.

RET пишет SP (опасность WAR, хотя сам RET был последним читателем). Также WAW, если мы считаем, что pu sh и ret оба пишут SP.

RET выполняет косвенный переход (в основном pop ip), используя адрес, загруженный из памяти (управляющая опасность для конвейера, если есть). Все текущие процессоры будут неверно прогнозировать ret, потому что у них есть специальный стек предикторов call / ret, который предполагает, что ret перейдет к адресу возврата совпадающего call, как в обычном коде. (http://blog.stuffedcow.net/2018/04/ras-microbenchmarks/)

push 123 по заданному адресу возврата

  • читает и записывает SP (RAW и WAR опасности )
  • записывает в память, что предыдущий pu sh написал (опасность памяти WAW), и какой RET только что прочитал (опасность памяти WAR).

Я положил push после ret на тот случай, если вы хотите посмотреть только на пару ret / pu sh, с pu sh в «тени» возможно ошибочно предсказанной ветви.


Конечно, буфер хранилища с пересылкой хранилища скрывает / обрабатывает опасности данных в памяти, эффективно переименовывая расположение памяти / кэша. (Модель упорядочения памяти x86 в основном соответствует программному порядку + буфер хранилища с пересылкой хранилища: ядрам разрешается перезагружать свои собственные хранилища, прежде чем они станут глобально видимыми.)

Современные процессоры x86 обрабатывают цепочку зависимостей данных RAW посредством указатель стека с «механизмом стека», который может отслеживать несколько смещений указателя стека за такт. (И, что не менее важно, устраняет необходимость в дополнительном мопе для фактического добавления E / RSP в бэк-энде, поэтому push / pop может быть единичным мопом.) Таким образом, это эффективный механизм альтернативной нулевой задержки для выполнения частей модификации указателя стека инструкций стека. Непосредственное использование E / RSP (например, mov bp, sp) приводит к стеку syn c uop (на процессорах Intel), который обнуляет сохраненное смещение и применяет его к значению сервера. Если смещение было ненулевым.

...