Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

6.2. Языковые виртуальные машины

Языковые виртуальные машины предназначены для исполнения программ на конкретном языке программирования (или семействе таких языков) в условиях различных программно-аппаратных платформах без перекомпиляции. Иными словами, упрощается портирование программ.

Рассмотрим классический пример П-кода (P-code) для виртуального выполнения программ на языка Паскаль, предложенный в середине 70-х годов. Специальный вариант компилятора Паскаля порождает платформонезависимый П-код. Для различных программно-аппаратных платформ реализованы интерпретаторы П-кода, также включающие библиотеку времени выполнения. В результате компилятор существует только в одном варианте и для каждой из новых целевых платформ достаточно реализовать небольшую программу – интерпретатор П-кода.

К ярким историческим примерам успешного портирования компьютерных игр на множество различных игровых платформ относятся текстовая игра Zork (1978), реализованная в коде виртуальной Z-машины в 1979 году, а также игра Another World (1991), имеющая изощренную виртуальную машину с поддержкой многопоточности, графики и звука.

К популярным современным языковым виртуальным машинам можно отнести:

  • Java Virtual Machine (JVM). Виртуальная машина для Java.
  • Common Language Runtime (CLR). Виртуальная машина среды .NET.
  • CPython Virtual Machine. Виртуальная машина языка Python.
  • WebAssembly (WASM). Виртуальная машина для веб-приложений.

Языковая виртуальная машина выполняет команды абстрактного процессора, ориентированного на конструкции конкретного языка или целого семейства языков. Программу в таком представлении принято называть байткодом. Такое название закрепилось исторически, оно связано с виртуальной машиной языка Smalltalk, в которой большинство команд кодировалось одним байтом.

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

Архитектуры виртуальных машин, как и архитектуры реальных процессоров, можно разделить на два класса:

  • стековые машины,
  • регистровые машины.

Рассмотрим байткод различных виртуальных машин для вычисления выражения

$$b b - 4 a c.$$

В стековой модели вычислений это выражение представляется в постфиксной форме записи:

b b * 4 a * c * -

В регистровой модели вычислений то же выражение может быть представлено трехадресным кодом:

t1 = b * b
t2 = 4 * a
t3 = t2 * c
t4 = t1 - t3

В JVM используется стековая модель вычислений:

0: iload_1
1: iload_1
2: imul
3: iconst_4
4: iload_0
5: imul
6: iload_2
7: imul
8: isub
9: ireturn

В CPython тоже используется стековая модель вычислений:

 0 LOAD_FAST                1 (b)
 2 LOAD_FAST                1 (b)
 4 BINARY_MULTIPLY
 6 LOAD_CONST               1 (4)
 8 LOAD_FAST                0 (a)
10 BINARY_MULTIPLY
12 LOAD_FAST                2 (c)
14 BINARY_MULTIPLY
16 BINARY_SUBTRACT
18 RETURN_VALUE

Виртуальная машина языка Lua использует регистровую вычислительную модель (см. числа после имен операций):

1   MUL     3 1 1   
2   MMBIN   1 1 8   ; __mul
3   MULK    4 0 0   ; 4
4   MMBINK  0 0 8 1 ; __mul 4 flip
5   MUL     4 4 2   
6   MMBIN   4 2 8   ; __mul
7   SUB     3 3 4   
8   MMBIN   3 4 7   ; __sub
9   RETURN1 3   
10  RETURN0

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

  • AOT (Ahead-of-Time). Полная трансляция байткода в машинный код целевой платформы перед ее запуском.
  • JIT (Just-in-Time). Динамическая трансляция областей программы прямо во время интерпретации байткода.