компиляторы, интерпретаторы, байт коды ... (было anti zend - миф или правда)

Crazy

Developer
Автор оригинала: si
своими словами изложить СВОЕ видение данного вопроса на текущий момент истории ?
Мое видение? Легко. Поток сознания. Преднамеренно не буду перечитывать при публикации. :)

Начнем с интерпретаторов и компиляторов. Сама идея "чистых концепций" себя изжила полностью.

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

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

Идеальным сочетанием здесь является juice, в котором компилятор порождает и сериализует в объектный файл дерево разбора. Это дает нам следующие плюсы:

1. Компилятору неважна целевая платформа. Вообще.
2. Исполняющей среде неважен язык. Вообще.
3. Наличия дерева разбора позволяет эффективно строить код под целевую среду.

Третий пункт -- это как раз то, во что уперлись разработчики первых JIT'ов. Дело в том, что архитектура JVM очень отличается от архитектуры интеловских процессоров. Кардинально.

Соответственно, очень трудно, имея программу в машинных кодах JVM (я преднамеренно не буду пока употреблять термин "байткод"), очень трудно породжить оптимальный код для нативной архтектуры. Результатом стала совершенно ублюдочная скорость первых JIT'ов (не говоря уж о чистой интерпретирующей JVM).

Навскидку не скажу, кому первому пришла в голову здравая мысль, но очень быстро из "а что же делать" проблема переросла в "это очевидно". Суть в том, что имея машинный код можно провести операцию, обратную компиляции. Т.е. восстановить дерево разбора. А уж на него натравить оптимизирующий кодогенератор.

Здесь разработчики дружно воспели хвалу горе-разработчикам компилятора javac, который нихрена не умеет оптимизировать. Их раздолбайство и спасло Java.

Соответственно, сейчас любая серверная JVM (думаю, нет нужды говорить, что их делает не только Sun), вооруженная знанием о том, как работает javac, шустро декомпилирует код и компилирует его заново. В родные, нативные коды.

В результате в некоторых тестах Java обставляет C++. Но странного в этом, как мы видим, в этом нет ни капли.

Разработчики исполняющей среды .net тоже наклали на juice, но все же перед их глазами было наглядно распято тельце зверька под названием "чистая интерпретирующая JVM". Это зрелище так их потрясло, что они решили добавить в машинный код своей платформы дополнительные метаданные, которые позволяли легко и почти без извращений докомпилить в нативный код. По крайней мере, для интелов это сработало.

Теперь к байткодам: начнем с того, что первым примером успешного и раскрученного переносимого компилятора был UCSD Pascal. Его компилятор формировал машинный код некоей не существовавшей в железе пи-машины. И была написана исполняющая среда p-system.

Успех породил многочисленные подражания. Поскольку использовать термин пи-код было спорно с точки зрения авторских прав, то ему придумали замену. У пи-кода была особенность: его команды, если мне не изменяет память, в массе своей были байтовыми. Соответственно "байткод" было очевидной для многих заменой.

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

Теперь вернемся к Zend. По имеющимся косвенным данным -- это байткод. Т.е. именно машинный код вирутуальной zend-машины, :) а не дерево разбора. Как водится, дополненный метаданными, которые, судя по косвенным данным, предназначены для упрощения отладки скрипта, но не для его оптимизации. Вероятность моей ошибки здесь высока.

Можно ли восстановить исходный код из байткода? Обычно -- да. Причина здесь в том, что, как было показано выше на примере Java, разработчики системы команд и исполняющей системы обычно не делают существенной оптимизации на уровне компилируемого машинного кода, подразумевая, что оптимизация либо не будет нужна вовсе (Tcl, JavaScript), либо что она будет сделана перекомпиляцией, а здесь предварительная оптимизация только вредит.

Можно ли реализовать eval для таких систем? Как ни странно, ответ вообще не зависит от "нативности" кода. Т.е. пытаясь реализовать eval для системы с пи-кодом или для интеловского процессора мы столкнемся с одной и той же проблемой. И это естественно ввиду отсутствия концептуальной разницы между нативным кодом и пи-кодом.

Перед нами встанут такие проблемы:

1. Обеспечение языкового окружения при компиляции (для этого есть специальное слово, но меня ломает его вспоминать). Т.е. если мы пишем eval('a=b'), то компилятор должен откуда-то узнать, что a -- это байтовая переменная, а b -- интовая. И выругаться.
2. Обеспечение связывания. После компиляции код должен быть временно (это важно) быть встроен в нужное место. К примеру, Turbo Pascal умеет генерировать код для работы с матемаьтикой, который при первом использовании замещается нативными командами для использования сопроцессора, либо для обращения к эмулятору -- но делается это раз и навсегда. До выгрузким программы. Мы же должны обеспечить выгрузку кода, который создал eval.

Для решения обеих задач нам нужны метаданные. Технических проблем с их порождением нет. Соответственно, реализуемость eval зависит только от того, насколько он был интересен разработчикам.

Поскольку у меня уже руки устли, на этом остановимся.

Или к чему-то вернемся подробнее?

P.S. Все факты изложены по памяти. Мог наврать. Но мое личное мнение это отражает.
 

valyala

Новичок
Ничего не понял. Но попытаюсь вставить свои "пять копеек".

1) Для того, чтобы исходная программа могла работать с eval'ом, ей необходим полноценный компилятор языка, который поддерживает eval. Обычно это тот же самый язык, на котором написана программа. Компилятор может являться частью исходной программы или динамически подключаться (вызываться) при необходимости.

2) Eval может существовать только в тех языках программирования, которые хранят ВСЮ таблицу символов в компилируемом коде, по которой без труда можно восстановить имена всех объектов в исходной программе.

Доказательство:
Для решения обеих задач нам нужны метаданные. Технических проблем с их порождением нет.
Допустим, у нас есть программа, написанная на "гипотетическом" языке программирования, поддерживающем eval:
PHP:
$var = 1234;
eval($_GET['eval_me']);
// интересно, а значение $var изменилось?
Даже не представляю, какие метаданные нужно сгенерировать при компиляции, чтобы обойтись без полноценного компилятора во время выполнения этой программы.

RTTI, которое есть ни что иное, как возвращение данных языка высокого уровня обратно в машинный код, есть уже во всех реально применяемых компиляторах (ну кроме, разве что, C--).
RTTI есть ни что иное, как информация о типе на этапе выполнения. В C++ она поддерживается весьма ограниченно. А именно - для полиморфных объектов на уровне typeid, а также dynamic_cast при понижающем и перекрестном приведении. Неполиморфные объекты не имеют вовсе никакой RTTI.

Теперь вернемся к Zend. По имеющимся косвенным данным -- это байткод. Т.е. именно машинный код вирутуальной zend-машины, а не дерево разбора.
Какие еще косвенные данные? Загляните в /Zend/zend_compile.h и найдите там строчку
, за которой заdefine'ны команды "виртуальной zend-машины".

Как водится, дополненный метаданными, которые, судя по косвенным данным, предназначены для упрощения отладки скрипта, но не для его оптимизации.
См. выше пункт 2 насчет eval'а. А еще ПХП5 хранит в промежуточном коде комментарии, которые начинаются на /** и заканчиваются */ и находятся перед определением класса или функции. За подробностями обращайтесь к первоисточнику: http://us2.php.net/manual/en/language.oop5.reflection.php

Насчет "байт-кода", JIT'ов, jiuce'ов, JVM, .NET и иже с ними: промежуточный код придумали на заре вычислительной техники. Читайте классику http://oz.by/books/more102900.html. Все новое - это хорошо забытое старое.
 

Crazy

Developer
Автор оригинала: valyala
1) Для того, чтобы исходная программа могла работать с eval'ом, ей необходим полноценный компилятор языка
Если под "полноценный" понимается "понимающий язык в полном объеме", то да.

2) Eval может существовать только в тех языках программирования, которые хранят ВСЮ таблицу символов в компилируемом коде
Вся не нужна. Нужно только то, что потенциально доступно в месте, где вызван eval.

Код:
function foo() {
  $x = 1;
  ...
}
function bar() {
  $y = 2;
  eval($q);
}
В данном случае для выполнения eval нужны символы 'foo', 'bar' и 'y', но однозначно не нужен 'x'.

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

RTTI есть ни что иное, как информация о типе на этапе выполнения. В C++ она поддерживается весьма ограниченно. А именно - для полиморфных объектов на уровне typeid, а также dynamic_cast при понижающем и перекрестном приведении. Неполиморфные объекты не имеют вовсе никакой RTTI.
И? Это ты подтверждаешь высказанное мной тверждением или пытался его опровергать?

Какие еще косвенные данные? Загляните в /Zend/zend_compile.h и найдите там строчку
, за которой заdefine'ны команды "виртуальной zend-машины".
Это сообщение было помещено в топике, посвященном энкодеру. Лично у меня его исходников нет.

Насчет "байт-кода", JIT'ов, jiuce'ов, JVM, .NET и иже с ними: промежуточный код придумали на заре вычислительной техники. Читайте классику http://oz.by/books/more102900.html. Все новое - это хорошо забытое старое.
Ты читаешь невнимательно.Я повторю: "первым примером успешного и раскрученного переносимого компилятора был UCSD Pascal." Не первым в мире. Разница понятна.

А с juice ты, извини, вообще ткнул пальцем в небо. Впрочем, если ты покажешь конкретную цитату из этой книжки.
 

Sam

Новичок
читал, много думал. )
спасибо

афтар, пишы исчо )
 
Сверху