КулЛиб - Классная библиотека! Скачать книги бесплатно 

Профессиональное программирование на ассемблере x64 с расширениями AVX, AVX2 и AVX-512 [Даниэль Куссвюрм] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
Даниэль Куссвюрм

Профессиональное
программирование
на ассемблере x64
с расширениями
AVX, AVX2 и AVX-512

Modern X86 Assembly
Language Programming
Covers x86 64-bit, AVX, AVX2,
and AVX-512
Second Edition
Daniel Kusswurm

Профессиональное
программирование
на ассемблере x64
с расширениями
AVX, AVX2 и AVX-512
Даниэль Куссвюрм

Москва, 2021

УДК 004.4
ББК 32.972
K94

К94

Даниэль Куссвюрм
Профессиональное программирование на ассемблере x64 с расшире­
ниями AVX, AVX2 и AVX-512 / пер. с анг. В. С. Яценкова. – М.: ДМК Пресс,
2021. – 628 с.: ил.
ISBN 978-5-97060-928-6
В книге рассматривается программирование для 64-разрядной архитектуры
x86 и использование расширенного набора векторных команд (AVX). Изучив
этот материал, вы сможете кодировать быстродействующие функции и алго­
ритмы с использованием 64-разрядного языка ассемблера x86 и расширений
набора команд AVX, AVX2 и AVX-512.
Примеры исходного кода разработаны с использованием Visual Studio C++
и MASM; для их запуска рекомендуется ПК на базе x86 с 64-разрядной ОС
Windows 10 и процессором, поддерживающим AVX.
Предполагается, что читатели имеют опыт программирования на языках
высокого уровня и базовые знания C++.
Книга предназначена разработчикам, которые хотят научиться писать код
с использованием языка ассемблера x64.

УДК 004.4
ББК 32.972
Modern X86 Assembly Language Programming; Covers x86 64-bit, AVX, AVX2, and
AVX-512 by Daniel Kusswurm, edition: 2
This edition has been translated and published under licence from APress Media, LLC, part
of Springer Nature.
APress Media, LLC, part of Springer Nature takes no responsibility and shall not be made
liable for the accuracy of the translation.
Все права защищены. Любая часть этой книги не может быть воспроизведена
в какой бы то ни было форме и какими бы то ни было средствами без письменного раз­
решения владельцев авторских прав.

ISBN (анг.) 978-1-4842-4062-5
ISBN (рус.) 978-5-97060-928-6

© 2018 by Daniel Kusswurm
© Оформление, издание, перевод, ДМК Пресс, 2021

Оглавление
Предисловие от издательства. ..................................................................11
Об авторе............................................................................................................12
О техническом редакторе. ..........................................................................13
Благодарности..................................................................................................14
Вступление.........................................................................................................15
О чем эта книга................................................................................................16
Глава 1. Архитектура ядра x86-64...........................................................19
1.1. Исторический обзор...................................................................................19
1.2. Типы данных...............................................................................................22
1.2.1. Основные типы данных.......................................................................22
1.2.2. Числовые типы данных.......................................................................24
1.2.3. Типы данных SIMD..............................................................................24
1.2.4. Прочие типы данных...........................................................................26
1.3. Внутренняя архитектура............................................................................26
1.3.1. Регистры общего назначения.............................................................27
1.3.2. Регистр RFLAGS ...................................................................................29
1.3.3. Указатель команд.................................................................................31
1.3.4. Операнды команд................................................................................32
1.3.5. Адресация памяти...............................................................................33
1.4. Различия между программированием x86-64 и x86-32...........................35
1.4.1. Недопустимые команды......................................................................37
1.4.2. Устаревшие команды ..........................................................................38
1.5. Обзор набора команд..................................................................................38
1.6. Заключение.................................................................................................41

Глава 2. Программирование ядра x86-64. Часть 1...........................42
2.1. Простая целочисленная арифметика........................................................42
2.1.1. Сложение и вычитание........................................................................43
2.1.2. Логические операции..........................................................................46
2.1.3. Операции сдвига..................................................................................49
2.2. Расширенная целочисленная арифметика...............................................53
2.2.1. Умножение и деление..........................................................................53
2.2.2. Вычисления с использованием смешанных типов...........................57

6



Оглавление

2.3. Команды адресации памяти и состояния.................................................63
2.3.1. Режимы адресации памяти.................................................................63
2.3.2. Условные команды ..............................................................................68
2.4. Заключение.................................................................................................72

Глава 3. Программирование ядра x86-64. Часть 2 ..........................74
3.1. Массивы.......................................................................................................74
3.1.1. Одномерные массивы..........................................................................74
3.1.2. Двумерные массивы............................................................................81
3.3. Строки..........................................................................................................94
3.3.1. Подсчет символов................................................................................94
3.3.2. Конкатенация строк.............................................................................97
3.3.3. Сравнение массивов..........................................................................103
3.3.4. Обращение массива...........................................................................106
3.4. Заключение...............................................................................................110

Глава 4. Векторное расширение набора команд AVX....................111
4.1. Обзор AVX..................................................................................................111
4.2. Концепции программирования SIMD.....................................................113
4.3. Арифметика с переносом или арифметика с насыщением?.................114
4.4. Среда выполнения AVX............................................................................116
4.4.1. Набор регистров.................................................................................116
4.4.2. Типы данных......................................................................................117
4.4.3. Синтаксис команд..............................................................................118
4.5. Скалярные вычисления AVX с плавающей запятой...............................119
4.5.1. Концепция программирования с плавающей запятой...................119
4.5.2. Набор скалярных регистров с плавающей запятой.........................123
4.5.3. Регистр управления и состояния......................................................123
4.5.4. Обзор набора команд.........................................................................125
4.6. Операции с упакованными числами с плавающей запятой в AVX.......126
4.6.1. Обзор набора команд.........................................................................129
4.7. Операции с упакованными целыми числами в AVX...............................130
4.7.1. Обзор набора команд.........................................................................131
4.8. Различия между x86-AVX и x86-SSE........................................................133
4.9. Заключение...............................................................................................135

Глава 5. Программирование AVX – cкалярные вычисления
с плавающей запятой..................................................................................137
5.1. Скалярная арифметика с плавающей запятой.......................................138
5.1.1. Арифметика с плавающей запятой одинарной точности...............138
5.1.1. Арифметика с плавающей запятой двойной точности...................141
5.2. Скалярные сравнения и преобразования с плавающей запятой..........146
5.2.1. Сравнение с плавающей запятой.....................................................147
5.2.2. Преобразования чисел с плавающей запятой.................................156

  7
5.3. Скалярные массивы и матрицы с плавающей запятой.........................163
5.3.1. Массивы значений с плавающей запятой........................................163
5.3.2. Матрицы значений с плавающей запятой.......................................167
5.4. Соглашение о вызовах..............................................................................171
5.4.1. Базовые фреймы стека......................................................................172
5.4.2. Использование энергонезависимых регистров
общего назначения.....................................................................................176
5.4.3. Использование энергонезависимых регистров XMM.....................181
5.4.4. Макросы для прологов и эпилогов...................................................187
5.5. Заключение...............................................................................................194

Глава 6. Программирование AVX – упакованные числа
с плавающей запятой..................................................................................196
6.1. Упакованная арифметика с плавающей запятой...................................196
6.2. Сравнение упакованных чисел с плавающей запятой...........................203
6.3. Преобразования упакованных чисел с плавающей запятой.................208
6.4. Массивы упакованных чисел с плавающей запятой..............................213
6.4.1. Квадратные корни из упакованных чисел с плавающей запятой......213
6.4.2. Поиск минимального и максимального значений массива
упакованных значений с плавающей запятой..........................................217
6.4.3. Наименьшие квадраты упакованных чисел
с плавающей запятой..................................................................................222
6.5. Упакованные матрицы значений с плавающей запятой.......................228
6.5.1. Транспонирование матрицы............................................................229
6.5.2. Умножение матриц............................................................................236
6.6. Заключение...............................................................................................242

Глава 7. Программирование AVX –
упакованные целые числа........................................................................244
7.1. Сложение и вычитание упакованных целых чисел................................244
7.2. Сдвиг упакованных целых чисел..............................................................250
7.3. Умножение упакованных целых чисел....................................................255
7.4. Обработка изображений с применением упакованных целых чисел...261
7.4.1. Минимальные и максимальные значения пикселей.......................262
7.4.2. Средняя интенсивность пикселей.....................................................270
7.4.3. Преобразования пикселей.................................................................275
7.4.4. Гистограммы изображений...............................................................283
7.4.5. Пороговая обработка изображений..................................................290
7.5. Заключение................................................................................................302

Глава 8. Подробнее про AVX2..................................................................304
8.1. Среда выполнения AVX2...........................................................................304
8.2. Команды AVX2 для упакованных чисел с плавающей запятой.............305
8.3. Команды AVX2 для упакованных целых чисел.......................................307

8 

Оглавление

8.4. Расширения набора команд X86..............................................................308
8.4.1. Числа с плавающей запятой половинной точности........................308
8.4.2. Слитное умножение-сложение (FMA)..............................................309
8.4.3. Расширения набора команд для регистров общего назначения..... 311
8.5. Заключение...............................................................................................312

Глава 9. Программирование AVX2 – упакованные числа
с плавающей запятой..................................................................................314
9.1. Арифметика упакованных чисел с плавающей запятой........................315
9.2. Массивы упакованных чисел с плавающей запятой..............................321
9.2.1. Простые вычисления.........................................................................321
9.2.2. Среднее арифметическое значение столбца...................................328
9.2.3. Коэффициент корреляции.................................................................334
9.3. Умножение и транспонирование матриц...............................................341
9.4. Обращение матриц...................................................................................349
9.5. Команды смешивания и перестановки...................................................361
9.6. Команды извлечения данных..................................................................367
9.7. Заключение................................................................................................373

Глава 10. Программирование AVX2 –
упакованные целые числа........................................................................375
10.1. Основные операции над упакованными целыми числами.................375
10.1.1. Основные арифметические операции...........................................376
10.1.2. Упаковка и распаковка....................................................................380
10.1.3. Увеличение размера.........................................................................386
10.2. Обработка изображений с упакованными
целочисленными пикселями .........................................................................391
10.2.1. Усечение пикселей...........................................................................391
10.2.2. Поиск минимального и максимального значений RGB................396
10.2.3. Преобразование RGB в оттенки серого..........................................403
10.3. Заключение..............................................................................................411

Глава 11. Программирование AVX2 –
расширенные команды..............................................................................412
11.1. Программирование операций FMA.......................................................412
11.1.1. Свертки.............................................................................................413
11.1.2. Скалярные операции FMA...............................................................415
11.1.3. Операции FMA с упакованными операндами...............................424
11.2. Команды для работы с регистрами общего назначения......................431
11.2.1. Бесфлаговое умножение и сдвиги...................................................432
11.2.2. Расширенные манипуляции битами..............................................436
11.3. Преобразования с плавающей запятой половинной точности...........440
11.4. Заключение..............................................................................................443

  9

Глава 12. Система векторных команд AVX-512................................445
12.1. Обзор AVX-512.........................................................................................445
12.2. Среда выполнения AVX-512...................................................................446
12.2.1. Наборы регистров............................................................................ 447
12.2.2.Типы данных.....................................................................................448
12.2.3. Синтаксис команды.........................................................................448
12.3. Обзор набора команд..............................................................................452
12.3.1. AVX512F............................................................................................453
12.3.2. AVX512CD..........................................................................................455
12.3.3. AVX512BW.........................................................................................456
12.3.4. AVX512DQ.........................................................................................456
12.3.5. Регистры маски операции...............................................................457
12.4. Заключение..............................................................................................458

Глава 13. Программирование AVX-512 –
числа с плавающей запятой.....................................................................459
13.1. Скалярные операнды с плавающей точкой..........................................459
13.1.1. Маскирование слиянием.................................................................460
13.1.2. Маскирование нулем.......................................................................463
13.1.3. Округление на уровне команды......................................................466
13.2. Упакованные числа с плавающей запятой............................................470
13.2.1. Арифметика упакованных чисел с плавающей запятой...............471
13.2.2. Сравнение упакованных чисел с плавающей запятой..................478
13.2.3. Средние значения столбца упакованных чисел
с плавающей запятой..................................................................................483
13.2.4. Векторные перекрестные произведения.......................................491
13.2.5. Умножение матрицы на вектор......................................................501
13.2.6. Свертки.............................................................................................512
13.3. Заключение..............................................................................................516

Глава 14. Программирование AVX-512 –
упакованные целые числа........................................................................517
14.1. Базовая арифметика...............................................................................517
14.2. Обработка изображений.........................................................................523
14.2.1. Пиксельные преобразования..........................................................523
14.2.2. Пороговая обработка изображений................................................530
14.2.3. Статистика изображений................................................................535
14.2.4. Преобразование формата RGB в оттенки серого...........................546
14.3. Заключение..............................................................................................553

Глава 15. Стратегии и методы оптимизации......................................555
15.1. Микроархитектура процессора..............................................................555
15.1.1. Обзор архитектуры процессора......................................................556

10

 Оглавление
15.1.2. Функциональная схема конвейера микроархитектуры................557
15.1.3. Механизм выполнения....................................................................560
15.2. Оптимизация кода на языке ассемблера...............................................561
15.2.1. Основные методы............................................................................562
15.2.2. Операции с плавающей запятой.....................................................563
15.2.3. Ветвление программы.....................................................................564
15.2.4. Выравнивание данных.....................................................................565
15.2.5. Методы SIMD....................................................................................566
15.3. Заключение..............................................................................................567

Глава 16. Продвинутое программирование.......................................568
16.1. Команда CPUID........................................................................................568
16.2. Постоянные хранилища в памяти.........................................................584
16.3. Предварительная выборка данных........................................................589
16.4. Многопоточность....................................................................................596
16.5. Заключение..............................................................................................609

Приложение. ...................................................................................................611
П.1. Программные утилиты для процессоров x86.........................................611
П.2. Visual Studio..............................................................................................611
П.2.1. Запуск примера исходного кода.......................................................612
П.2.2. Создание проекта Visual Studio C ++................................................612
П.3. Основные и дополнительные материалы..............................................620
П.3.1. Справочные руководства по программированию на X86..............620
П.3.2. Справочные материалы по x86 и микроархитектуре.....................621
П.3.3. Вспомогательные ресурсы................................................................622
П.3.4. Ссылки на ресурсы по алгоритмам..................................................622
П.3.5. Ссылки на ресурсы по C ++...............................................................623

Предметный указатель...............................................................................624

Предисловие от издательства
Отзывы и пожелания
Мы всегда рады отзывам наших читателей. Расскажите нам, что вы думаете об
этой книге – что понравилось или, может быть, не понравилось. Отзывы важны
для нас, чтобы выпускать книги, которые будут для вас максимально полезны.
Вы можете написать отзыв на нашем сайте www.dmkpress.com, зайдя на
страницу книги и оставив комментарий в разделе «Отзывы и рецензии». Также
можно послать письмо главному редактору по адресу dmkpress@gmail.com;
при этом укажите название книги в теме письма.
Если вы являетесь экспертом в какой-либо области и заинтересова­
ны в написании новой книги, заполните форму на нашем сайте по адресу
http://dmkpress.com/authors/publish_book/ или напишите в издательство по
адресу dmkpress@gmail.com.

Список опечаток
Хотя мы приняли все возможные меры для того, чтобы обеспечить высокое
качество наших текстов, ошибки все равно случаются. Если вы найдете ошибку
в одной из наших книг – возможно, ошибку в основном тексте или программ­
ном коде, – мы будем очень благодарны, если вы сообщите нам о ней. Сделав
это, вы избавите других читателей от недопонимания и поможете нам улуч­
шить последующие издания этой книги.
Если вы найдете какие-либо ошибки в коде, пожалуйста, сообщите о них
главному редактору по адресу dmkpress@gmail.com, и мы исправим это
в следующих тиражах.

Нарушение авторских прав
Пиратство в интернете по-прежнему остается насущной проблемой. Изда­
тельство «ДМК Пресс» очень серьезно относится к вопросам защиты автор­
ских прав и лицензирования. Если вы столкнетесь в интернете с незаконной
публикацией какой-либо из наших книг, пожалуйста, пришлите нам ссылку на
интернет-ресурс, чтобы мы могли применить санкции.
Ссылку на подозрительные материалы можно прислать по адресу
dmkpress@gmail.com.
Мы высоко ценим любую помощь по защите наших авторов, благодаря кото­
рой мы можем предоставлять вам качественные материалы.

Об авторе
Даниэль Куссвюрм более 30 лет занимается раз­
работкой программного обеспечения и инфор­
мационными технологиями. За свою карьеру он
разработал инновационное программное обеспе­
чение для различного медицинского и научного
оборудования и приложения для обработки изо­
бражений. Во многих из этих проектов он успешно
использовал язык ассемблера x86 для значитель­
ного повышения быстродействия ресурсоемких
алгоритмов и решения уникальных задач про­
граммирования. Он получил степень бакалавра
электроники и электротехники в Университете
Северного Иллинойса, а также степень магистра
и доктора информатики в Университете ДеПола.

О техническом редакторе
Пол Коэн пришел в корпорацию Intel на самых
первых этапах развития архитектуры x86, начиная
с 8086, и ушел из Intel после 26 лет работы в сфере
продаж, маркетинга и управления. В настоящее
время он является партнером Douglas Technology
Group и специализируется на написании техниче­
ской литературы по заказу Intel и других корпора­
ций. Пол также совместно с Академией молодых
предпринимателей (YEA) ведет учебный курс, ко­
торый превращает учеников средних и старших
классов в настоящих, уверенных в себе предпри­
нимателей. Он также является комиссаром по во­
просам дорожного движения города Бивертон, штат Орегон, и входит в совет
директоров нескольких некоммерческих организаций.

Благодарности
Производство фильма и издание книги в чем-то похожи. Трейлеры к фильму
чествуют актеров, играющих главных героев. На обложке книги звучат имена
авторов. Актеры и авторы в конечном итоге получают признание публики за
свои усилия. Однако невозможно создать фильм или опубликовать книгу без
преданности делу, опыта и творческого подхода профессиональной закадро­
вой команды. Эта книга не исключение.
Я хочу поблагодарить за прекрасную работу талантливый редакционный
коллектив Apress, особенно Стива Энглина, Марка Пауэрса и Мэтью Муди. Пол
Коэн заслуживает отдельной похвалы за его тщательный технический обзор
и практические предложения. Корректор Эд Куссвюрм заслуживает аплодис­
ментов и признательности за его упорный труд и конструктивные отзывы.
Я беру на себя полную ответственность за любые оставшиеся недостатки.
Я также хочу поблагодарить Нирмала Селвараджа, Дулси Нирмала, Кезию
Эндсли, Дханеша Кумара и весь редакционный персонал Apress за их вклад,
а моих профессиональных коллег за их одобрение и поддержку. Наконец, я хо­
тел бы поблагодарить родителей Армина (вечная ему память) и Мэри, а также
двоюродную сестру Мэри и двоюродных братьев Тома, Эда и Джона, которые
вдохновляли меня на написание этой книги.

Вступление
С момента изобретения персонального компьютера (ПК) разработчики про­
граммного обеспечения использовали язык ассемблера x86 для создания ин­
новационных решений применительно к широкому спектру алгоритмических
задач. В первые дни эры ПК было обычной практикой писать большие фраг­
менты программного кода или полные приложения с использованием языка
ассемблера x86. Учитывая преобладание в XXI веке языков высокого уровня,
таких как C++, C#, Java и Python, может быть удивительно узнать, что многие
разработчики программного обеспечения все еще используют язык ассемблера
для кодирования критически важных в плане быстродействия участков своих
программ. И хотя с годами компиляторы заметно улучшились с точки зрения
генерации машинного кода, который стал эффективнее по объему и произво­
дительности, все еще остаются ситуации, когда разработчику программного
обеспечения имеет смысл использовать преимущества программирования на
языке ассемблера.
Архитектура современных процессоров x86 с одним потоком команд и множеством потоков данных (SIMD, single instruction stream – multiple data stream)
является еще одним объяснением постоянного интереса к программирова­
нию на языке ассемблера. Процессор с поддержкой SIMD обладает вычисли­
тельными ресурсами, которые упрощают параллельные вычисления с исполь­
зованием нескольких значений данных, что может значительно повысить
быстродействие приложений, обеспечивающих высокую скорость отклика
в реальном времени. Архитектуры SIMD также хорошо подходят для ресурсо­
емких проблемных областей, таких как обработка изображений, кодирование
аудио и видео, автоматизированное проектирование, компьютерная графика
и интеллектуальный анализ данных. К сожалению, многие языки высокого
уровня и инструменты разработки по-прежнему не могут полностью или хотя
бы частично использовать возможности SIMD современного процессора x86.
С другой стороны, язык ассемблера открывает разработчику программного
обеспечения полный доступ к ресурсам SIMD процессора.

О чем эта книга
Эта книга рассказывает о программировании на 64-битном (x86-64) языке
ассемблера x86. Содержание и структура книги призваны помочь вам быстро
освоить программирование на языке ассемблера x86-64 и вычислительные
ресурсы векторных расширений набора команд Advanced Vector Extensions
(AVX)1. Она также содержит множество примеров исходного кода, которые
способствуют ускоренному изучению и пониманию основных конструкций
языка ассемблера x86-64 и концепций программирования SIMD. После про­
чтения этой книги вы сможете кодировать быстродействующие функции
и алгоритмы с помощью языка ассемблера x86-64 и наборов инструкций
AVX, AVX2 и AVX-512.
Прежде чем продолжить, я должен обратить ваше внимание, что в этой книге не рассматривается программирование на языке ассемблера x86-32. В нем
также не обсуждаются устаревшие технологии x86, такие как модуль с плава­
ющей запятой x87, MMX и Streaming SIMD Extensions. Если вы заинтересованы
в изучении этих тем, прочтите первое издание книги (в переводе не издава­
лось – прим. перев.). Эта книга не объясняет архитектурные особенности x86
или привилегированные команды процессора, которые используются в опе­
рационных системах. Однако вам все равно придется досконально изучить
материал, представленный в данной книге, чтобы разрабатывать код на языке
ассемблера x86 для использования в операционной системе.
Хотя теоретически возможно написать целую прикладную программу, ис­
пользуя только язык ассемблера, высокие требования к разработке современ­
ного программного обеспечения делают такой подход непрактичным и не­
целесообразным. Вместо этого в данной книге основное внимание уделяется
кодированию функций языка ассемблера x86-64, вызываемых из C++. Каждый
пример исходного кода был создан с использованием Microsoft Visual Studio
C++ и Microsoft Macro Assembler (MASM).

Целевая аудитория
Целевая аудитория этой книги – разработчики программного обеспечения,
в том числе:
 разработчики, которые создают прикладные программы для платформ
на базе Windows и хотят научиться писать алгоритмы и функции высоко­
го быстродействия с использованием языка ассемблера x86-64;
 разработчики, которые создают прикладные программы для сред, отлич­
ных от Windows, и хотят изучить программирование на языке ассемб­
лера x86-64;
1

Расширение системы команд x86 для микропроцессоров Intel и AMD, предложенное
Intel в марте 2008. – Прим. перев.

О чем эта книга  17
 разработчики, которые хотят научиться создавать вычислительные
функции SIMD с использованием наборов команд AVX, AVX2 и AVX-512;
 разработчики и студенты, изучающие информатику, которые хотят до­
биться глубокого понимания платформы x86-64 и ее архитектуры SIMD.
Основная аудитория данной книги – это разработчики программного обес­
печения на базе Windows, поскольку примеры исходного кода были разработа­
ны с использованием Visual Studio C++ и MASM. Разработчики, ориентирован­
ные на платформы, отличные от Windows, также могут извлечь пользу из этой
книги, поскольку большая часть информативного контента излагается незави­
симо от какой-либо конкретной операционной системы. Предполагается, что
читатели данной книги уже имеют опыт программирования на языках высо­
кого уровня и базовые знания C++. Знакомство с программированием в Visual
Studio или Windows PowerShell не требуется.

Обзор содержания
Основная цель этой книги – помочь вам изучить программирование на 64-бит­
ном языке ассемблера x86, а также набор команд AVX, AVX2 и AVX-512. Гла­
вы и содержание книги построены таким образом, чтобы достичь этой цели.
Озна­комьтесь с кратким обзором того, что вы найдете в этой книге.
В главе 1 рассматривается основная архитектура платформы x86-64. Она
включает в себя описание основных типов данных платформы, внутренней
архитектуры, наборов регистров, операндов команд и режимов адресации па­
мяти. В этой главе также описывается основной набор команд x86-64. В гла­
вах 2 и 3 объясняются основы программирования на языке ассемблера x86-64
с использованием базового набора команд и общих программных конструк­
ций, включая массивы и структуры. Примеры исходного кода, представленные
в этих (и последующих) главах, оформлены как рабочие программы, которые
в процессе обучения вы можете запускать, изменять или иным образом экс­
периментировать с кодом.
Глава 4 посвящена архитектурным ресурсам AVX, включая наборы регистров,
типы данных и набор команд. В главе 5 объясняется, как использовать набор
команд AVX для выполнения скалярных арифметических операций с плава­
ющей запятой, со значениями как с одинарной, так и с двойной точностью.
В главах 6 и 7 рассказано про программирование AVX SIMD с использованием
упакованных операндов, как целочисленных, так и с плавающей запятой.
Глава 8 знакомит с AVX2 и исследует его расширенные возможности, включая
широковещательную передачу, сбор и перестановку данных. Здесь также объяс­
няются операции слитного умножения-сложения (FMA, fused multiply-add). Главы 9
и 10 содержат примеры исходного кода, которые иллюстрируют различные вы­
числительные алгоритмы, использующие AVX2 с упакованными операндами. Гла­
ва 11 включает примеры исходного кода, демонстрирующие программирование
FMA. В этой главе также рассмотрены примеры, иллюстрирующие недавние рас­
ширения платформы x86 с использованием регистров общего назначения.
В главе 12 детально рассмотрен набор команд AVX-512. В этой главе описаны
наборы регистров и типы данных AVX-512. В ней также рассмотрены основ­
ные усовершенствования AVX-512, включая условное выполнение и слияние,
встроенные широковещательные операции и округление на уровне команд.

18

 О чем эта книга

В главах 13 и 14 содержится множество примеров исходного кода, демонстри­
рующих, как использовать эти расширенные функции.
В главе 15 представлен обзор современного многоядерного процессора x86
и лежащей в его основе микроархитектуры. В этой главе также описаны кон­
кретные стратегии и методы программирования, которые можно использо­
вать для повышения быстродействия кода на языке ассемблера x86. В главе 16
приведен обзор нескольких примеров исходного кода, иллюстрирующего пе­
редовые методы программирования на языке ассемблера x86, включая обна­
ружение функций процессора, ускоренный доступ к памяти и многопоточные
вычисления.
В приложении А рассказано, как выполнить примеры исходного кода с по­
мощью Visual Studio и MASM. Оно также содержит список ссылок и ресурсов,
к которым вы можете обратиться для получения дополнительной информации
о программировании на языке ассемблера x86.

Исходный код примеров
Примеры исходного кода этой книги доступны на веб-сайте Apress по адресу
https://www.apress.com/us/book/9781484240625. Для каждой главы есть ZIPархив, содержащий файлы исходного кода C++ и ассемблера, а также файлы
проекта Visual Studio. Процедура установки не требуется. Вы можете просто
извлечь содержимое ZIP-архива главы в папку по вашему выбору.
Внимание! Единственное назначение исходного кода – показать примеры программирования, которые напрямую связаны с темами, обсуждаемыми в этой книге.
В этих примерах уделяется минимальное внимание важным проблемам разработки
программного обеспечения, таким как надежная обработка ошибок, риски безопасности, вычислительная стабильность, ошибки округления или плохо согласованные
функции. Вы принимаете на себя всю ответственность за решение этих проблем,
если решите использовать исходный код каких-либо примеров в своих собственных
программах.
Примеры исходного кода были созданы с помощью Visual Studio Professional
2017 (версия 15.7.1) на ПК с 64-разрядной Windows 10 Pro. На веб-сайте Visual
Studio (https://visualstudio.microsoft.com) содержится дополнительная информа­
ция об этом и других выпусках Visual Studio. Технические сведения об уста­
новке, настройке и разработке приложений Visual Studio доступны по адресу
https://docs.microsoft.com/en-us/visualstudio/?view=vs-2017.
Рекомендуемой аппаратной платформой для запуска примеров исходного
кода является ПК на базе x86 с 64-разрядной ОС Windows 10 и процессором,
поддерживающим AVX. Для запуска примеров исходного кода, в которых ис­
пользуются эти наборы команд, требуется процессор, совместимый с AVX2 или
AVX-512. Вы можете использовать одну из свободно доступных утилит, пере­
численных в приложении, чтобы определить, какие расширения набора ко­
манд x86-AVX поддерживает процессор вашего компьютера.

Глава

1
Архитектура ядра x86-64

В этой главе архитектура ядра x86-64 рассматривается с точки зрения приклад­
ной программы. Она открывается кратким историческим обзором платформы
x86, формирующим основу для понимания последующего материала. Затем
следует обзор основных типов данных – числовых и SIMD. Далее рассмотре­
на архитектура ядра x86-64, включая описания наборов регистров процессора,
флагов состояния, операндов команд и режимов адресации памяти. Глава за­
вершается обзором набора команд ядра x86-64.
В отличие от языков высокого уровня, таких как C и C++, программирование
на языке ассемблера требует от разработчика глубокого понимания конкрет­
ных архитектурных особенностей целевого процессора. Темы, обсуждаемые
в данной главе, соответствуют этому требованию и заложат основу для пони­
мания примеров кода, которые вы встретите далее в данной книге. В этой главе
также представлен базовый материал, необходимый для понимания расши­
ренных возможностей SIMD x86-64.

1.1. Исторический обзор
Прежде чем исследовать технические детали архитектуры ядра x86-64, полез­
но понять, как эта архитектура развивалась за минувшие годы. Следующий
краткий обзор посвящен заслуживающим внимания усовершенствованиям
процессоров и наборов команд, которые повлияли на то, как разработчики
программного обеспечения используют язык ассемблера x86. Читатели, кото­
рым интересна более полная история происхождения x86, должны обратиться
к ресурсам, перечисленным в приложении.
Платформа процессора x86-64 является расширением исходной платфор­
мы x86-32. Первым воплощением платформы x86-32 в кремнии был микро­
процессор Intel 80386, представленный в 1985 году. Модель 80386 расширила
архитектуру 16-битной 80286 за счет добавления 32-битных регистров и ти­
пов данных, режимов плоской модели памяти, 4 ГБ логического адресного
пространства и страничной организации памяти. Процессор 80486 превзошел
производительность 80386 за счет реализации кешей памяти на кристалле
и оптимизированных команд. В отличие от процессора 80386, который нуж­
дался во внешнем сопроцессоре 80387 (FPU, floating-point unit) для вычисле­
ний с плавающей запятой, большинство версий процессора 80486 также вклю­
чали интегрированный модуль x87 FPU.

20

 Архитектура ядра x86-64

Расширение платформы x86-32 продолжилось выпуском в 1993 году перво­
го процессора марки Pentium, представителя микроархитектуры P5. Улучше­
ния производительности включали конвейер выполнения сдвоенных команд,
64-битную внешнюю шину данных и отдельные кеши памяти на кристалле как
для кода, так и для данных. Более поздние версии (1997 г.) микроархитекту­
ры P5 получили новый вычислительный ресурс, так называемую технологию
MMX, которая поддерживает операции SIMD над упакованными целыми чис­
лами с использованием регистров разрядностью 64 бита. Упакованное целое
число (packed integer) – это совокупность нескольких целочисленных значений,
которые обрабатываются одновременно.
Микроархитектура P6, впервые использованная в Pentium Pro (1995 г.), а за­
тем в Pentium II (1997 г.), расширила платформу x86-32 за счет трехстороннего
суперскалярного конвейера. Это означает, что процессор может (в среднем) деко­
дировать, отправлять и выполнять три отдельные команды в течение каждого
тактового цикла. Другие усовершенствования P6 включали выполнение команд
вне очереди, улучшенные алгоритмы предсказания ветвлений и упреждающе­
го исполнения команд. Pentium III, также основанный на микроархитектуре P6,
был выпущен в 1999 году и получил поддержку новой технологии SIMD под на­
званием Streaming SIMD extension (SSE). Эта технология добавляет к платформе
x86-32 восемь 128-битных регистров и команды, которые реализуют упакован­
ную арифметику с плавающей запятой одинарной точности.
В 2000 году Intel представила новую микроархитектуру Netburst с техноло­
гией SSE2, которая расширила возможности SSE с плавающей запятой за счет
обработки упакованных значений двойной точности. SSE2 также включала до­
полнительные команды, позволяющие использовать 128-битные регистры SSE
для упакованных целочисленных вычислений и скалярных операций с плава­
ющей запятой. Линейка процессоров, основанных на архитектуре Netburst,
включала несколько вариантов Pentium 4. В 2004 году микроархитектура
Netburst была модернизирована и теперь включает в себя SSE3 и технологию
Hyper-Threading. SSE3 содержит новые команды операций с упакованными це­
лыми числами и числами с плавающей запятой для платформы x86, в то время
как технология Hyper-Threading распараллеливает конвейеры команд внеш­
него интерфейса процессора для повышения производительности. Процес­
соры с поддержкой SSE3 входят в 90-нм (и менее) версии линейки продуктов
Pentium 4 и Xeon.
В 2006 году Intel запустила в массовое производство новую микроархи­
тектуру под названием Core. Микроархитектура Core стала следствием глу­
бокой модернизации многих интерфейсных конвейеров и исполнительных
модулей Netburst с целью повышения производительности и снижения энер­
гопотребления. Она также содержит ряд улучшений SIMD, включая SSSE3
и SSE4.1. Эти расширения добавили на платформу новые команды для ра­
боты с упакованными целыми числами и числами с плавающей запятой, но
не добавили новых регистров или типов данных. К процессорам на основе
микроархитектуры Core относятся процессоры серий Core 2 Duo, Core 2 Quad
и Xeon 3000/5000.
Микроархитектура под названием Nehalem последовала за Core в конце
2008 года. Эта микроархитектура вернула платформе x86 гиперпоточность,

1.1. Исторический обзор  21
которая была исключена из микроархитектуры Core. Микроархитектура
Nehalem также поддерживает SSE4.2. Это последнее усовершенствование x86SSE добавило в набор команд x86-SSE несколько команд ускорителя для опре­
деленных приложений. SSE4.2 также содержит новые команды, упрощающие
обработку текстовых строк с использованием 128-битных регистров x86-SSE.
К процессорам на основе микроархитектуры Nehalem относятся процессоры
Core i3, i5 и i7 первого поколения. Она также включает процессоры серий Xeon
3000, 5000 и 7000.
В 2011 году Intel запустила новую микроархитектуру под названием Sandy
Bridge. В микроархитектуре Sandy Bridge появилась новая технология SIMD
x86 под названием Advanced Vector Extensions (AVX). AVX поддерживает опе­
рации над упакованными числами с плавающей запятой (как одинарной,
так и двойной точности) с использованием регистров шириной 256 бит. AVX
также поддерживает новый синтаксис команд с тремя операндами, повы­
шающий эффективность кода за счет уменьшения количества передач дан­
ных из регистра в регистр, которые должна выполнять функция программы.
Процессоры на базе микроархитектуры Sandy Bridge включают в себя про­
цессоры Core i3, i5 и i7 второго и третьего поколений, а также процессоры
серии Xeon V2.
В 2013 году Intel представила свою микроархитектуру Haswell. Haswell под­
держивает набор команд AVX2, который расширяет набор AVX командами
поддержки операций с упакованными целыми числами с использованием ре­
гистров шириной 256 бит. AVX2 также поддерживает расширенные возмож­
ности передачи данных с помощью команд широковещательной передачи,
сбора и перестановки. (Команды широковещательной передачи реплицируют
одно значение в несколько мест; команды сбора данных загружают несколько
элементов из несмежных ячеек памяти; команды перестановки переупоря­
дочивают элементы упакованного операнда.) Другой особенностью микроар­
хитектуры Haswell является включение в нее операции слитного умножениясложения (FMA, fused multiply-add). FMA позволяет программным алгоритмам
выполнять вычисления умножения-сложения (или скалярного произведения)
с использованием одной операции округления с плавающей запятой, что помо­
гает улучшить как быстродействие, так и точность. Микроархитектура Haswell
также включает в себя несколько новых команд для работы с регистрами обще­
го назначения. К процессорам на основе микроархитектуры Haswell относятся
процессоры Core i3, i5 и i7 четвертого поколения. AVX2 поддерживают и более
поздние поколения процессоров семейства Core, а также процессоры серий
Xeon V3, V4 и V5.
Доработки платформы x86 не ограничивались усовершенствованиями SIMD.
В 2003 году AMD представила свой процессор Opteron, который расширил ис­
полняющее ядро x86 с 32 до 64 бит. Intel последовала примеру AMD в 2004 году,
добавив практически такие же 64-разрядные расширения к своим процессо­
рам, начиная с определенных версий Pentium 4. Все процессоры Intel на базе
микроархитектур Core, Nehalem, Sandy Bridge, Haswell и Skylake поддерживают
исполняющее ядро x86-64.
Процессоры AMD также претерпели изменения за прошедшие годы. В 2003
году AMD представила серию процессоров на базе своей микроархитектуры

22

 Архитектура ядра x86-64

K8. Оригинальные версии K8 включали поддержку MMX, SSE и SSE2, в то вре­
мя как в более поздних версиях появилась поддержка SSE3. В 2007 году была
запущена микроархитектура K10, которая включала расширение SIMD под
названием SSE4a. Расширение SSE4a содержит несколько команд по сдвигу
маски и хранению потоковых данных, которые не поддерживают процессо­
ры Intel. Вслед за K10 в 2011 году AMD представила новую микроархитекту­
ру Bulldozer. Микроархитектура Bulldozer включает поддержку SSSE3, SSE4.1,
SSE4.2, SSE4a и AVX. Она также поддерживает FMA4 – версию слитного ум­
ножения-сложения с четырьмя операндами. Процессоры Intel не поддержи­
вают команды FMA4. Обновление 2012 года для микроархитектуры Bulldozer
под названием Piledriver включает поддержку как FMA4, так и трехоперанд­
ной версии FMA, которую некоторые утилиты для обнаружения функций ЦП
и сторонние документы именуют FMA3. Самая последняя микроархитектура
AMD, представленная в 2017 году, называется Zen (в 2018 году была пред­
ставлена микроархитектура Zen 2 – прим. перев.). Эта микроархитектура
включает усовершенствования набора команд AVX2 и используется в про­
цессорах серии Ryzen.
Высокопроизводительные процессоры для настольных и серверных си­
стем на основе микроархитектуры Intel Skylake-X, также впервые поступив­
шие на рынок в 2017 году, включают новое расширение SIMD под названием
AVX-512. Эта усовершенствованная архитектура поддерживает сжатые целые
числа и операции с плавающей запятой с использованием регистров шири­
ной 512 бит. AVX-512 также поддерживает дополнения архитектуры, которые
упрощают условное слияние данных на уровне команд, управление округле­
нием с плавающей запятой и широковещательные операции. Ожидается, что
в ближайшие несколько лет и AMD, и Intel включат AVX-512 в свои основные
процессоры для настольных ПК и ноутбуков.

1.2. Типы данных
Программы, написанные на ассемблере x86, могут использовать самые раз­
ные типы данных. Большинствотипов данных программ происходят из не­
большого набора основных типов данных, которые присущи платформе x86.
Эти основные типы данных позволяют процессору выполнять числовые и ло­
гические операции с использованием целых чисел со знаком и без знака, зна­
чений с плавающей запятой одинарной (32-битные) и двойной (64-битные)
точности, текстовых строк и значений SIMD. В этом разделе вы узнаете об
основных типах данных, а также о нескольких различных типах данных, под­
держиваемых x86.

1.2.1. Основные типы данных
Основной тип данных – это элементарная единица данных, которая обраба­
тывается процессором во время выполнения программы. Платформа x86
поддерживает основные типы данных с разрядностью от 8 бит (1 байт) до
128 бит (16 байт). В табл. 1.1 перечислены эти типы и обычные способы их
использования.

1.2. Типы данных  23
Таблица 1.1. Основные типы данных
Тип данных

Длина
(биты)

Типичное применение

Байт (byte)

8

Символы, небольшие целые числа

Слово (word)

16

Символы, целые числа

Двойное слово (doubleword)

32

Числа – целые и с плавающей запятой
(одинарной точности)

Четверное слово (quadword)

64

Числа – целые и с плавающей запятой
(двойной точности)

Двойное четверное слово (double
quadword)

128

Упакованные целые числа, упакованные
числа с плавающей запятой

Неудивительно, что размер основных типов данных зависит от целых степе­
ней двойки. Разряды основного типа данных нумеруются справа налево, начи­
ная с нуля и до значения размер–1, обозначающих младший и старший разряды
числа соответственно. Основные типы данных размером более одного байта
хранятся в последовательных ячейках памяти, начиная с младшего байта по
наименьшему адресу памяти. Этот тип расположения байтов в памяти назы­
вается прямым порядком байтов (little endian). На рис. 1.1 показаны схемы ну­
мерации битов и порядок байтов, которые применяются с основными типами
данных.
Номер бита

Байт
Слово
Двойное
слово
Четверное
слово
Двойное
четверное
слово
Адрес в памяти

Рис. 1.1. Нумерация битов и порядок байтов для основных типов данных

Правильно выровненный основной тип данных – это тот, адрес которого без
остатка делится на его размер в байтах. Например, двойное слово правильно
выровнено, когда оно хранится в ячейке памяти с адресом, который делится
на четыре без остатка. Точно так же четверные слова правильно выровнены по
адресам, делящимся на восемь. Если это специально не оговорено требованиями

24

 Архитектура ядра x86-64

операционной системы, процессор x86 не требует правильного выравнивания
многобайтовых типов данных в памяти. Однако стандартной практикой явля­
ется правильное выравнивание всех многобайтовых значений, когда это воз­
можно, чтобы избежать потенциальных потерь быстродействия, которые могут
возникнуть, если процессору требуется доступ к смещенным в памяти данным.

1.2.2. Числовые типы данных
Числовой тип данных – это элементарное скалярное значение, такое как целое
число или число с плавающей запятой. Все числовые типы данных, распознава­
емые ЦП, представлены с использованием одного из основных типов данных,
рассмотренных в предыдущем разделе. Таблица 1.2 содержит список число­
вых типов данных x86 вместе с соответствующими типами C/C++. Эта таблица
также включает типы фиксированного размера, которые определены в заго­
ловочном файле C++ (см. http://www.cplusplus.com/reference/cstdint/
для получения дополнительной информации об этом заголовочном файле).
Набор команд x86-64 поддерживает арифметические и логические операции
с использованием 8-, 16-, 32- и 64-битных целых чисел, как со знаком, так и без
знака. Он также поддерживает арифметические вычисления и операции ма­
нипулирования данными с использованием значений с плавающей запятой
одинарной и двойной точности.
Таблица 1.2. Числовые типы данных x86
Тип
Целое со знаком

Беззнаковое целое

С плавающей запятой

Длина
(биты)

Тип C/C++



8

char

int8_t

16

short

int16_t

32

int, long

int32_t

64

long long

int64_t

8

unsigned char

uint8_t

16

unsigned short

uint16_t

32

unsigned int, unsigned long

uint32_t

64

unsigned long long

uint64_t

32

float

Не применимо

64

double

Не применимо

1.2.3. Типы данных SIMD
Тип данных SIMD – это последовательный набор байтов, используемый про­
цессором для выполнения операций или вычислений, в которых участвуют
несколько значений. Тип данных SIMD можно рассматривать как объект-контейнер, который содержит несколько экземпляров одного и того же основного
типа данных (например, байты, слова, двойные слова или четверные слова).
Как и в случае основных типов данных, разряды данных SIMD пронумерованы

1.2. Типы данных  25
справа налево от нуля и до значения размер–1, обозначающих младший и стар­
ший биты соответственно. При хранении данных SIMD в памяти тоже исполь­
зуется прямой порядок байтов, как показано на рис. 1.2.
Номер бита

Адрес в памяти

Рис. 1.2. Типы данных SIMD

Программисты могут использовать данные SIMD (или упакованные данные) для выполнения одновременных вычислений с использованием целых
чисел или значений с плавающей запятой. Например, упакованные данные
с разрядностью 128 бит можно использовать для хранения шестнадцати
8-битных целых чисел, восьми 16-битных целых чисел, четырех 32-битных
целых чисел или двух 64-битных целых чисел. Упакованные данные шири­
ной 256 бит могут содержать различные элементы данных, включая восемь
значений с плавающей запятой одинарной точности или четыре значения
с плавающей запятой двойной точности. Таблица 1.3 содержит полный спи­
сок типов данных SIMD и максимальное количество элементов для различ­
ных числовых типов данных.
Таблица 1.3. Типы данных SIMD и максимальное количество элементов данных
Числовой тип
8-битное целое число

xmmword
16

ymmword
32

zmmword
64

16-битное целое число

8

16

32

32-битное целое число

4

8

16

64-битное целое число

2

4

8

Одинарной точности с плавающей
запятой

4

8

16

Двойной точности с плавающей
запятой

2

4

8

Как было сказано ранее в этой главе, усовершенствования SIMD регуляр­
но добавлялись в платформу x86, начиная с технологии MMX в 1997 году,
и вплоть до относительно недавнего обновления AVX-512. Это создает неко­
торые проблемы для разработчика, который хочет использовать эти техно­
логии, поскольку типы упакованных данных, описанные в табл. 1.3, и связан­

26

 Архитектура ядра x86-64

ные с ними наборы команд не всегда поддерживаются всеми процессорами.
К счастью, существуют методы, позволяющие во время выполнения про­
граммы определить наличие поддерживаемых функций SIMD и наборов ко­
манд конкретного процессора. Вы узнаете, как использовать некоторые из
этих методов, в главе 16.

1.2.4. Прочие типы данных
Платформа x86 также поддерживает ряд прочих типов данных, включая стро­
ки, битовые поля и битовые строки. Строка x86 представляет собой непрерыв­
ный блок байтов, слов, двойных или четверных слов. Строки x86 используются
для поддержки текстовых типов данных и операций обработки строк. Напри­
мер, типы данных C/C++ char и wchar_t обычно реализуются с использованием
байта или слова x86 соответственно. Строки x86 также можно использовать для
выполнения операций обработки массивов, растровых изображений и анало­
гичных структур данных из непрерывных блоков. Набор команд x86 включает
команды, позволяющие выполнять операции сравнения, загрузки, перемеще­
ния, сканирования и сохранения строковых данных.
Остальные типы данных представляют из себя битовые поля и битовые
строки. Битовое поле – это непрерывная последовательность битов, которая
используется некоторыми командами в качестве значения маски. Битовое
поле может начинаться с любой битовой позиции в байте и содержать до
32 бит. Битовая строка – это непрерывная последовательность битов, со­
держащая до 232–1 бит. Набор команд x86 содержит команды, которые могут
сбрасывать, устанавливать, сканировать и тестировать отдельные биты в би­
товой строке.

1.3. Внутренняя архитектура
С точки зрения исполняемой программы внутренняя архитектура процессора
x86-64 может быть логически разделена на несколько отдельных блоков. К ним
относятся регистры общего назначения, флаги состояния и управления (ре­
гистр RFLAGS), указатель команд (регистр RIP), регистры XMM, а также управ­
ления и состояния операций с плавающей запятой (MXCSR). По умолчанию
исполняемая программа использует только регистры общего назначения, ре­
гистр RFLAGS и регистр RIP. Программа не обязательно обращается к регист­
рам XMM, YMM, ZMM или MXCSR. На рис. 1.3 показана внутренняя архитектура
процессора x86-64.

1.3. Внутренняя архитектура  27

Состояние
и управление

Указатель команды
AVX/AVX2 Регистры
Состояние
и управление
(плав. запятая)

AVX-512 Регистры

Регистр общего
назначения

SSE2 Регистры

Регистры маски операции

Рис. 1.3. Внутренняя архитектура процессора X86-64

Все процессоры, совместимые с x86-64, поддерживают SSE2 и имеют 16
128-битных регистров XMM, которые программисты могут использовать для
выполнения скалярных вычислений с плавающей запятой. Эти регистры так­
же могут использоваться для выполнения операций SIMD с использованием
упакованных целых чисел или упакованных значений с плавающей запятой
(как одинарной, так и двойной точности). Вы узнаете, как использовать регист­
ры XMM, регистр MXCSR и набор команд AVX для выполнения вычислений
с плавающей запятой, в главах 4 и 5. В этой главе также более подробно рас­
сматривается набор регистров YMM и другие архитектурные концепции AVX.
Вы узнаете о AVX2 и AVX-512 в главах 8 и 12 соответственно.

1.3.1. Регистры общего назначения
Блок исполнения x86-64 содержит 16 64-битных регистров общего назначения,
которые используются для выполнения арифметических и логических опера­
ций, операций сравнения, передачи данных и вычисления адресов. Их также
можно использовать в качестве временных хранилищ для констант, промежу­
точных результатов и указателей на значения данных, хранящиеся в памяти.
На рис. 1.4 показан полный набор регистров общего назначения x86-64 вместе
с именами их командных операндов.

28

 Архитектура ядра x86-64

64- и 32-битные регистры

16- и 8-битные
регистры

Рис. 1.4. Регистры общего назначения x86-64

Двойное слово младшего разряда, слово и байт каждого 64-битного регист­
ра доступны независимо и могут использоваться для работы с 32-битными,
16-битными и 8-битными операндами. Например, функция может использо­
вать регистры EAX, EBX, ECX и EDX для выполнения 32-битных вычислений
в двойных словах младшего разряда регистров RAX, RBX, RCX и RDX соответ­
ственно. Точно так же регистры AL, BL, CL и DL могут использоваться для выпол­
нения 8-битных вычислений в младших байтах. Следует отметить, что сущест­
вует несоответствие в названиях некоторых байтовых регистров. 64-битный
ассемблер Microsoft использует имена, показанные на рис. 1.4, а в докумен­
тации Intel применяются имена R8L–R15L. В этой книге используются име­
на регистров Microsoft, чтобы поддерживать согласованность между текстом
и примерами кода. На рис. 1.4 не показаны устаревшие байтовые регистры AH,
BH, CH и DH. Эти регистры связаны со старшими байтами регистров AX, BX,
CX и DX соответственно. Устаревшие байтовые регистры могут использоваться
в программах x86-64, хотя и с некоторыми ограничениями, как описано далее
в этой главе.

1.3. Внутренняя архитектура  29
Несмотря на название регистров общего назначения, набор команд x86-64
налагает некоторые заметные ограничения на то, как их можно использовать.
Некоторые команды явно требуют или неявно используют определенные ре­
гистры в качестве операндов. Это устаревший шаблон проектирования, восхо­
дящий к 8086, предположительно для повышения плотности кода. Например,
некоторые варианты команды imul (целочисленное умножение со знаком)
сохраняют вычисленное целочисленное произведение в RDX:RAX, EDX:EAX,
DX:AX или AX (двоеточие означает, что конечный результат содержится в двух
регистрах, где первый регистр содержит старшие биты). Команда idiv (цело­
численное деление со знаком) требует, чтобы целочисленное делимое было
загружено в RDX:RAX, EDX:EAX, DX:AX или AX.
Строковые команды x86 требуют, чтобы адреса операндов источника и при­
емника были помещены в регистры RSI и RDI соответственно. Строковые ко­
манды, содержащие префикс повтора, должны использовать RCX в качестве
регистра счетчика, в то время как команды битового и циклического сдвига
должны загружать значение счетчика в регистр CL.
Процессор использует регистр RSP для поддержки операций, связанных
со стеком, таких как вызовы и возврат функций. Сам стек – это просто не­
прерывный блок памяти, который операционная система назначает про­
цессу или потоку. Прикладные программы также могут использовать стек
для передачи аргументов функции и хранения временных данных. Регистр
RSP всегда указывает на самый верхний элемент стека. Операции загрузки
в стек и выгрузки из стека выполняются с использованием 64-битных опе­
рандов. Это означает, что положение стека в памяти обычно выравнивается
по 8-байтовой границе. Некоторые среды выполнения (например, 64-бит­
ные программы Visual C++, работающие в Windows) выравнивают стековую
память и RSP по 16-байтовой границе, чтобы избежать неправильного пере­
носа содержимого памяти между регистрами XMM и 128-битными операн­
дами, хранящимися в стеке.
Хотя технически возможно использовать регистр RSP в качестве регистра
общего назначения, такое использование непрактично и настоятельно не ре­
комендуется. Регистр RBP обычно используется как начальный указатель для
доступа к элементам данных, которые хранятся в стеке. RSP также можно ис­
пользовать в качестве начального указателя для доступа к элементам данных
в стеке. Когда он не используется в качестве указателя, программы могут ис­
пользовать RBP как регистр общего назначения.

1.3.2. Регистр RFLAGS
Регистр RFLAGS содержит ряд битов состояния (или флагов), которые процес­
сор использует для обозначения результатов арифметической или логической
операции, или операции сравнения. Он также содержит ряд управляющих би­
тов, которые в основном используются операционными системами. В табл. 1.4
показана организация битов в регистре RFLAGS.

30



Архитектура ядра x86-64

Таблица 1.4. Регистр RFLAGS
Номер
бита

Название

Обозначение
CF

Применение

0

Флаг переноса (Carry Flag)

Состояние

1

Зарезервирован

2

Флаг четности (Parity Flag)

3

Зарезервирован

4

Вспомогательный флаг переноса
(Auxiliary Carry Flag)

5

Зарезервирован

6

Флаг нулевого результата (Zero Flag)

ZF

Состояние

7

Флаг знака (Sign Flag)

SF

Состояние

8

Флаг трассировки (Trap Flag)

TF

Системный

9

Флаг разрешения прерываний
(Interrupt Enable Flag)

IF

Системный

10

Флаг направления (Direction Flag)

DF

Управление

11

Флаг переполнения (Overflow Flag)

OF

Состояние

12

Бит 0 уровня привилегии ввода-вывода
(I/O Privilege Level Bit 0)

IOPL

Системный

13

Бит 1 уровня привилегии ввода-вывода
(I/O Privilege Level Bit 1)

IOPL

Системный

14

Вложенная задача (Nested Task)

NT

Системный

15

Зарезервирован

16

Флаг итога (Resume Flag)

RF

Системный

17

Виртуальный режим 8086
(Virtual 8086 Mode)

VM

Системный

18

Проверка выравнивания
(Alignment Check)

AC

Системный

19

Флаг виртуального прерывания
(Virtual Interrupt Flag)

VIF

Системный

20

Запрос виртуального прерывания
(Virtual Interrupt Pending)

VIP

Системный

21

Флаг ID (ID Flag)

ID

Системный

22–63

Зарезервированы

1
PF

Состояние
0

AF

Состояние
0

0

0

1.3. Внутренняя архитектура  31
Для прикладных программ наиболее важными битами в регистре RFLAGS
являются следующие флаги состояния: флаг переноса (CF), флаг переполнения
(OF), флаг четности (PF), знак знака (SF) и флаг нулевого результата (ZF). Флаг
переноса устанавливается процессором для обозначения состояния перепол­
нения при выполнении беззнаковой целочисленной арифметики. Он также ис­
пользуется некоторыми командами циклического и битового сдвига регистров.
Флаг переполнения сигнализирует, что результат целочисленной операции со
знаком слишком мал или слишком велик. Процессор устанавливает флаг чет­
ности, чтобы указать, содержит ли младший значимый байт арифметической,
логической или операции сравнения четное число битов 1 (биты четности ис­
пользуются некоторыми протоколами связи для обнаружения ошибок пере­
дачи). Флаги знака и нулевого результата устанавливаются арифметическими
и логическими командами для обозначения отрицательного, положительного
или нулевого результата.
Регистр RFLAGS содержит управляющий бит, называемый флагом направ­
ления (DF). Прикладная программа может устанавливать или сбрасывать флаг
направления, который определяет направление автоматического инкремента
(0 = адреса от младшего к старшему, 1 = от старшего к младшему) регистров
RDI и RSI во время выполнения строковых команд. Остальные биты в регистре
RFLAGS используются исключительно операционной системой для управле­
ния прерываниями, ограничения операций ввода-вывода, поддержки отладки
программ и обработки виртуальных операций. Они никогда не должны изме­
няться прикладной программой. Зарезервированные биты также никогда не
должны изменяться, и не следует делать никаких предположений относитель­
но состояния какого-либо зарезервированного бита.

1.3.3. Указатель команд
Регистр указателя команд (RIP) содержит логический адрес следующей коман­
ды, которая должна быть выполнена. Значение в регистре RIP обновляется
автоматически во время выполнения каждой команды. Он также неявно из­
меняется во время выполнения команд, связанных с передачей управления.
Например, команда call (вызов процедуры) помещает содержимое регистра
RIP в стек и передает управление программой по адресу, представленному
в указанном операнде. Команда ret (возврат из процедуры) передает управле­
ние программой, извлекая восемь самых верхних байтов из стека и загружая
их в регистр RIP.
Команды jmp (переход) и jcc (переход, если условие выполнено) также пере­
дают управление программой, изменяя содержимое регистра RIP. В отличие от
команд call и ret, все команды перехода x86-64 выполняются независимо от
стека. Регистр RIP также используется для относительной адресации памяти
операндов, как описано в следующем разделе. Выполняемая задача не может
напрямую обращаться к содержимому регистра RIP.

32  Архитектура ядра x86-64

1.3.4. Операнды команд
Все команды x86-64 используют операнды, обозначающие конкретные значе­
ния, с которыми будет работать команда. Почти все команды требуют наличия
одного или нескольких операндов-источников вместе с одним операндом-приемником. Большинство команд также требуют, чтобы программист явно указал
операнды источника и приемника. Однако существует ряд команд, в которых
регистровые операнды либо указаны неявно, либо зависят от команды, как об­
суждалось в предыдущем разделе.
Есть три основных типа операндов: непосредственные, регистровые и хра­
нимые (запоминаемые). Непосредственный операнд – это постоянное значе­
ние, которое кодируется как часть команды. Обычно они используются для
указания постоянных значений. Непосредственное значение можно присво­
ить только операнду источника. Регистровые операнды содержатся в регистре
общего назначения или регистре SIMD. Хранимый операнд определяет место
в памяти, где может храниться любой из типов данных, описанных ранее
в этой главе. Хранимым операндом команды может быть либо исходный, либо
целевой операнд, но не оба вместе. Таблица 1.5 содержит несколько примеров
команд, которые используют различные типы операндов.
Таблица 1.5. Примеры основных типов операндов
Тип
Непосредственный

Регистровый

Хранимый

Пример

Аналогичный оператор C/C++

mov rax, 42

rax = 42

imul r12,–47

r12 *= –47

shl r15,8

r15 ≪= 8

xor ecx,80000000h

ecx ^= 0x80000000

sub r9b,14

r9b –= 14

mov rax,rbx

rax = rbx

add rbx,r10

rbx += r10

mul rbx

rdx:rax = rax * rbx

and r8w,0ff00h

r8w &= oxffoo

mov rax, [r13]

rax = *r13

or rcx,[rbx+rsi*8]

rcx |= *(rbx+rsi*8)

sub qword ptr [r8],17

*(long long*)r8 –= 17

shl word ptr [r12],2

*(short*)r12