В компьютерном программировании , A р-машинный код ( портативный машинный код [1] ) представляет собой виртуальная машина предназначена для выполнения п-коды (на языке ассемблера или машинный код гипотетического центрального процессора (CPU)). Этот термин применяется как в целом ко всем таким машинам (таким как виртуальная машина Java (JVM) и предварительно скомпилированный код MATLAB ), так и к конкретным реализациям, наиболее известной из которых является p-Machine системы Pascal-P , в частности, UCSD Pascal. реализации, в том числе разработчиков , чьи, то рв p-коде чаще всего означал псевдокод, чем переносимый , поэтому псевдокод означал инструкции для псевдо-машины.
Хотя концепция была впервые реализована примерно в 1966 году (как O-код для базового комбинированного языка программирования ( BCPL ) и P-код для языка Euler ), [2] термин p-код впервые появился в начале 1970-х годов. Двумя ранними компиляторами, генерирующими p-код, были компилятор Pascal-P в 1973 году Кесав В. Нори, Урс Амманн, Кэтлин Йенсен, Ханс-Генрих Нэгели и Кристиан Якоби [3] и компилятор Pascal-S в 1975 году, автор: Никлаус Вирт .
Программы, которые были переведены в p-код, могут либо интерпретироваться программой, которая имитирует поведение гипотетического ЦП, либо транслироваться в машинный код ЦП, на котором программа должна запускаться и затем выполняться. Если есть достаточный коммерческий интерес, может быть построена аппаратная реализация спецификации ЦП (например, Pascal MicroEngine или версия процессора Java ).
Преимущества и недостатки внедрения p-кода
По сравнению с прямым переводом в машинный код , двухэтапный подход, включающий перевод в p-код и выполнение посредством интерпретации или JIT -компиляции, предлагает несколько преимуществ.
- Намного проще написать небольшой интерпретатор p-кода для новой машины, чем модифицировать компилятор для генерации собственного кода для той же машины.
- Генерация машинного кода - одна из наиболее сложных частей написания компилятора. Для сравнения, генерация p-кода намного проще, потому что при генерации байт-кода не должно учитываться машинно-зависимое поведение . Это делает его полезным для быстрого запуска и запуска компилятора.
- Поскольку p-код основан на идеальной виртуальной машине, программа p-кода часто намного меньше, чем та же программа, переведенная в машинный код.
- Когда p-код интерпретируется, интерпретатор может применять дополнительные проверки во время выполнения , которые трудно реализовать с помощью машинного кода.
Одним из существенных недостатков p-кода является скорость выполнения, которую иногда можно исправить с помощью JIT-компиляции. P-код часто легче реконструировать, чем собственный код.
В начале 1980-х по крайней мере две операционные системы достигли машинной независимости благодаря широкому использованию p-кода. Операционная система Бизнес (BOS) была операционная система кросс-платформа , разработанная для запуска программ , р-код исключительно. UCSD р-система , разработанная в Университете Калифорнии, Сан - Диего, был самостоятельной компиляции и самостоятельной хостинг [ разъяснение необходимости ] операционная система на основе р-кода , оптимизированного для генерации на Pascal языке.
В 1990-х годах перевод в p-код стал популярной стратегией для реализации таких языков, как Python , Microsoft P-Code в Visual Basic и байт-код Java в Java . [4] [ неудачная проверка ]
В языке Go используется универсальная переносимая сборка как форма p-кода, реализованная Кеном Томпсоном как расширение работы Bell Labs над Plan 9 . В отличие от байт-кода Common Language Runtime (CLR) или байт-кода JVM, здесь нет стабильной спецификации, и инструменты сборки Go не генерируют формат байт-кода, который будет использоваться в более позднее время. Ассемблер Go использует общий язык ассемблера в качестве промежуточного представления , а исполняемые файлы Go представляют собой машинно- зависимые статически связанные двоичные файлы. [5]
UCSD p-машина
Архитектура
Как и многие другие машины с p-кодом, UCSD p-Machine является стековой машиной , что означает, что большинство инструкций берут свои операнды из стека и помещают результаты обратно в стек. Таким образом, add
инструкция заменяет два самых верхних элемента стека их суммой. Несколько инструкций требуют немедленного аргумента. Как Паскаль, р-код сильно типизированных , поддерживая логическое значение (B), символ (с), целое число (I), в режиме реального (г), набор (ы), и указатель (а) типы данных изначально.
Несколько простых инструкций:
Insn. Стек Описание стека до после adi i1 i2 i1 + i2 сложить два целых числаadr r1 r2 r1 + r2 сложить два реалаdvi i1 i2 i1 / i2 целочисленное делениеinn i1 s1 b1 установить членство; b1 = является ли i1 членом s1ldci i1 i1 загрузить целочисленную константуmov a1 a2 движениеnot b1 ~ b1 логическое отрицание
Среда
В отличие от других сред на основе стека (таких как Forth и виртуальная машина Java ), но очень похожей на реальный целевой ЦП, p-System имеет только один стек, совместно используемый кадрами стека процедур (обеспечивающий адрес возврата и т. Д.), И аргументы для местные инструкции. Три машины регистров указывают в стек (который растет вверх):
- SP указывает на вершину стека ( указатель стека ).
- MP отмечает начало активного кадра стека ( указатель метки ).
- EP указывает на место наивысшего стека, используемое в текущей процедуре ( крайний указатель ).
Также присутствует постоянная область, а под ней куча, растущая вниз по направлению к стопке. Регистр NP ( новый указатель ) указывает на верхний (наименьший используемый адрес) кучи. Когда EP становится больше NP, память машины исчерпана.
Пятый регистр, PC, указывает на текущую инструкцию в области кода.
Соглашения о вызовах
Фреймы стека выглядят так:
EP -> локальный стекSP -> ... местные жители ... параметры ... обратный адрес (предыдущий ПК) предыдущий EP динамическая ссылка (предыдущий МП) статическая ссылка (MP окружающей процедуры)MP -> возвращаемое значение функции
Последовательность вызова процедуры работает следующим образом: вызов вводится с помощью
mst n
где n
указывает разницу в уровнях вложенности (помните, что Паскаль поддерживает вложенные процедуры). Эта инструкция пометит стек, то есть зарезервирует первые пять ячеек указанного выше кадра стека и инициализирует предыдущую EP, динамическую и статическую ссылку. Затем вызывающий абонент вычисляет и передает любые параметры для процедуры, а затем выдает
чашка n, p
для вызова пользовательской процедуры ( n
количество параметров, p
адрес процедуры). Это сохранит ПК в ячейке обратного адреса и установит адрес процедуры как новый ПК.
Пользовательские процедуры начинаются с двух инструкций
ent 1, я ent 2, j
Первый устанавливает SP в MP + i
, второй устанавливает EP в SP + j
. Таким образом, по i
сути, указывается пространство, зарезервированное для локальных пользователей (плюс количество параметров плюс 5), и j
указывается количество записей, необходимых локально для стека. На этом этапе проверяется нехватка памяти.
Возврат к звонящему осуществляется через
retC
с C
указанием типа возвращаемого значения (i, r, c, b, a, как указано выше, и p, если значение не возвращается). Возвращаемое значение должно быть предварительно сохранено в соответствующей ячейке. Для всех типов, кроме p, при возврате это значение останется в стеке.
Вместо вызова пользовательской процедуры (чашки) q
можно вызвать стандартную процедуру с помощью
csp q
Эти стандартные процедуры являются процедурами Паскаля, такими как readln()
( csp rln
), sin()
( csp sin
) и т. Д. Особенностью eof()
является инструкция p-кода.
Пример машины
Никлаус Вирт определил простую машину с p-кодом в книге 1976 года « Алгоритмы + структуры данных = программы» . У машины было 3 регистра - счетчик программ p , базовый регистр b и регистр вершины стека t . Было 8 инструкций:
- горит 0, a : постоянная нагрузки a
- opr 0, a : выполнить операцию a (13 операций: RETURN, 5 математических функций и 7 функций сравнения)
- lod l , a : переменная нагрузки l, a
- sto l , a : сохранить переменную l, a
- cal l , a : вызвать процедуру a на уровне l
- int 0, a : увеличить t-регистр на a
- JMP 0, : переход к а
- jpc 0, a : условный переход к a [6]
Это код машины, написанный на Паскале:
const amax = 2047 ; {максимальный адрес} levmax = 3 ; {максимальная глубина вложения блоков} cxmax = 200 ; {размер массива кода}введите fct = ( lit , opr , lod , sto , cal , int , jmp , jpc ) ; инструкция = упакованная запись f : fct ; l : 0 .. levmax ; a : 0 .. amax ; конец ;вар код : массив [ 0 .. cxmax ] из инструкции ; интерпретация процедуры ; const stacksize = 500 ; var p , b , t : целое число ; {программные, базовые, верхние регистры} i : инструкция ; {} регистра команд ев : массив [ 1 .. STACKSIZE ] из целого числа ; {хранилище данных} функциональная база ( l : целое число ) : целое число ; var b1 : целое число ; начало b1 : = b ; {найти базовые уровни л} вниз , пока л > 0 действительно начинают b1 : = ев [ b1 ] ; l : = l - 1 конец ; base : = b1 end {base} ;начать писать ( 'начать пл / 0' ) ; t : = 0 ; b : = 1 ; р : = 0 ; s [ 1 ] : = 0 ; s [ 2 ] : = 0 ; s [ 3 ] : = 0 ; повторить i : = код [ p ] ; р : = р + 1 ; с я делать случай е из освещено : начинаются т : = т + 1 ; ев [ т ] : = конец ; opr : case a of {operator} 0 : begin {return} t : = b - 1 ; p : = s [ t + 3 ] ; b : = s [ t + 2 ] ; конец ; 1 : s [ t ] : = - s [ t ] ; 2 : начало t : = t - 1 ; s [ t ] : = s [ t ] + s [ t + 1 ] конец ; 3 : начало t : = t - 1 ; s [ t ] : = s [ t ] - s [ t + 1 ] конец ; 4 : начало t : = t - 1 ; s [ t ] : = s [ t ] * s [ t + 1 ] конец ; 5 : начало t : = t - 1 ; s [ t ] : = s [ t ] div s [ t + 1 ] конец ; 6 : s [ t ] : = ord ( нечетное ( s [ t ])) ; 8 : начало t : = t - 1 ; s [ t ] : = ord ( s [ t ] = s [ t + 1 ]) конец ; 9 : начало t : = t - 1 ; s [ t ] : = ord ( s [ t ] <> s [ t + 1 ]) конец ; 10 : начало t : = t - 1 ; s [ t ] : = ord ( s [ t ] < s [ t + 1 ]) конец ; 11 : начало t : = t - 1 ; s [ t ] : = ord ( s [ t ] > = s [ t + 1 ]) конец ; 12 : начало t : = t - 1 ; s [ t ] : = ord ( s [ t ] > s [ t + 1 ]) конец ; 13 : начало t : = t - 1 ; s [ t ] : = ord ( s [ t ] <= s [ t + 1 ]) конец ; конец ; lod : begin t : = t + 1 ; s [ t ] : = s [ основание ( l ) + a ] конец ; сто : начало с [ основание ( л ) + а ] : = с [ т ] ; Writeln ( s [ t ]) ; t : = t - 1 конец ; cal : begin {генерировать новую метку блока} s [ t + 1 ] : = base ( l ) ; s [ t + 2 ] : = b ; s [ t + 3 ] : = p ; б : = t + 1 ; р : = а конец ; int : t : = t + a ; jmp : p : = a ; jpc : begin, если s [ t ] = 0, то p : = a ; t : = t - 1 конец end {with, case} до p = 0 ; Writeln ( 'конец пл / 0' ) ; конец {интерпретировать} ;
Эта машина использовалась для запуска PL / 0 Вирта, компилятора подмножества Паскаля, используемого для обучения разработке компиляторов. [7] [ неудачная проверка ]
Смотрите также
- Яблоко Целое Сладкое 16
- Байт-код
- Джоэл МакКормак , разработчик версии машины p-кода для корпорации NCR
- LLVM IR
- Microsoft P-код
- Система выполнения
- Распределение токенов
Рекомендации
- ^ Аптон, Эбен; Дантеманн, Джеффри; Робертс, Ральф; Мамтора, Тим; Эверард, Бен (13 сентября 2016). Изучение компьютерной архитектуры с Raspberry Pi . Джон Вили и сыновья. ISBN 978-1-119-18393-8.
- ^ Вирт, Никлаус ; Вебер, Гельмут (1966). «EULER: обобщение АЛГОЛА и его формальное определение: Часть II» . Коммуникации ACM . Нью-Йорк, США: Ассоциация вычислительной техники (ACM). 9 (2): 89–99. DOI : 10.1145 / 365170.365202 . S2CID 12124100 .
- ^ Nori, Kesav V .; Амманн, Урс; Дженсен, Кэтлин; Нэгели, Ханс-Генрих; Якоби, Кристиан (1975). Pascal P компилятора Замечания по реализации . Цюрих, Швейцария: Eidgenössische Technische Hochschule (ETH).
- ^ «Система p-кода» .
- ^ Пайк, Роберт С. (2016). «Дизайн Go Assembler» (доклад на конференции) . Проверено 25 августа 2017 .
- ^ «Архив категорий: Вирт - Эйлер - Дизайн Никлауса Вирта и Гельмута Вебера» . Паскаль для малых машин - языки вирт, Паскаль, UCSD, Turbo, Delphi, Freepascal, Oberon . 2018-08-02.
- ^ Альперт, Дональд (сентябрь 1979 г.). Интерпретатор P-кода Pascal для Stanford Emmy (PDF) (Report). Лаборатория компьютерных систем, факультеты электронной инженерии и компьютерных наук, Стэнфордский университет. Техническая записка № 164.
дальнейшее чтение
- Пембертон, Стивен ; Дэниелс, Мартин. Реализация Pascal: компилятор и интерпретатор P4 . Джон Вили . ISBN 0-13-653031-1.
- Пембертон, Стивен , изд. (2011-04-13). «Реализация Паскаля: книга и источники» .(NB. Имеет исходные коды компилятора и интерпретатора P4 на языке Pascal , инструкции по использованию.)
- Пембертон, Стивен , изд. (2011-04-13). «pcode компилятора Pascal, скомпилированный самим собой» .(NB. Имеет p-код компилятора P4 , сгенерированный им самим.)
- "Страница компьютерного музея Джефферсона на p-System UCSD" .
- «Реализация с открытым исходным кодом» ., включая упаковку и предварительно скомпилированные двоичные файлы; дружеская вилка Klebsch. «Клебщая реализация» .
- Терри, Пэт (2005). Компиляция с C # и Java . п. 624. ISBN 0-321-26360-X.
- Вирт, Никлаус (1975). Алгоритмы + структуры данных = программы . ISBN 0-13-022418-9.
- Вирт, Никлаус (1996). Конструкция компилятора . ISBN 0-201-40353-6.
- Liffick, Blaise W., ed. (1979). Байт-книга Паскаля . ISBN 0-07-037823-1.
- Бэррон, Дэвид Уильям , изд. (1981). Паскаль: язык и его реализация . ISBN 0-471-27835-1.(NB. Особенно см. Статьи Pascal-P Implementation Notes и Pascal-S: A Subset and its Implementation .)