Реклама:

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

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

Hex

0x10

Мнемоника

BIPUSH byte

Примечание

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

0x59

DUP

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

0xA7

GOTO offset

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

0x60

IADD

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

0x7E

IAND

Выталкивает два слова из стека; помещает в стек результат логического умножения (операция И)

0x99

IFEQ offset

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

0x9B

IFLT offset

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

0x9F

IFJCMPEQ offset

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

0x84

I INC varnum const

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

0x15

I LOAD varnum

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

0xB6

INVOKEVIRTUALd/sp

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

0x80

IOR

Выталкивает два слова из стека; помещает в стек результат логического сложения (операция ИЛИ)

OxAC

IRETURN

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

0x36

ISTORE varnum

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

0x64

ISUB

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

0x13

LDCJ/V index

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

0x00

NOP

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

0x57

POP

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

0x5F

SWAP

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

OxC4

WIDE

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

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

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

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

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

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

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

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

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

Рис. 4.10. Память до выполнения команды 1г\]\/ОКЕ\/1ВТиА1_ (а); память после выполнения этой команды (б)

Посмотрим, что происходит перед вызовом процедуры (см. также рис. 4.10). Два байта без знака, которые следуют за кодом операции, используются для индексирования таблицы констант (первый байт - старший). Команда вычисляет базовый адрес нового фрейма локальных переменных. Для этого из указателя стека вычитается число параметров, а ЬУ устанавливается на ОВ^ЕР. В ОВ^ЕР хранится адрес ячейки, в которой находится старое значение РС. Этот адрес вычисляется следующим образом. К размеру фрейма локальных переменных (параметры + локальные переменные) прибавляется адрес, содержащийся в регистре ЬУ. Сразу над адресом, предназначенным для сохранения старого значения РС, находится адрес, в котором должно быть сохранено старое значение ЬУ. Над этим адресом начинается стек для новой вызванной процедуры. БР указывает на старое значение ЬУ, адрес которого находится сразу под первой пустой ячейкой стека. Помните, что БР всегда указывает на верхнее слово в стеке. Если стек пуст, то БР указывает на адрес, который находится непосредственно под стеком, поскольку стек заполняется снизу вверх.

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

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

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

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

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

Модель памяти IJVM || Оглавление || Компиляция IJVM