crypt of decay - MES, виртуальная машина [entries|archive|friends|userinfo]
ketmar

[ userinfo | ljr userinfo ]
[ archive | journal archive ]

MES, виртуальная машина [Dec. 8th, 2017|08:28 am]
Previous Entry Add to Memories Tell A Friend Next Entry
[Tags|]

первый из серии постов про то, как написан и работает мой компилятор скриптов. он называется «mes», в дальнейшем так и буду писать. предполагается, что читатель как минимум умеет сделать сканер и лексер, а также имеет некоторое базовое представление о том, что такое синтаксический разбор (aka «парзинг»), «виртуальная машина», «байткод», «стек».

посты будут помечены тэгом «mes internals».

исходный код на repo.or.cz: http://repo.or.cz/mes.d.git.

этот пост кратко описывает виртуальную машину.


самая простая из возможных виртуальных машин — это чисто стековая. состоит из операций типа `push op0`, `push op1`, `add` (эта снимает со стека два операнда, делает что‐то, и заталкивает результат обратно на стек). проста в реализации, в формате байткода (который действительно может быть байткодом), но не самая быстрая и удобная для всего остального.

почему не самая удобная? потому что компилятор должен вести внутренний стек, где записано, какого типа значения в данное время на стек затолкал кодогенератор. а также неудобно делать «загрузку значения по необходимости» — придётся или воротить сложный трекинг состояния стека, или забить и толкать на стек сразу как только распарзил (и заморачиваться «откручиванием» сгенерированого кода и стека назад для constant folding).

поскольку mes — это компилятор без фазы построения синтаксических деревьев, стиля «чо вижу — то сразу и пою», то я выбрал другой тип виртуальной машины. этот тип исторически (и неправильно) называется «регистровый». в нём каждая команда — это триплет (ну да, обычно считают только количество операндов; потому и «триплет») вида «opcode, destreg, srcreg0, srcreg1». т.е. `add r2,r0,r1` поместит в регистр r2 сумму значений регистров r0 и r1.

«регистр» же здесь не какой‐то там «регистр виртуального процессора», а всего лишь адрес ячейки на стеке, отсчитываемый от некоторой базы. при входе в функцию текущее значение указателя стека запоминается как база, и операции с регистрами дальше делаются просто как `stack[stackbase+regnum]`.

я хуй знает, почему это назвали регистрами, но когда вы читаете в интернетах про «регистровую виртуальную машину», то на самом деле это вот оно. Lua, например, использует именно такую.

итак. виртуальная машина mes очень простая: одна команда всегда занимает четыре байта, и для большинства команд формат именно «opcode, destreg, srcreg0, srcreg1». некоторые команды используют только `destreg` и `srcreg0`. совсем исключения — загрузка в регистры чисел, переходы и вызовы функций.

в принципе, команду перехода можно было сделать непрямой: грузим в регистр адрес, потом делаем `jump reg`. но в большинстве случаев адрес известен заранее, и мы просто будем генерировать две команды вместо одной. так что для переходов `srcreg0` и `srcreg1` объединены в одно шестнадцатибитное число, которое указывает на место приземления. `destreg` же используется в командах `JTrue` и `JFalse` — очевидно зачем.

а вот Call (вызов функции) я сделал как раз непрямым — для поддержки указателей на функции. так что для Call адрес функции сначала загружается в регистр, а потом генерируется команда `Call funcaddrreg,firstargumentreg,argumentcount`. в итоге оно выглядит примерно так:

literal r0,FuncAddr
literal r1,42
literal r2,666
literal r3,0
call r0,r1,3

вообще‐то количество аргументов — штука больше вспомогательная, для самой VM не особо и важная. но её удобно иметь для вызова «внешних функций», которые предоставлены скриптовому движку хост‐окружением. пригодится, короче.

в общем, ничего сложного тут нет (как и во всём остальном mes ;-), обычная «регистровая VM».
Linkmeow!

Comments:
From:(Anonymous)
Date:December 8th, 2017 - 08:32 am
(Link)
Слушай, а почему ты всегда пишешь "парзер", а не "парсер"?
[User Picture]
From:[info]ketmar
Date:December 8th, 2017 - 08:34 am
(Link)
а хуй знает. Так Исторически Сложилось, никакой Особенной Причины нет.
[User Picture]
From:[info]ketmar
Date:December 8th, 2017 - 08:35 am
(Link)
как с «н» и «нн», которые я ставлю совершенно рандомно.
From:(Anonymous)
Date:December 8th, 2017 - 06:29 pm
(Link)
поннятнно
[User Picture]
From:[info]steinkrauz
Date:December 8th, 2017 - 09:05 pm
(Link)
Вот тут, кстати, пишут, что регистровая ВМ ещё довольно шустрее стековой.

И мне казалось, что ParrotVM как раз регистры как регистры реализовал. Но точно не помню, сайт их что-то не отзывается, а в коде чёрт ногу сломит.
[User Picture]
From:[info]ketmar
Date:December 9th, 2017 - 06:33 am
(Link)
шустрее, но не особо намного. там пишут про всякие оптимизации — которые да, удобно делать на SSA.

а «регистры как регисты» (что бы это ни было) не имеет никакого смысла. потому что в нормальном кодогенераторе у тебя всё равно будет SSA.