-
Notifications
You must be signed in to change notification settings - Fork 2
Лекция 6
Здесь полезный (по мнению автора) дополнительный материал по теме лекции.
- Изначально - отдельное опциональное устройство на материнской плате, с 80486DX встроен в процессор. Именно с этого момента утратило смысл прерывание, возникающее при попытке обратиться к несуществующему FPU, - теперь он есть по умолчанию.
- Операции над 7-ю типами данных:
- целое слово (16 бит)
- короткое целое (32 бита)
- длинное слово (64 бита)
- упакованное двоично-десятичное (80 бит)
- короткое вещественное (32 бита) - float в Си
- длинное вещественное (64 бита) - double в Си
- расширенное вещественное (80 бит) - в компиляторе языка Си этот тип отсутствует (вроде как), поэтому, чтобы получить возможность работать с такими числами, нужно работать с FPU напрямую.
- Нормализованная форма представления числа (мантисса (в диапазоне [0; 1)) и экспонента: 1,... * 2^exp)
- Экспонента увеличена на константу для хранения в положительном виде
- Пример представления 0,625 в коротком вещественном типе:
- 1/2+1/8 = 0,101b
- 1,01b*2-1
- Бит 31 - знак мантиссы, 30-23 - экспонента, увеличенная на 127, 22-0 - мантисса без первой цифры. Экспонента увеличивается на 127 для того, чтобы она всегда хранилась в положительном виде и нам не приходилось возиться со знаком экспоненты (переводить в дополнительный код...), то есть формально в экспоненте всегда лежит число >= 1.
- 0|01111110|01000000000000000000000
- Все вычисления FPU - в расширенном 80-битном формате, то есть, независимо от типов обрабатываемых чисел, сопроцессор при вычислениях расширяет (дополняет) их до 80 бит, и работает уже с этими представлениями чисел.
Помимо обычных чисел , FPU поддерживает на аппаратном уровне ещё и некоторые особые числа:
- Положительная бесконечность: знаковый - 0, мантисса - нули, экспонента - единицы
- Отрицательная бесконечность: знаковый - 1, мантисса - нули, экспонента - единицы
-
NaN (Not a Number; да-да, NaN в JS и Python произошёл именно отсюда, из аппаратного уровня):
- qNAN (quiet - тихий) - возникает при приведении типов/отдельных сравнениях
- sNAN (signal - сигнальный) - возникает при переполнении в большую/меньшую сторону и т.д. То есть, если соответствующим образом настроить сопроцессор, то он при возникновении sNAN будет выдавать ошибки или вызывать прерывание, сигнализирующее о возникновении нештатной ситуации при вычислениях.
Пример: если мы попытаемся привести маленькое 80-битное число к 32-битному,
мы получим 0, так как младшие значащие разряды потеряются. Но сопроцессор запишет
не значение 0, а NaN (qNaN), при этом никакие прерывания не сработают.
- Денормализованные числа (экспонента равна 0): находятся ближе к нулю, чем наименьшее представимое нормальное число. За счёт обнуления экспоненты можно расширить диапазон значений мантиссы и выйти ~1 порядок за пределы нормализованных чисел. Эти числа могут получаться как промежуточный результат вычислений, и обрабатывать их штатными способами нельзя.
-
R0
..R7
- основные регистры (80-битные), адресуются не по именам, а рассматриваются в качестве стекаST
(свой собственный циклический стек, не имеющий никакого отношения к стеку в оперативной памяти).ST
- указатель на вершину стека (соответствует регистру - текущей вершине стека),ST
(1)..ST
(7) - прочие регистры (перенумеровываются динамически относительноST
от вершины к дну) -
SR
(State Register) - регистр состояний, содержит слово состояния FPU. Сигнализирует о различных ошибках в процессе вычислений, в том числе о переполнениях -
CR
(Control Register) - регистр управления (контроль округления, точности). Позволяет настраивать параметры округления, которые сопроцессор учитывает при вычислениях, и точность. Например, с помощью него можно задать математические параметры округления, а можно задать округление в меньшую/большую сторону. -
TW
(Tag Register) – 8 пар битов, описывающих состояния регистровST
(1)..ST
(7): число (00), ноль (01), не число (10 - SNAN, QNAN, бесконечность, денормализованное или не поддерживаемое число), пусто (11 - регистр пока не был задействован). Когда мы обращаемся к сопроцессору в первый раз (инициализируем его), все регистры помечаются пустыми. -
FIP
(FPU Instruction Pointer),FDP
(FPU Data Pointer) - отладочные регистры, хранящие адрес последней выполненной команды и её операнда для обработки исключений
Сопроцессор поддерживает свою группу исключений, которые прикладная программа может как-то обрабатывать.
-
Неточный результат - произошло округление по правилам, заданным в
CR
. Тогда соответствующий бит вSR
хранит направление округления (в большую или меньшую сторону) - Антипереполнение - переход в денормализованное число (получили какое-то слишком маленькое число, которое в каком-то виде ещё хранится, но уже не соответствует правилам обработки чисел в FPU)
- Переполнение - переход в "бесконечность" соответствующего знака
- Деление на ноль - переход в "бесконечность" соответствующего знака
- Денормализованный операнд (попытка использования денормализованного операнда при вычислениях)
-
Недействительная операция (попытка использования
запрещённыхопераций; например, обращение к пустым регистрам)
Набор команд FPU сопоставим по объёму с набором команд основного процессора.
-
FLD
- загрузить вещественное число из источника (либо переменная из оперативной памяти или регистра FPU, либоST
(n)) на вершину стека. Номер вершины вSR
увеличивается. -
FST
/FSTP
- скопировать/считать число с вершины стека в приёмник. В случае копирования вершина стека не изменится, а в случае считывания она увеличится и регистр будет помечен пустым. -
FILD
- преобразовать целое число из источника в вещественное и загрузить в стек (I в названии команды означает, что её операнд – целое число (Integer)) -
FIST
/FISTP
- преобразовать вершину в целое и скопировать/считать в приёмник -
FBLD
,FBSTP
- загрузить/считать десятичное BCD-число (B в названии команды означает, что её операнд – упакованное двоично-десятичное число (packed Binary-coded decimal)) -
FXCH
- обменять местами два регистра стека (вершину (ST
(0)) и источник (если не указан, тоST(1)
))
Автор не указывал операнды команд, потому что не придумал, как их написать так, чтобы это всё вообще читалось... За подробной информацией перейдите по ссылке в начале лекции.
-
FADD
,FADDP
,FIADD
- сложение, сложение с выталкиванием из стека, сложение целых. Один из операндов - вершина стека. P в конце второй команды означает выборку операнда из стека (to pop - вытолкнуть). Результат записывается в приёмник. -
FSUB
,FSUBP
,FISUB
- вычитание -
FSUBR
,FSUBRP
,FISUBR
- обратное вычитание (приёмника из источника). R в конце команды или перед P означает обратный (Reverse) порядок операндов. -
FMUL
,FMULP
,FIMUL
- умножение -
FDIV
,FDIVP
,FIDIV
- деление -
FDIVR
,FDIVRP
,FIDIVR
- обратное деление (источника на приёмник) -
FPREM
- найти частичный остаток от деления (делитсяST
(0) наST
(1)). Остаток ищется цепочкой вычитаний, до 64 раз. Если за 64 итерации вычитания делителя из делимого мы не получим частное, меньшее чем делитель, цепочка завершится досрочно и вызовется соответствующее исключение, а результатом будет промежуточный остаток от деления. В некоторых задачах бывает достаточно и этого, но если мы хотим всё-таки получить остаток от деления, можно вызвать команду повторно -
FABS
- взять модуль числа -
FCHS
- изменить знак (аналог командыNEG
, не имеющий никакого отношения к дополнительному коду; команда просто меняет знаковый бит числа) -
FRNDINT
- округлить до целого -
FSCALE
- масштабировать по степеням двойки (ST
(0) умножается на 2^ST
(1)). Экспонента меняется, а мантисса остаётся прежней. -
FXTRACT
- извлечь мантиссу и экспоненту.ST
(0) разделяется на мантиссу и экспоненту, которые записываются в разные регистры. Экспонента (как целое число) останется на вершине стека, а в следующий регистр стека будет записана мантисса (как целое число). -
FSQRT
- аппаратно вычисляет квадратный кореньST
(0)
-
FCOM
,FCOMP
,FCOMPP
- сравнить и вытолкнуть из стека. PP в конце команды означает выборку из стека двух операндов. -
FUCOM
,FUCOMP
,FUCOMPP
- сравнить только мантиссы (без учёта порядков) и вытолкнуть из стека. -
FICOM
,FICOMP
,FICOMP
- сравнить целые -
FCOMI
,FCOMIP
,FUCOMI
,FUCOMIP
(начиная с Pentium P6 и позднее) - занесение результатов сравнения в регистр EFLAGS, не используя командыFSTSW AX
/SAHF
-
FTST
- сравнивает с нулём -
FXAM
- выставляет флаги сопроцессора в соответствии с типом числа
Здесь подразумеваются тригонометрические и экспоненциальные операции, т.е. операции с иррациональными числами.
OFFTOP от автора:
Числа, не удовлетворяющие никакому алгебраическому уравнению с целыми коэффициентами,
называются трансцендентальными. π=3,141592... и e=2,71828... - трансцендентальные числа.
Если a и b – положительные алгебраические числа, то число loga(b) либо рациональное,
либо трансцендентальное. Числа log2(3), lg(5), ln(27) и тому подобные – трансцендентальны.
Десятичный логарифм любого целого числа, не изображаемого единицей с нулями (1, 10, 100,
0.1, 0.01) – трансцендентальное число.
Трансцендентальные функции – это аналитические функции, не являющиеся алгебраическими.
К элементарным трансцендентным функциям относятся:
- тригонометрические (sin, cos, tg,...);
- обратные тригонометрические (arcsin, arccos, arctg,...);
- логарифмические функции (log2x, lg(x), ln(x));
- показательные функции (xy, 2y, 10x, ex);
- гиперболические функции (sh, ch, th, ...);
- обратные гиперболические (arcsh, arch, arcth, ...);
FPU не реализует их все. В нём представлены только основные функции, которые
необходимы для вычисления всех остальных возможных.
-
FSIN
- синус -
FCOS
- косинус -
FSINCOS
- одновременно и синус, и косинус (в ST(0) помещается sin(ST
(0)), а вST
(1) - cos(ST
(0))) -
FPTAN
- тангенс -
FPATAN
- арктангенс -
F2XM1
– 2^x - 1 -
FYL2X
,FYL2XP1
– y * log2(x), y * log2(x+1)
Эти команды реализованы на таком низком уровне (аппаратный уровень сопроцессора), так как находят широкое применение в различных областях: компьютерная графика, физические расчёты и т.д.
Команды загрузки на вершину стека сопроцессора готовых констант:
-
FLD1
- 1,0 -
FLDZ
- +0,0 -
FLDPI
- число Пи -
FLDL2E
- log2(e) -
FLDL2T
- log2(10) -
FLDLN2
– ln(2) -
FLDLG2
– lg(2)
-
FINCSTP
,FDECSTP
- увеличить/уменьшить указатель вершины стека (не задействуя команды записи/чтения) -
FFREE
- освободить регистр (просто пометить освобождённым) -
FINIT
,FNINIT
- инициализировать сопроцессор / инициализировать без ожидания (очистка данных, инициализацияCR
иSR
по умолчанию). Эти команды сбрасывают регистры, помечают их как пустые и позволяют использовать их не задумываясь о том, что там остался мусор от других программ. -
FCLEX
,FNCLEX
- обнулить флаги исключений / обнулить без ожидания -
FSTCW
,FNSTCW
- сохранитьCR
в переменную / сохранить без ожидания -
FLDCW
- загрузитьCR
-
FSTENV
,FNSTENV
– сохранить вспомогательные регистры (14/28 байт) / сохранить без ожидания -
FLDENV
- загрузить вспомогательные регистры -
FSAVE
,FNSAVE
,FXSAVE
- сохранить состояние FPU целиком (94/108 байт) и инициализировать, аналогичноFINIT
-
FRSTOR
,FXRSTOR
- восстановить состояние FPU целиком.
Сохранение состояния FPU необходимо в многозадачных ОС. Если несколько программ выполняются одновременно, ОС при переключении между процессами помимо сохранения состояния текущей задачи в сегмент TSS
будет ещё и сохранять состояние сопроцессора, чтобы программы, которые работают с FPU не мешали друг другу.
-
FSTSW
,FNSTSW
- сохранениеCR
-
WAIT
,FWAIT
- обработка исключений -
FNOP
- отсутствие операции
С появлением FPU у нас в распоряжении стало намного больше регистров. И вот
мы написали какую-то программу, передали на другой компьютер. А как она узнает,
что в есть конкретно в этом процессоре?
Команда CPUID
отвечает за идентификацию процессора:
- Если
EAX
= 0, то вEAX
- максимальное допустимое значение (1 или 2), аEBX:ECX:EDX
12-байтный идентификатор производителя (ASCII-строка). - Если
EAX
= 1, то вEAX
- версия, вEDX
- информация о расширениях-
EAX
- модификация, модель, семейство -
EDX
: наличие FPU, поддержка V86, поддержка точек останова, наличие/отсутствие управляющего регистраCR4
, доступность расширенной физической адресации - Physical Address Extension (PAE),APIC
, быстрые системные вызовы,PGE
, машинно-специфичный регистр,CMOVcc
, наличие расширений MMX, FXSR (MMX2), SSE и т.д.
-
- Если
EAX
= 2, то вEAX
,EBX
,ECX
,EDX
возвращается информация о кэшах и TLB (какие кеши/механизмы кеширования поддерживает процессор и какие возможности есть у TLB)
Увеличение эффективности обработки больших потоков (однотипных) данных (изображения, звук, видео...) - Выполнение простых операций над массивами однотипных чисел.
Например, мы хотим обработать какую-то картинку (поменять яркость или как-то
проанализировать...). Простейшая растровая картинка представляет собой большой
поток (массив) байт, каждый из которых нам надо проанализировать. Если мы будем
читать из памяти отдельно каждый байт (картинка может храниться на диске или уже
может быть считана в оперативную память), то это будет работать очень долго. Как раз
для ускорения подобных потоковых операций и была создана группа расширений процессора,
первым из которых мы рассмотрим MMX.
- Не предусматривает собственных регистров, а разделяет их с FPU. При этом задействуются только 64 из 80 бит (биты мантиссы), которые содержат регистры FPU.
- Восемь 64-битных регистров
MM0
..MM7
- мантиссы регистров FPU (регистрыMM0
..MM7
буквально являются именованными частямиR0
..R7
). При записи вMMn
экспонента и знаковый бит заполняются единицами (чтобы из сопроцессора случайно не получить что-то нехорошее). - Пользоваться одновременно и FPU, и MMX не получится, требуется
FSAVE
+FRSTOR
(переключение между работой с каждым из них). - Типы данных MMX:
- учетверённое слово (16 * 4 = 64 бита);
- упакованные двойные слова (2 независимых части (слова) длиной по 32 бита);
- упакованные слова (4 независимых части (слова) длиной по 16 бита);
- упакованные байты (8 независимых байтов).
- С точки зрения представления данных в памяти разницы между этими типами данных нет, все занимают по 64 бит. Разница между этими типами данных состоит в том, что для работы с каждым из них предусмотрен свой набор команд (операций), которые интерпретируют значения в регистрах в зависимости от типа данных, с которым они работают.
- Команды MMX перемещают упакованные данные в оперативную память или обычные регистры целиком, но арифметические и логические операции выполняют поэлементно - в этом и заключается суть потоковой обработки данных: в относительно большие 64-битные регистры помещается сразу 8 байт (элементов) и одной командой можно сделать что-то сразу со всеми байтами.
- Насыщение - замена переполнения/антипереполнения превращением в максимальное/минимальное значение.
Зачем это нужно? Вернёмся к примеру с растровой картинкой.
Допустим, мы хотим увеличить яркость каких-то пикселей. Пиксель с самой
максимальной яркостью будет иметь белый цвет (код - все единички). И нам
важно не перейти этот порог в процессе обработки изображения (потока байт),
то есть нельзя допускать переполнения в командах MMX (иначе при переполнении
мы получим из белого пикселя чёрный). И тут нам на помощь приходит понятие
насыщения, и всё начинает обрабатываться так, как мы хотим.
-
MOVD
,MOVQ
- пересылка двойных/учетверённых слов -
PACKSSWB
,PACKSSDW
- упаковка (уплотнение) со знаковым насыщением слов в байты/двойных слов в слова. Приёмник -> младшая половина приёмника, источник -> старшая половина приёмника -
PACKUSWB
- упаковка (уплотнение) слов в байты с беззнаковым насыщением -
PUNPCKHBW
,PUNPCKHWD
,PUNPCKHDQ
- распаковка и объединение старших элементов источника и приёмника через 1 бит (в итоге мы получаем что-то среднее между двумя значениями - ?). Работают с двумя регистрами, а результат записывают в один регистр.
Зависят от типов данных, с которыми мы хотим работать.
-
PADDB
,PADDW
,PADDD
- поэлементное сложение, перенос игнорируется -
PADDSB
,PADDSW
- сложение с насыщением (для двойных слов соответствующая команда отсутствует) -
PADDUSB
,PADDUSW
- беззнаковое сложение с насыщением -
PSUBB
,PSUBW
,PDUBD
- вычитание, заём игнорируется -
PSUBSB
,PSUBSW
- вычитание с насыщением -
PSUBUSB
,PSUBUSW
- беззнаковое вычитание с насыщением -
PMILHW
,PMULLW
- старшее/младшее умножение (раздельно сохраняет старшую или младшую части результата в приёмник) -
PMADDWD
- умножение и сложение. Перемножает 4 слова, затем попарно складывает произведения двух старших и двух младших слов, получая одно число в качестве результата.
-
PCMPEQB
,PCMPEQW
,PCMPEQD
- проверка на равенство. Если пара равна - соответствующий элемент приёмника заполняется единицами, иначе - нулями -
PCMPGTB
,PCMPGTW
,PCMPGTD
- сравнение. Если элемент приёмника больше, то заполняется единицами, иначе - нулями
Нет разделения команд по типам данных, с которыми они работают, потому что логическим (побитовым) операциям всё равно, какую интерпретацию имеют её операнды.
-
PAND
- логическое И -
PANDN
- логическое НЕ-И (штрих Шеффера) (источник * НЕ(приёмник)) -
POR
- логическое ИЛИ -
PXOR
- исключающее ИЛИ
-
PSLLW
,PSLLD
,PSLLQ
- логический сдвиг влево -
PSRLW
,PSRLD
,PSRLQ
- логический сдвиг вправо -
PSRAW
,PSRAD
- арифметический сдвиг вправо
Отсутствует команда для арифметического сдвига влево, так как это то же самое, что и логический сдвиг влево.
Отсутствуют команды для сдвига байтов.
Дополнение функционала MMX, решение проблемы параллельной работы с FPU
- Восемь 128-разрядных регистров (теперь они никак не пересекаются с FPU, т.е. можно использовать SSE и FPU параллельно)
- Свой регистр флагов
- Основной тип - вещественные одинарной точности (32 бита) - float в Си
- Целочисленные команды работают с регистрами MMX
- Команды (больше, чем в MMX):
- Пересылки
- Арифметические
- Сравнения
- Преобразования типов
- Логические
- Целочисленные
- Упаковки
- Управления состоянием
- Управления кэшированием
- Развитие: SSE2, SSE3...
Расширение для криптографических преобразований, в частности для блочного шифрования.
Шифрование - преобразование информации с целью сокрытия от тех, кто не должен
её получить. Информация преобразуется так, что злоумышленник, даже зная алгоритм,
не сможет получить доступ к информации, не зная ключа. Алгоритмы шифрования
разрабатываются так, чтобы взлом был сопряжён с максимальной вычислительной
сложностью, т.е. подобрать ключ за какое-то разумное время было невозможно
(даже если в распоряжении злоумышленника есть суперкомпьютер). Отсюда алгоритмы
шифрования получаются достаточно сложными в реализации. Соответственно, их надо
пытаться оптимизировать на аппаратном уровне, так как шифрование сейчас
применяется повсеместно (особенно в интернете: при выходе в сеть шифруется
практически вся информация, которой мы обмениваемся). Поэтому соответствующие
расширения для ускорения работы алгоритмов шифрования стали добавлять в
современные процессоры.
- Команды:
- раунда шифрования;
- раунда расшифровывания;
- способствования генерации ключа