Согласование дизайна виртуальной машины с основным языком программирования

В качестве фона для побочного проекта я читал о различных проектах виртуальных машин, и, конечно же, JVM получил максимальную отдачу. Я также посмотрел на BEAM (Erlang), GHC RTS (вроде, но не совсем VM) и некоторые из реализаций JavaScript. У Python также есть интерпретатор байт-кода, который я знаю, но он мало что читал.

То, что я не нашел, является хорошим объяснением того, почему конкретные варианты выбора виртуальной машины сделаны для определенного языка. Меня особенно интересуют варианты дизайна, которые бы соответствовали параллельным и/или очень динамичным (Ruby, JavaScript, Lisp) языкам.


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

Вот еще один пример: для Haskell документ, на который нужно обратить внимание, Внедрение ленивых функциональных языков на биржевом оборудовании: Бесконечная безматрица G-машина. Это очень отличается от любого другого типа VM, о котором я знаю. На самом деле GHC (премьера реализации Haskell) не работает вживую, а используется как промежуточный шаг в компиляции. Пейтон-Джонс перечисляет не менее 8 других виртуальных машин, которые не работают. Я хотел бы понять, почему какая-то виртуальная машина преуспевает там, где другие терпят неудачу.

Ответы

Ответ 1

Я отвечу на ваш вопрос по другому вопросу: что такое виртуальная машина? VM - это просто спецификация "переводчика" языка более низкого уровня, чем исходный язык. Здесь я использую значение черного слова слова "переводчик". Мне все равно, как внедрена виртуальная машина (как встроенный байт-код, JIT-компилятор, что угодно). При формулировке этого способа, с точки зрения дизайна, VM не является интересным, это язык низкого уровня.

Идеальный язык VM будет делать две вещи. Во-первых, это упростит сбор исходного языка в нем. И два это также облегчит интерпретацию на целевой платформе (ых) (где снова интерпретатор может быть реализован очень наивно или может быть действительно сложным JIT, как Hotspot или V8).

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

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

Теперь, более широко, концептуально любой компилятор можно рассматривать как серию преобразований от исходного языка к промежуточным формам, которые сами могут рассматриваться как языки для виртуальных машин. VM для промежуточных языков никогда не могут быть построены, но они могут быть. Компилятор в конечном итоге испускает окончательную форму. И эта окончательная форма сама будет языком для виртуальной машины. Мы могли бы назвать это VM "JVM", "V8"... или мы могли бы назвать это VM "x86", "ARM" и т.д.

Надеюсь, что это поможет.

Ответ 2

Один из методов получения виртуальной машины - просто перейти по цепочке компиляции, превратив исходный язык на все более и более низкоуровневые промежуточные языки. Когда вы заметите достаточно низкий уровень языка, подходящий для плоского представления (то есть тот, который может быть сериализован в последовательность "инструкций" ), это в значительной степени ваша виртуальная машина. И ваш интерпретатор VM или JIT-компилятор просто продолжит цепочку преобразований с той точки, которую вы выбрали для сериализации.

Некоторые методы сериализации очень распространены - например, использование представления псевдо-стека для деревьев выражений (например, в .NET CLR, которое вообще не является "реальной" машиной стека). В противном случае вы можете использовать SSA-форму для сериализации, как в LLVM, или просто 3-адресную VM с бесконечным количеством регистров (как в Dalvik). На самом деле не имеет значения, какой способ вы принимаете, поскольку это всего лишь сериализация, и позже она будет де-сериализована для продолжения вашего обычного способа компиляции.

Это немного другая история, если вы намерены сразу интерпретировать ваш VM-код, а не компилировать его. В настоящее время нет консенсуса в отношении того, какие виртуальные машины лучше подходят для интерпретации. Как стек, так и (или я смею сказать, Forth-) основанные виртуальные машины и основанные на регистрации, оказались эффективными.

Ответ 3

Я нашел эту книгу полезной. В нем обсуждаются многие вопросы, о которых вы просите. (заметьте, я никоим образом не связан с Amazon, и я не продвигаю Amazon, просто было самым простым местом для ссылки).

http://www.amazon.com/dp/1852339691/