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

Архитектура компьютера - Таненбаум Э

..pdf
Скачиваний:
485
Добавлен:
24.05.2014
Размер:
5.67 Mб
Скачать

Пример микроархитектуры

243

Отметим, что в любом случае регистр МРС может принять только одно из двух возмол.чых значений:

1.Значение NEXT_ADDRESS.

2.Значение NEXT_ADDRESS со старшим битом, соединенным с логической единицей операцией ИЛИ.

Других значений не существует. Если старший бит значения NEXT_ADDRESS уже равен 1, нет смысла использоватьJAMN или JAMZ.

Отметим, что если все битыJAM равны 0, то адрес следующей команды — просто 9-битный номер в поле NEXT_ADDRESSЕсли JAMN или JAMZ равны 1, то существует два потенциально возможных адреса следующей микрокоманды: NEXT_ ADDRESS и NEXT_ADDRESS, соединенный операцией ИЛИ с 0x100 (предполагается, что NEXT_ADDRESS<OxFF). (Отметим, что Ох указывает, что число, следующее за ним, дается в шестнадцатеричной системе счисления). Это проиллюстрировано рис. 4.6. Текущая микрокоманда с адресом 0x75 содержит поле NEXT_ADDRESS=0x92, причем бит JAMZ установлен на 1. Следовательно, следующий адрес микрокоманды зависит от значения бита Z, сохраненного при предыдущей операции АЛУ. Если бит Z равен 0, то следующая микрокоманда имеет адрес 0x92. Если бит Z равен 1, то следующая микрокоманда имеет адрес 0x192.

Третий бит в полеJAM —JMPC. Если он установлен, то 8 битов регистра MBR поразрядно связываются операцией ИЛИ с 8 младшими битами поля NEXT_ ADDRESS из текущей микрокоманды. Результат отправляется в регистр МРС. На рис. 4.5 значком «ИЛИ» обозначена схема, которая выполняет операцию ИЛИ над MBR и NEXT_ADDRESS, если бит JMPC равен 1, и просто отправляет NEXT_ADDRESS в регистр МРС, если битJMPC равен 0. ЕслиJMPC равен 1, то младшие 8 битов поля NEXT_ADDRESS равны 0. Старший бит может быть 0 или 1, поэтому значение поля NEXT_ADDRESS обычно 0x000 или 0x100. Почему иногда используется 0x000, а иногда — 0x100, мы обсудим позже.

Адрес

Addr

JAM

Биты управления трактом данных

0x75

0x92

001

Набор

битов JAMZ

 

 

 

 

 

;

 

0x92

 

 

Один из этих

 

 

адресов

 

 

 

 

 

;

последует

 

 

за 0x73

 

 

 

в зависимости

0x192

 

 

OTZ

Рис. 4.6. Микрокоманда с битом JAMZ, равным 1, указывает на две потенциальные последующие микрокоманды

Возможность выполнять операцию ИЛИ над MBR и NEXT_ADDRESS и сохранять результат в регистре МРС позволяет реализовывать межуровневые переходы. Отметим, что по битам, находящимся в регистре MBR, можно определить любой адрес из 256 возможных. Регистр MBR содержит код операции, поэтому использование JMPC приведет к единственно возможному выбору следующей

244 Глава 4. Микроархитектурный уровень

микрокоманды. Этот метод позволяет осуществлять быстрый переход у функции, соответствующей вызванному коду операции.

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

Во время подцикла 1, который инициируется задним фронтом сигнала, адрес, находящийся в данный момент в регистре МРС, загружается в регистр MIR. Во время подцикла 2 регистр MIR выдает сигналы и в шину В загружается выбранный регистр. Во время подцикла 3 происходит работа АЛУ и схемы сдвига. Во время подцикла 4 стабилизируются значения шины С, шин памяти и АЛУ. На нарастающем фронте сигнала загружаются регистры из шины С, загружаются триггеры N и Z, а регистры MBR и MDR получают результаты работы памяти, начавшейся в конце предыдущего цикла (если эти результаты вообще имеются). Как только регистр MBR получает свое значение, загружается регистр МРС. Это происходит где-то в середине отрезка между нарастающим и задним фронтами, но уже после загрузки MBR/MDR. Он может загружаться уровнем сигнала (но не фронтом сигнала) либо загружаться через фиксированный отрезок времени после нарастающего фронта. Все это означает, что регистр МРС не получает своего значения до тех пор, пока не будут готовы регистры MBR, N и Z, от которых он зависит. На заднем фронте сигнала, когда начинается новый цикл, регистр МРС может обращаться к памяти.

Отметим, что каждый цикл является самодостаточным. В каждом цикле определяется, значение какого регистра должно поступать на шину В, что должны делать АЛУ и схема сдвига, куда нужно сохранить значение шины С, и, наконец, каким должно быть следующее значение регистра МРС.

Следует сделать еще одно замечание по поводу рис. 4.5. До сих пор мы считали МРС регистром, который состоит из 9 битов и загружается на высоком уровне сигнала. В действительности этот регистр вообще не нужен. Все его входные сигналы можно непосредственно связать с управляющей памятью. Поскольку они имеются в управляющей памяти на заднем фронте синхронизирующего сигнала, когда выбирается и считывается регистр MIR, этого достаточно. Их не нужно хранить в регистре МРС. По этой причине МРС может быть реализован в виде виртуального регистра, который представляет собой просто место скопления сигналов и похож скорее на коммутационное поле, чем на настоящий регистр. Если МРС сделать виртуальным регистром, то процедура синхронизации сильно упрощается: теперь события происходят только на нарастающем фронте и заднем фронте сигнала. Но если вам проще считать МРС реальным регистром, то такой подход тоже вполне допустим.

Пример архитектуры команд: IJVM

Чтобы продолжить описание нашего примера, введем уровень набора команд, которые должна интерпретировать микропрограмма машины IJVM (см. рис. 4.5). Для удобства уровень архитектуры команд мы иногда будем называть макроархитек-

Примерархитектурыкоманд. IJVM 245

турой, чтобы противопоставить его микроархитектуре. Однако перед тем как приступать к описанию IJVM, мы немного отвлечемся.

Стек

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

К сожалению, предоставить каждой переменной абсолютный адрес в памяти невозможно. Проблема заключается в том, что процедура может вызывать себя сама. Мы рассмотрим такие рекурсивные процедуры в главе 5. А пока достаточно сказать, что если процедура вызывается дважды, то хранить ее переменные под конкретными адресами в памяти нельзя, поскольку второй вызов нарушит результаты первого.

Вместо этого используется другая стратегия. Для переменных резервируется особая область памяти, которая называется стеком, но отдельные переменные не получают в нем абсолютных адресов. Какой-либо регистр, скажем, LV, указывает на базовый адрес локальных переменных для текущей процедуры. Рассмотрим рис. 4.7, а. В данном случае вызывается процедура А с локальными переменными а1,а2и аЗ, и для этих переменных резервируется участок памяти, начинающийся с адреса, который указывается регистром LV. Другой регистр, SP, указывает на старшее слово локальных переменных процедуры А. Если значение регистра LV равно 100, а слова состоят из 4 байтов, то значение SP будет 108. Для обращения к переменной нужно вычислить ее смещение от адреса LV. Структура данных между LV и SP (включая оба указанных слова) называется фреймом локальных

переменных.

 

 

 

S P - -

 

 

 

 

 

 

LV -»-

d

SP

d5

 

 

SP-«~

Ь4

Ь4

 

d4

 

 

 

ЬЗ

ЬЗ

 

d3

 

 

 

Ь2

Ь2

 

d2

 

 

LV -»~

Ы

Ы

LV

d1

SP

аЗ

108

аЗ

аЗ

 

a3

LV

а2

104

а2

а2

 

a2

а1

100

а1

а1

 

a1

Рис, 4.7. Стек для хранения локальных переменных во время процедуры А (а); после того как процедура А вызывает процедуру В (б), после того как процедура В вызывает процедуру С (в); после того как процедуры С и в прекращаются, а процедура А вызывает процедуру D (г)

А теперь давайте посмотрим, что происходит, если процедура А вызывает другую процедуру, В. Где должны храниться 4 локальные переменные процедуры В (Ы, Ь2, ЬЗ, Ь4)? Ответ: в стеке, расположенном над стеком для процедуры Л, как показано на рис. 4.7, 6. Отметим, что после вызова процедуры регистр LV указывает уже на локальные переменные процедуры В. Обращаться к локальным переменным процедуры В можно по их сдвигу от LV. Если процедура В вызывает про-

246

Глава 4. Микроархитектурный уровень

цедуру С, то регистры LV и SP снова переопределяются и указывают на местонахождение локальных переменных процедуры С, как показано на рис. 4.7, в.

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

Предположим, что процедура А вызывает процедуру D, которая содержит 5 локальных переменных. Соответствующий стек показан на рис. 4.7, г. Локальные переменные процедуры D используют участок памяти процедуры В и часть стека процедуры С. В памяти с такой организацией размещаются только текущие процедуры. Когда процедура завершена, отведенный для нее участок памяти освобождается.

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

а1=а2+аЗ

Чтобы вычислить эту сумму, можно поместить а2 в стек, как показано на рис. 4.8, а. Тогда значение регистра SP увеличится на число, равное количеству байтов в слове (скажем, на 4), и будет указывать на адрес первого операнда. Затем в стек помещается переменная аЗ, как показано на рис. 4.8, б. Отметим, что названия процедур и переменных выбираются пользователем, а названия регистров и кодов операций встроены. Названия процедур и переменных мы выделяем в тексте курсивом.

Теперь можно произвести вычисление, выполнив команду, которая выталкивает два слова из стека, складывает их и помещает результат обратно в стек, как показано на рис. 4.8, в. После этого верхнее слово можно вытолкнуть из стека и поместить его в локальную переменную а1, как показано на рис. 4.8, г.

SP

 

SP"

 

SP-

 

 

 

 

 

а2

а2+аЗ

 

 

 

аЗ

 

аЗ

 

аЗ

SP-

аЗ

 

а2

 

а2

 

а2

 

а2

LV

а1

LV

а1

LV

а1

LV

а2+аЗ

 

а

 

б

 

в

 

г

Рис. 4.8. Использование стека операндов для арифметических действий

Фреймы локальных переменных и стеки операндов могут смешиваться. Например, когда вызывается функция i при вычислении выражения x2+f(x), часть этого выражения (х2) может находиться в стеке операндов. Результат вычисления функции остается в стеке над х2, чтобы следующая команда сложила их.

Следует упомянуть, что все машины используют стек для хранения локальных переменных, но не все используют его для операндов. В большинстве машин нет стека операндов, но у JVM и IJVM он есть. Стековые операции мы рассмотрим подробно в главе 5.

Пример архитектуры команд: IJVM

247

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

А теперь мы можем рассмотреть архитектуру IJVM. Она состоит из памяти, которую можно рассматривать либо как массив из 4 294 967 296 байтов (4 Гбайт), либо как массив из 1 073 741 824 слов, каждое из которых содержит 4 байта. В отличие от большинства архитектур команд, виртуальная машина Java не совершает обращений к памяти, видимых на уровне команд, но здесь существует несколько неявных адресов, которые составляют основу для указателя. Команды IJVM могут обращаться к памяти только через эти указатели. Определены следующие области памяти:

1.Набор констант. Эта область состоит из констант, цепочек и указателей на другие области памяти, на которые можно делать ссылку. Данная область загружается в тот момент, когда программа загружается из памяти, и после этого не меняется. Существует неявный регистр СРР (Constant Pool Pointer — указатель набора констант), который содержит адрес первого слова набора констант.

2.Фреймлокальныхпеременных. Этаобластьпредназначенадля хранения переменных во время выполнения процедуры. Она называется фреймом локальныхпеременных. В началеэтого фрейма располагаются параметры (или аргументы) вызванной процедуры. Фрейм локальных переменных не включает в себя стек операндов. Он помещается отдельно. Исходя из соображений производительности, мы поместили стек операндов прямо над фреймом локальных переменных. Существует неявный регистр, который содержит адрес первой переменной фрейма. Мы назовем этот регистр LV (Local Variable — локальная переменная). Параметры вызванной процедуры хранятся в начале фрейма локальных переменных.

3.Стек операндов. Стек операндов не должен превышать определенный размер, который заранее вычисляется компилятором Java. Пространство стека операндов располагается прямо над фреймом локальных переменных, как показано на рис. 4.9. В данном случае стек операндов удобно считать частью фрейма локальных переменных. В любом случае существует виртуальный регистр, который содержит адрес верхнего слова стека. Отметим, что в отличие от регистров СРР и LV этот указатель меняется во время выполнения процедуры, поскольку операнды помещаются в стек и выталкиваются из него.

4.Область процедур. Наконец, существует область памяти, в которой содержится программа. Есть виртуальный регистр, содержащий адрес команды, которая будет вызвана следующей. Этот указатель называется счетчиком команд, или PC (Program Counter). В отличие от других участков памяти, область процедуры представляет собой массив байтов.

Следует сделать одно примечание по поводу указателей. Регистры СРР, LV и SP указывают на слова, а не на байты, и смещения происходят на определенное число слов. Например, LV, LV+1 и LV+2 указывают на первые три слова из фрейма локальных переменных, a LV, LV+4 и LV+8 — на слова, расположенные на расстоянии четырех слов (16 байтов) друг от друга.

248

Глава 4. Микроархитектурный уровень

 

 

 

 

SP

 

 

Текущий стек

 

 

 

операндов 3

 

 

 

Текущий

 

 

 

фрейм

 

 

 

локальных

 

 

 

переменных 3

 

 

 

Фрейм

LV

 

 

Область

 

 

локальных

 

 

переменных 2

процедур

 

Набор

Фрейм

 

констант

 

 

 

локальных

PC

 

 

переменных 1

 

СРР

Рис.4.9.ОбластипамятиIJVM

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

Набор команд IJVM

Набор команд IJVM приведен в табл. 4.2. Каждая команда состоит из кода операции и иногда из операнда (например, смещения адреса или константы). В первом столбце приводится шестнадцатеричный код команды. Во втором столбце дается мнемоника языка ассемблера. В третьем столбце описывается предназначение команды.

Команды нужны для того, чтобы помещать слова из различных источников в стек. Источники — это набор констант (LDC_W), фрейм локальных переменных (ILOAD) и сама команда (BIPUSH). Переменную можно также вытолкнуть из стека и сохранить ее во фрейме локальных переменных (ISTORE). Над двумя верхними словами стека можно совершать две арифметические (IADD и ISUB) и две логические операции (IAND и I0R). При выполнении любой арифметической или логической операции два слова выталкиваются из стека, а результат помещается обратно в стек. Существует 4 команды перехода: одна для безусловного перехода (GOTO), а три другие для условных переходов (IFEQ, IFLT и IF_ICMPEQ). Все эти команды изменяют значение PC на размер их смещения, который следует за кодом операции в команде. Операнд смещения состоит из 16 битов. Он прибавляется к адресу кода операции. Существуют также команды для перестановки двух верхних слов стека (SWAP), дублирования верхнего слова (DUP) и удаления верхнего слова (POP).

Пример архитектуры команд: IJVM

249

Таблица4.2. Набор команд IJVM. Размер операндов byte, const и varnum — 1 байт. Размер операндов disp, index и offset — 2 байта

Число

Мнемоника

Примечание

0x10

BIPUSHbyfe

Помещает байт в стек

0x59

DUP

Копирует верхнее слова стека и помещает его в стек

0хА7

GOTO offset

Безусловный переход

0x60

IADD

Выталкивает два слова из стека; помещает в стек их сумму

0x7Е

IAND

Выталкивает два слова из стека; помещает в стек результат

 

 

логического умножения (операция И)

0x99

IFEQoffsef

Выталкивает слово из стека и совершает переход, если оно

 

 

равно нулю

0x9B

IFLT offset

Выталкивает слово из стека и совершает переход, если оно

 

 

меньше нуля

0x9F

IFJCMPEQ offset

Выталкивает два слова из стека; совершает переход, если

 

 

они равны

0x84

IINC varnum const

Прибавляет константу к локальной переменной

0x15

\LOAD varnum

Помещает локальную переменную в стек

0xB6

INVOKEVIRTUAL disp

Вызывает процедуру

0x80

IOR

Выталкивает два слова из стека; помещает в стек результат

 

 

логического сложения {операция ИЛИ)

OxAC

IRETURN

Выдает результат выполнения процедуры (целое число)

0x36

ISTORE varnum

Выталкивает слово из стека и запоминает его во фрейме

 

 

локальных переменных

0x64

ISUB

Выталкивает два слова из стека; помещает в стек их разность

0x13

LDCJN index

Берет константу из набора констант и помещает ее в стек

0x00

NOP

Не производит никаких действий

0x57

POP

Удаляет верхнее слово стека

0x5F

SWAP

Переставляет два верхних слова стека

0xC4

WIDE

Префиксная команда; следующая команда содержит

 

 

16-битный индекс

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

Наконец, существует команда для вызова другой процедуры (INVOKEVIRTUAL) и команда для выхода из текущей процедуры и возвращения к процедуре, из которой она была вызвана. Из-за сложности механизма мы немного упростили определение. Ограничение состоит в том, что, в отличие от языка Java, в нашем примере процедура может вызывать только такую процедуру, которая находится внутри нее. Это ограничение сильно искажает язык Java, но зато позволяет представить более простой механизм, избегая требования размещать процедуру динамически. (Если вы не знакомы с объектно-ориентированным программированием, вы можете пропустить это предложение. Мы просто превратили языкJava из объектно-

250 Глава 4. Микроархитектурный уровень

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

Механизм вызова процедуры состоит в следующем. Сначала вызывающая программа помещает в стек указатель на объект, который нужно вызвать. На рис. 4.10, а этот указатель обозначен буквами OBJREF. Затем вызывающая программа помещает в стек параметры процедуры (в данном примере Параметр 1, Параметр 2 и Параметр 3). После этого выполняется команда INVOKEVIRTUAL.

 

 

 

Стекпослевыполнения

 

 

 

 

команды INVOKEVIRTUAL

 

 

 

LVвызывающей

-SP

 

 

 

процедуры

 

 

 

 

PC вызывающей

 

 

 

 

процедуры

 

 

 

 

Пространство

 

 

 

 

длялокальных

 

 

 

 

переменных

 

 

Стекдовыполнения

 

вызывающей

 

 

 

процедуры

 

 

команды INVOKEVIRTUAL

 

 

 

Основание стека

Параметр 3

 

 

 

 

 

Параметр 3

SP после выполнения

Параметр 2

 

Вытолкнутые

Параметр 2

команды

 

Параметр1

 

INVOKEVIRTUAL

 

из стека

Параметр1

 

 

Связующий

 

параметры

 

 

 

 

LV

OBJREF

 

указатель

 

 

 

Предыдущее

 

Предыдущее

 

 

значениеLV

 

значениеLV

 

 

Предыдущее

 

Предыдущее

 

 

значениеPC

 

значениеPC

 

Фрейм

Фрейм локальных

 

Фрейм локальных

 

локальных

переменных

Основание стека

переменных

 

переменных

вызывающей

вызывающей

 

вызывающей

процедуры

довыполнения

процедуры

 

процедуры

Параметр 2

команды

Параметр 2

 

 

Параметр1

INVOKEVIRTUAL

Параметр1

 

 

 

 

 

Связывающий

LV

Связывающий

 

 

указатель

 

указатель

 

Рис. 4.10. Памятьдо выполнения команды INVOKEVIRTUAL (а); памятьпослевыполненияэтойкоманды(б)

Команда INVOKEVIRTUAL включает в себя относительный адрес (disp). Он указывает на позицию в наборе констант. В этой позиции содержится начальный адрес вызываемой процедуры, которая хранится в области процедур. Первые 4 байта в области процедур содержат специальные данные. Первые два байта представляют собой целое 16-битное число, указывающее на количество параметров данной процедуры (сами параметры были ранее помещены в стек). В данном случае OBJREF считается параметром: параметром 0. Это 16-битное целое число вместе со значением SP дает адрес OBJREF. Отметим, что регистр LV указывает на OBJREF, а не

Пример архитектуры команд: IJVM

251

на первый реальный параметр. Выбор, на что указывает LV, в какой-то степени произволен.

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

Ниже описывается, что происходит перед вызовом процедуры (см. также рис. 4.10). Два байта без знака, которые следуют за кодом операции, используются для индексирования таблицы констант (первый байт — это старший байт). Команда вычисляет базовый адрес нового фрейма локальных переменных. Для этого из указателя стека вычитается число параметров, a LV устанавливается на OBJREF. В OBJREF хранится адрес ячейки, в которой находится старое значение'РС. Этот адрес вычисляется следующим образом. К размеру фрейма локальных переменных (параметры + локальные переменные) прибавляется адрес, содержащийся в регистре LV. Сразу над адресом, в котором должно быть сохранено старое значение PC, находится адрес, в котором должно быть сохранено старое значение LV. Над этим адресом начинается стек для новой вызванной процедуры. SP указывает на старое значение LV, адрес которого находится сразу под первой пустой ячейкой стека. Помните, что SP всегда указывает на верхнее слово в стеке. Если стек пуст, то SP указывает на адрес, который находится непосредственно под стеком, поскольку стек заполняется снизу вверх.

И наконец, для выполнения команды INVOKEVIRTUAL нужно сделать так, чтобы PC указывал на пятый байт в кодовом пространстве процедуры.

Команда IRETURN противоположна команде INVOKEVIRTUAL (рис. 4.11). Она освобождает пространство, используемое процедурой. Она также возвращает стек в предыдущее состояние, за исключением того, что: 1) OBJREF и все параметры удаляются из стека; 2) возвращенное значение помещается в стек, туда, где раньше находился OBJREF. Чтобы восстановить прежнее состояние, команда IRETURN должна вернуть прежние значения указателей PC и LV. Для этого она обращается к связующему указателю (это слово, определяемое текущим значением LV). В этом месте, где изначально находился параметр OBJREF, команда OBJREF сохранила адрес, содержащий старое значение PC. Это слово и слово над ним извлекаются, чтобы восстановить старые значения PC и LV соответственно. Возвращенное значение, которое хранится на самой вершине стека завершающейся процедуры, копируется туда, где изначально находился OBJREF, и теперь SP указывает на этот адрес. И тогда управление переходит к команде, которая следует сразу за INVOKEVIRTUAL.

До сих пор у нашей машины не было никаких команд ввода-вывода. Мы и не собираемся их вводить. В нашем примере, как и в виртуальной машине Java, они не нужны, и в описании J V M никогда не упоминаются процессы ввода-вывода. Считается, что машина без ввода-вывода более надежна. (Чтение и запись осуществляются в J V M путем вызова специальных процедур.)

252

Глава 4. Микроархитектурный уровень

 

 

 

Стек до выполнения

 

 

 

 

команды I RETURN

 

 

 

 

Возвращаемое

SP

 

 

 

значение

 

 

 

Предыдущее

 

 

 

 

значение LV

 

 

 

 

Предыдущее

 

 

 

 

значение PC

 

 

 

 

локальные

 

 

 

 

переменные

 

 

 

 

вызывающей

 

 

 

 

процедуры

 

 

 

 

Параметр 3

Основание стека

Стек после

 

 

Параметр 2

выполнения

 

 

до выполнения

 

 

Параметр 1

команды IRETURN

 

 

команды IRETURN

 

 

Связующий

LV

Возвращаемое

 

 

указатель

значение

 

 

 

 

 

Предыдущее

 

Предыдущее

 

 

значение LV

 

значение LV

 

 

Предыдущее

 

Предыдущее

 

 

значение PC

 

значение PC

 

 

Локальные

 

Локальные

Локальные

переменные

 

переменные

переменные

вызывающей

Основание стека

вызывающей

вызывающей

процедуры

процедуры

после выполнения

процедуры

Параметр 2

Параметр 2

команды IRETURN

 

 

Параметр 1

Параметр 1

 

 

 

 

 

Связующий

 

Связующий

 

 

указатель

 

указатель

SP

LV

Рис. 4 . 1 1 . Память до выполнения команды IRETURN (a); память после выполнения этой команды (б)

Компиляция Java для IJVM

А теперь рассмотрим, как язык Java связывается с машиной IJVM. В листинге 4.1 представлен небольшой фрагмент программы на языке Java. Компилятор Java должен был бы переделать эту программу в программу на языке ассемблер IJVM. Эта программа приведена в листинге 4.2. Цифры с 1 по 15 в левой части листинга, а также комментарии за значком «//» не являются частью самой программы. Они даны для наглядности и просто облегчают понимание. Затем ассемблер Java транслировал бы ее в программу в двоичном коде. Эта программа приведена в листинге 4.3. (В действительности компилятор Java сразу производит двоичную программу.) В данном примере i — локальная переменная 1, j — локальная переменная 2, а к — локальная переменная 3.

Листинг4 . 1 . Фрагмент программы наязыкеJava

I—3) k-0:

else

J-J-l: