Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Boroda_2

.doc
Скачиваний:
7
Добавлен:
20.03.2015
Размер:
1.67 Mб
Скачать

Общий обзор уровня архитектуры набора команд

Давайте начнем изучение уровня архитектуры набора команд с вопроса о том, что он собой представляет. Этот вопрос на первый взгляд может показаться про­стым, но на самом деле здесь очень много тонкостей. В следующем подразделе мы обсудим некоторые из них, а затем мы рассмотрим модели памяти, регистров и команд.

Свойства уровня архитектуры набора команд

В принципе уровень архитектуры набора команд — это тот уровень, на котором компьютер представляется программисту, пишущему программы на машинном языке. Поскольку сейчас ни один нормальный программист таких программ не пишет, мы слегка переделали это определение: программа уровня архитектуры набора команд — это то, что получается в результате работы компилятора (в дан­ный момент мы будем касаться системных вызовов и символического языка ас­семблера). Чтобы получить программу уровня архитектуры набора команд, соз­датель компилятора должен знать, какая модель памяти используется в машине, какие регистры, типы данных и команды имеются в наличии, и т. д. Вся эта информация в совокупности и определяет уровень архитектуры набора команд.

В соответствии с этим определением вопросы о том, доступна ли микроархи­тектура программно, конвейеризирован ли компьютер, является ли он суперска- лярным и т. д., не относятся к уровню архитектуры набора команд, поскольку разработчик компилятора всего этого не видит. Однако это замечание не совсем справедливо, поскольку некоторые из этих характеристик влияют на производи­тельность, а производительность, в свою очередь, — вполне доступный для раз­работчика компилятора показатель. Рассмотрим, например, суперскалярную ма­шину, которая в одном цикле может обрабатывать сдвоенные команды, причем такие, что одна команда целочисленная, а вторая с плавающей точкой. Если в ко­де, полученном в результате работы компилятора, целочисленные команды и ко­манды с плавающей точкой будут чередоваться, то производительность заметно повысится. Таким образом, детали суперскалярной операции доступны на уров­не архитектуры набора команд, то есть границы между разными уровнями раз­мыты.

Для одних архитектур уровень команд определяется формальным докумен­том, который обычно выпускается промышленным консорциумом, для других — нет. Например, системы V9 SPARC (Version 9 SPARC) и JVM имеют официаль­ные определения [216]. Цель такого официального документа — дать возмож­ность различным производителям выпускать машины данного типа, чтобы эти машины могли выполнять одни и те же программы и получать при этом одни и те же результаты.

В случае с системой SPARC подобные документы нужны для того, чтобы раз­личные предприятия могли выпускать идентичные микросхемы SPARC, отли­чающиеся друг от друга только по производительности и цене. Чтобы эта идея работала, поставщики микросхем должны знать, что делает микросхема SPARC (на уровне архитектуры набора команд). Следовательно, в документе говорится о том, какова модель памяти, какие имеются регистры, какие действия выполня­ют команды и т. д., а не о том, что представляет собой микроархитектура.

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

Выполнение зарезервированного кода операции должно вызывать системное

прерывание.

Может быть и альтернативный подход:

Результат выполнения зарезервированного кода операции определяется реа­лизацией.

Это значит, что составитель компилятора не может просчитать какие-то кон­кретные действия предоставляет конструкторам свободу выбора. К описанию архитектуры часто прилагаются тестовые пакеты для проверки, действительно ли данная реализация соответствует техническим требованиям.

Совершенно ясно, почему система V9 SPARC поставляется с документом, в котором определяется уровень архитектуры набора команд, — это нужно для того, чтобы все микросхемы V9 SPARC могли выполнять одни и те же програм­мы. Для уровня архитектуры набора команд процессора Pentium 4 такого доку­мента нет, поскольку компания Intel не хочет, чтобы другие производители мог­ли выпускать микросхемы Pentium 4. Компания Intel даже обращалась в суд, чтобы запретить производство своих микросхем другим производителям, но проиграла процесс.

Другое важное качество уровня архитектуры набора команд состоит в том, что в большинстве машин поддерживаются по крайней мере два режима. В при­вилегированном режиме запускается операционная система. Этот режим по­зволяет выполнять все команды. Пользовательский режим предназначен для запуска прикладных программ. Он не позволяет выполнять некоторые потенци­ально опасные команды (например, те, которые непосредственно манипулируют кэш-памятью). В этой главе мы в первую очередь сосредоточимся на командах и свойствах пользовательского режима.

Модели памяти

Во всех компьютерах память разделена на ячейки, которые имеют последователь­ные адреса. В настоящее время наиболее распространенный размер ячейки — 8 бит, но раньше использовались ячейки от 1 до 60 бит (см. табл. 2.1). Ячейка из 8 бит называется байтом. Причиной применения именно 8-разрядных ячеек памяти является ASCII-символ, который занимает 7 бит, а вместе с битом чет­ности — 8. Если в будущем будет доминировать кодировка UNICODE, то ячей­ки памяти, возможно, станут 16-разрядными. Вообще говоря, число 24 лучше, чем 23, поскольку 4 — степень двойки, а 3 — нет.

Байты обычно группируются в 4-байтные (32-разрядные) или 8-байтные (64-разрядные) слова с командами манипулирования целыми словами. Многие архитектуры требуют, чтобы слова были выровнены в своих естественных гра­ницах. Так, 4-байтное слово может начинаться с адреса 0, 4, 8 и т. д., но не с адре­са 1 или 2. Точно так же слово из 8 байт может начинаться с адреса 0, 8 или 16, но не с адреса 4 или 6. Механизм размещения 8-байтных слов в памяти иллюст­рирует рис. 5.2.

Выравнивание адресов требуется довольно часто, поскольку при этом память работает наиболее эффективно. Например, процессор Pentium 4, который вызы­вает из памяти по 8 байт за обращение, использует 36-разрядные физические ад­реса, но содержит только 33 адресных бита. Следовательно, Pentium 4 даже не сможет обратиться к невыровненной памяти, поскольку младшие 3 бита явным образом не определены. Эти биты всегда равны 0, и все адреса памяти кратны значению 8 байт.

Однако требование относительно выравнивания адресов иногда вызывает не­которые проблемы. В процессоре Pentium 4 программы могут обращаться к сло­вам, начиная с любого адреса, — это качество восходит к модели 8088 с шиной данных шириной 1 байт, в которой не требовалось, чтобы ячейки располагались в 8-байтных границах. Если программа в процессоре Pentium 4 считывает 4-байтное слово с адреса 7, аппаратное обеспечение должно сделать одно обра­щение к памяти, чтобы вызвать байты с 0 по 7, а второе — чтобы вызвать байты с 8 по 15. Затем центральный процессор извлекает требуемые 4 байта из 16,

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

Еще один аспект модели памяти — семантика памяти. Естественно ожидать, что команда LOAD, если она выполняется после команды STORE, обратится к тому же адресу и возвратит только что сохраненное значение. Однако, как мы видели в главе 4, во многих машинах микрокоманды переупорядочиваются. Таким обра­зом, существует реальная опасность, что память будет работать не так, как ожи­дается. Ситуация усложняется при наличии мультипроцессора, когда каждый процессор посылает в общую память поток запросов на чтение и запись, и эти за­просы тоже могут быть переупорядочены.

Системные разработчики могут применять один из нескольких подходов ре­шения этой проблемы. С одной стороны, все запросы к памяти могут быть упо­рядочены таким образом, чтобы каждый из них завершался до того, как начнется следующий. Такая стратегия отрицательно сказывается на производительности, но зато дает простейшую семантику памяти (все операции выполняются строго в том порядке, в котором они расположены в программе).

С другой стороны, можно вообще не давать никаких гарантий относительно упорядоченности запросов к памяти, а чтобы добиться такой упорядоченности, программа выполняет команду SYNC, которая блокирует запуск всех новых опе­раций с памятью до тех пор, пока не завершатся предыдущие. Эта идея весьма затрудняет работу создателей компиляторов, поскольку им приходится тщатель­но разбираться в том, как работает соответствующая микроархитектура, но зато разработчикам аппаратного обеспечения предоставлена полная свобода в плане оптимизации использования памяти.

Возможны также промежуточные модели памяти, в которых аппаратное обес­печение автоматически блокирует запуск определенных операций с памятью (например, тех, которые связаны с RAW- и WAR-взаимозависимостями), при этом запуск всех других операций не блокируется. Хотя реализация этих воз­можностей на уровне архитектуры набора команд довольно утомительна (по крайней мере, для создателей компиляторов и программистов на языке ассемб­лера), сейчас заметна тенденция к преобладанию подобного подхода. Данная тенденция вызвана к жизни такими разработками, как механизмы переупорядо­чения микрокоманд, конвейеры, многоуровневая кэш-память и т. д. Другие, ме­нее известные, примеры такого рода мы рассмотрим в этой главе чуть позже.

Регистры

Во всех компьютерах имеются несколько регистров, доступных на уровне архи­тектуры набора команд. Они позволяют контролировать выполнение програм­мы, хранить временные результаты, а также служат для некоторых других целей. Обычно регистры, доступные на уровне микроархитектуры, например TOS и MAR (см. рис. 4.1), на уровне архитектуры набора команд недоступны, однако некоторые регистры, например счетчик команд и указатель стека, доступны на обоих уровнях. В то же время регистры, доступные на уровне архитектуры набо­ра команд, всегда доступны на уровне микроархитектуры, поскольку именно там они реализованы.

Регистры уровня архитектуры набора команд можно разделить на две катего­рии: специальные регистры и регистры общего назначения. К специальным реги­страм относятся счетчик команд и указатель стека, а также другие регистры, имеющие особые функции. Регистры общего назначения содержат ключевые ло­кальные переменные и промежуточные результаты вычислений. Их основная функция состоит в том, чтобы обеспечить быстрый доступ к часто используе­мым данным (обычно без обращений к памяти). RISC-машины с высокоскорост­ными процессорами и (относительно) медленной памятью обычно содержат как минимум 32 регистра общего назначения, причем в новых процессорах количе­ство регистров общего назначения постоянно растет.

В некоторых машинах регистры общего назначения полностью симметричны и взаимозаменяемы. Если все регистры эквивалентны, для хранения промежу­точного результата компилятор может использовать как регистр R1, так и ре­гистр R25. Выбор регистра не имеет никакого значения.

В других машинах некоторые регистры общего назначения могут быть спе­циализированными. Например, в процессоре Pentium 4 имеется регистр EDX, который может использоваться в качестве регистра общего назначения, но кото­рый, кроме того, используется для решения сугубо специфических задач (полу­чает половину произведения при умножении и половину делимого при делении).

Даже если регистры общего назначения полностью взаимозаменяемы, опера­ционная система или компиляторы часто следуют соглашениям о том, каким об­разом использовать эти регистры. Например, некоторые регистры могли бы при­меняться для хранения параметров вызываемых процедур, другие выполнять роль временных. Если компилятор помещает важную локальную переменную в ре­гистр R1, а затем вызывает библиотечную процедуру, которая воспринимает R1 как регистр, временно выделенный в ее распоряжение, то после возвращения значения этой процедурой в регистре R1 может остаться «мусор». То есть если существуют какие-либо системные соглашения по поводу того, как нужно ис­пользовать регистры, разработчики компиляторов и программисты, пишущие на ассемблере, должны им следовать.

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

Существует один регистр, который представляет собой «гибрид», доступный и в привилегированном, и в пользовательском режимах. Это — упоминавшийся в главе 4 регистр PSW (Program State Word — слово состояния программы), который еще называют флаговым. Флаговый регистр содержит различные биты, необходимые центральному процессору. Самые важные биты — это коды усло­вий. Они устанавливаются в каждом цикле АЛУ и отражают состояние резуль­тата предыдущей операции:

+ N — результат отрицателен (Negative);

+ Z — результат равен 0 (Zero);

+ V — результат вызвал переполнение (oVerflow);

+ С — перенос самого левого бита (Carry out);

♦ А — перенос бита 3 (Auxiliary carry — служебный перенос);

+ Р — результат четный (Parity).

Коды условий очень важны, поскольку используются при сравнениях и ус­ловных переходах. Например, команда СМР обычно вычитает один операнд из другого и на основе полученной разности устанавливает коды условий. Если операнды равны, то разность будет равна 0, а во флаговом регистре установится бит Z. Последующая команда BEQ (Branch Equal — переход в случае равенства) проверяет бит Z и совершает переход, если он установлен.

Флаговый регистр может хранить не только коды условий. Его содержимое в разных машинах может быть разным. Дополнительные поля указывают режим машины (например, пользовательский или привилегированный), бит трассиров­ки (который используется для отладки), уровень приоритета процессора, статус разрешения прерываний. Флаговый регистр обычно читается в пользователь­ском режиме, но некоторые поля могут записываться только в привилегирован­ном режиме (например, бит, который указывает режим).

Команды

Главная особенность уровня, который мы сейчас рассматриваем, — это набор ма­шинных команд. Они управляют действиями машины. В этом наборе всегда в той или иной форме присутствуют команды LOAD и STORE, предназначенные для перемещения данных между памятью и регистрами, и команда MOVE, которая слу­жит для копирования данных из одного регистра в другой. Также всегда присут­ствуют арифметические и логические команды, команды сравнения элементов данных и команды переходов в зависимости от результатов. Некоторые типич­ные команды мы уже рассматривали в главе 4 (см. табл. 4.2.), а в этой главе мы познакомимся со многими другими.

В этой главе мы обсудим три совершенно разные архитектуры команд: IA-32 компании Intel (она реализована в Pentium 4), Version 9 SPARC (она реализо­вана в процессорах UltraSPARC) и 8051. Мы не преследуем цель дать исчер­пывающее описание каждой из этих архитектур. Мы просто хотим продемон­стрировать важные аспекты архитектуры команд и показать, как эти аспекты меняются от одной архитектуры к другой. Начнем с машины Pentium 4.

Общий обзор уровня архитектуры набора команд Pentium 4

Процессор Pentium 4 развивался на протяжении многих лет. Как отмечалось в главе 1, его история восходит к самым первым микропроцессорам. Основная архитектура команд обеспечивает выполнение программ, написанных для про­цессоров 8086 и 8088 (которые имеют одну и ту же архитектуру команд), и от­части даже для 8080 — 8-разрядного процессора, который был популярен в 70-е годы. На 8080, в свою очередь, в значительной степени повлияли требования со­вместимости с процессором 8008, построенным на базе процессора 4004 (4-раз- рядной микросхемы, применявшейся еще в каменном веке).

С точки зрения программного обеспечения компьютеры 8086 и 8088 были 16-разрядными (хотя компьютер 8088 содержал 8-разрядную шину данных). Их последователь, 80286, также был 16-разрядным. Его главным преимуществом был больший объем адресного пространства, хотя очень немногие программы его использовали, поскольку оно состояло из 16 384 64-килобайтных сегментов, а не представляло собой линейную 230-байтную память.

Процессор 80386 был первой 32-разрядной машиной, выпущенной компани­ей Intel. Все последующие процессоры (80486, Pentium, Pentium Pro, Pentium II, Pentium III, Pentium 4, Celeron, Xeon, Pentium M, Centrino и т. д.) имеют точно такую же 32-разрядную архитектуру, которая называется IA-32, поэтому мы со­средоточим наше внимание именно на этой архитектуре. Единственным суще­ственным изменением архитектуры со времен процессора 80386 было введение в более поздние версии Pentium MMX-команд. Эти команды выполняют совер­шенно определенную функцию — повышают производительность мультимедий­ных приложений.

Pentium 4 имеет 3 операционных режима, в двух из которых он работает как 8086. В реальном режиме все функции, которыми был наделен процессор со вре­мен 8088, отключаются, и Pentium 4 работает как простой процессор 8088. При программной ошибке происходит полный отказ системы. Если бы компания Intel занималась разработкой человеческих существ, то внутрь каждого такого существа непременно помещался бы специальный бит, возвращающий человека в режим функционирования своих предков (примитивный мозг, отсутствие речи, обитание на деревьях, сугубо банановая диета и т. д.).

На следующей ступени находится режим виртуального процессора 8086, ко­торый делает возможным исполнение старых программ, написанных для 8088, но с защитой. Чтобы запустить старую программу 8088, операционная система создает специальную изолированную среду, которая работает как процессор 8088, если не считать того, что при программном сбое операционной системе передает­ся соответствующая информация, и полного краха системы не происходит. Ко­гда пользователь Windows открывает окно MS-DOS, запускаемая в этом окне программа выполняется в режиме виртуального процессора 8086 — это позволя­ет защитить Windows от возможных вольностей DOS-программ.

Последний режим — это защищенный режим, в котором Pentium 4 работает как Pentium 4, а не как 8088. В этом режиме доступны 4 уровня привилегий, за­даваемые битами во флаговом регистре (PSW). Уровень 0 соответствует приви­легированному режиму на других компьютерах и обеспечивает полный доступ к машине. Этот уровень используется операционной системой. Уровень 3 пред­назначен для пользовательских программ. На этом уровне блокируется доступ к определенным командам и регистрам управления, чтобы сбой какой-нибудь пользовательской программы не привел к краху всей системы. Уровни 1 и 2 при­меняются редко.

Pentium 4 имеет огромное адресное пространство. Память разделена на 16 384 сегмента, каждый из которых занимает адреса от 0 до 232 - 1. Однако большинство операционных систем (включая UNIX и все версии Windows) под­держивают только один сегмент, поэтому для прикладных программ обычно дос­тупно линейное адресное пространство размером 232 байт, причем иногда часть этого пространства занимает сама операционная система. Каждый байт в адрес­ном пространстве имеет свой адрес. Слова состоят из 32 бит. Байты нумеруются справа налево (то есть самый первый адрес соответствует самому младшему байту).

Регистры процессора Pentium 4 показаны на рис. 5.3. Первые четыре регистра, ЕАХ, ЕВХ, ЕСХ и EDX, — 32-разрядные. Это регистры общего назначения, хотя у каждого из них есть определенные особенности. ЕАХ — основной арифметиче­ский регистр; ЕВХ предназначен для хранения указателей (адресов памяти); ЕСХ связан с организацией циклов; EDX нужен для умножения и деления — этот ре­гистр вместе с ЕАХ содержит 64-разрядные произведения и делимые.

Младшие 16 и 8 бит в каждом из рассматриваемых регистров — это самостоя­тельные 16- и 8-разрядный регистры соответственно, позволяющие легко мани­пулировать 16- и 8-разрядными значениями. В компьютерах 8088 и 80286 име­ются только 8- и 16-разрядные регистры, 32-разрядные регистры появились в системе 80386 вместе с приставкой Е (Extended — расширенный).

Следующие три регистра также являются регистрами общего назначения, но с большей степенью специализации. Регистры ESI и EDI предназначены для хранения указателей и в основном ориентированы на аппаратную поддержку строковых команд: ESI указывает на исходную строку, EDI — на целевую. Ре­гистр EBP тоже предназначен для хранения указателей и обычно используется для указания на базу текущего фрейма локальных переменных, как и регистр LV в машине IJVM. Такой регистр обычно называют указателем фрейма. Наконец, регистр ESP — это указатель стека.

Следующая группа регистров от CS до GS — сегментные регистры. Это элек­тронные трилобиты — атавизмы, оставшиеся от процессора 8088, которому через 16-разрядные адреса было доступно 220 байт памяти. Достаточно сказать, что ко­гда Pentium 4 работает в режиме использования единого линейного 32-разрядно- го адресного пространства, их можно смело игнорировать. Регистр EIP (Extended Instruction Pointer — расширенный указатель команд) представляет собой счетчик команд. Регистр EFLAGS — флаговый.

Общий обзор уровня архитектуры набора команд UltraSPARC III

Архитектура SPARC была впервые введена в 1987 году компанией Sun Micro­systems. Эта архитектура стала одной из первых RISC-архитектур промышлен­ного назначения. Она была основана на исследовании, проведенном в Беркли в 80-е годы [161, 164]. Изначально архитектура SPARC была 32-разрядной, но UltraSPARC III — это 64-разрядная машина, основанная на архитектуре Ver­sion 9 SPARC, и именно ее мы будем описывать в этой главе. В целях согласо­ванности с остальными частями книги мы будем называть данную систему UltraSPARC III, хотя на уровне архитектуры набора команд все машины UltraSPARC идентичны.

Структура памяти машины UltraSPARC III очень проста — линейный массив размером 264 байт. В настоящее время реализовать ее невозможно, поскольку па­мять слишком велика (18 446 744 073 709 551 616 байт). Современные реализа­ции имеют ограничение на размер адресного пространства, к которому они могут обращаться (244 байт у UltraSPARC III), но в будущем это число увеличится. Байты нумеруются слева направо, но можно перейти на нумерацию справа нале­во, установив один из битов во флаговом регистре.

Важно, что предельное число адресуемых байтов больше, чем требуется для реализации архитектуры команд, поскольку в будущем скорее всего понадобит­ся увеличить объем памяти, к которой может обращаться процессор. Одна из самых серьезных проблем состоит в том, что архитектура команд ограничивает размер адресуемой памяти. Это — проявление глобальной информационной про­блемы (имеющихся битов всегда не хватает), которая, вероятно, не разрешится никогда. Когда-нибудь наши внуки будут удивляться, как же могли работать компьютеры, содержащие всего 32-разрядные адреса и 4 Гбайт памяти.

Архитектура команд SPARC достаточно проста, хотя организация регистров была немного усложнена, чтобы сделать вызовы процедур более эффективными. Практика показывает, что организация регистров требует больших усилий, и, хо­тя обычно эти усилия того не стоят, правило совместимости не позволяет от это­го отказаться.

В системе UltraSPARC III имеется две группы регистров: 32 64-разрядных регистра общего назначения и 32 регистра для команд с плавающей точкой. Ре­гистры общего назначения называются R0-R31, но в определенных контекстах используются другие названия. Варианты названий регистров и их функции приведены в табл. 5.1.

Таблица 5.1. Регистры общего назначения в системе UltraSPARC III

Регистр

Другой вариант названия

Назначение

R0

GO

Аппаратный нуль. То, что сохранено в этом регистре, просто игнорируется

R1-R7

G1-G7

Содержат глобальные переменные

R8-R13

00-05

Содержат параметры вызываемой процедуры

R14

SP

Указатель стека

R15

07

Временный регистр

R16-R23

L0-L7

Содержат локальные переменные для текущей процедуры

R24-R29

I0-I5

Содержат входные параметры

R30

FP

Указатель на базу текущего стекового фрейма

R31

17

Содержит адрес возврата для текущей процедуры

Все регистры общего назначения 64-разрядные. Все они, кроме R0, который всегда нулевой, могут считываться и записываться при помощи различных ко­манд загрузки и сохранения. Назначение этих регистров, приведенное в табл. 5.1, отчасти определено по соглашению, отчасти зависит от используемого аппарат­ного обеспечения. Однако, вообще говоря, не стоит отклоняться от указанного назначения, если вы не являетесь крупным специалистом по компьютерам SPARC. Программист должен быть уверен, что программа правильно обращается к реги­страм и выполняет с ними допустимые арифметические действия. Например, очень легко загрузить числа с плавающей точкой в регистры общего назначения, а затем произвести над ними целочисленное сложение — операцию, результатом которой будет полнейшая чепуха, но которую центральный процессор обяза­тельно выполнит, если того потребует программа.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]