Двоичный формат исполняемого кода

Команда состоит из байта кода команды и возможных аргументов. Аргументы, где это не указано явно, идут с Интеловским порядком байт. Один байт позволяет диспетчить команды прямо по таблице вычисляемым переходом и свести выполнение тривиальных операций к единицам команд ассемблера x86.


0x00 - 0x0F - 16 безоперандных команд или команд с длинным операндом

0x01 - return

0x02 - if

0x03 - dup

0x04 - swap

0x05 - drop

0x06 - summon thread (Не нужна? Удалить?)

0x07 - switch, три 32-битных целых следуют - scale, shift, max

0x08 - push catcher

0x09 - pop catcher

0x0A - throw

0x0B - summon type

0x0C - copy


0x1Y-0xFY - 15 команд с коротким или длинным операндом. Первая тетрада - номер команды, вторая - аргумент. Если вторая - ноль, аргумент следует в виде 32-битного числа в Intel-овском формате. Для команд перехода неиспользование нуля естественно, так как переход на себя нужен очень редко :-). Для команд sys, pop, push аргумент уменьшается на единицу перед использованием или, что эквивалентно, адресация слотов объекта для push/pop или системных вызовов для sys начинается с единицы. Таким образом близкие переходы или обращения к полям небольших объектов, а так же большинство "подземных" вызовов умещаются в один байт.

0x1Y - call, следует 16-битный uint - номер метода

0x2Y - jump

0x3Y - djnz

0x4Y - push

0x5Y - pop

0x6Y - move up

0x7Y - move down

0x8Y - sys

0x9Y - const - см. описание ниже

0xAY - hint - см. описание ниже

0xBY - int op - см. описание ниже


const

Y - тип.

0, 1 - bool со значением 0 и 1. Ноль байт следует.

2, 3 - целое со значением 0 и 1. Ноль байт следует.

4, 5 - целое, 8 и 32 бита.

6, 7 - строка, длина которой (в юникодах) выражена целым в 8 и 32 бита.

8, 9, 10 - float, 32, 64, 80 бит.

11, 12, 13 - резиновые Int, float, строка. Первый байт - число байт int/float, для float второй байт - число байт в порядке, строка - как int, но затем идёт сама строка, длина которой выражена int-ом.


hint

Y - номер подсказки

0 - объект, кажется, помер. Адрес объекта - на стеке. Интерпретатор снимает адрес со стека (чтобы убить последнюю ссылку), проверяет число ссылок на объект и, если оно нулевое, убивает. Если не ноль то либо компилятор погорячился (имеет право), либо это цикл и с ним будет разбираться GC.


int op

Операции над целыми на верхушке стека. Для однооперандных команд размер стека не меняется, для двухоперандных верхушка (A) снимается, следующий элемент (B) претерпевает изменение.

Y - номер операции.

0 - to bool, преобразует int в bool, если refcount == 1, то прямо на месте. :-)

1 - B += A

2 - B -= A

3 - B += A

4 - B /= A

5 - A++

6 - A--

 


Двоичный формат адреса

class phantom_ptr
{
};

class phantom_ref
{
phantom_ptr obj;
phantom_ptr interface;
};

 

Двоичный формат объекта

 

class phantom_object {

// для быстрой проверки встроенности типа
uint kind : 8; // enum { normal = 0, int, float, string, other }

int refcount; // оптимизация сборки мусора

phantom_ref data[];

};

 

Двоичный формат стека адресов возврата

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

class phantom_istack_frame {

phantom_ptr class;
// у ловушки тут - указатель на объект типа класс.
// вторая половина его ссылки константа для данной системы
// и легко при необходимости регенерируется

// у кадра вызова тут - ноль.

phantom_ptr ip, op, dsl;

// для кадра вызова - адрес возврата, адрес
// контекстного объекта, нижняя граница стека данных.
// для ловушки - только ip имеет значение и указывает на начало
// кода собственно ловушки.

};

Exceptions

Nikita V. Belenki: [генерируем] множество предков того класса, exception которого мы бросаем. А искать в нём надо идентификатор класса, который мы в данной точке стека ловим.


Сборка мусора

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

С тем, чтобы оптимизировать сей процесс в системе (в подземелье) создаются две очереди ссылок. Инкрементная и декрементная. При создании и удалении ссылок их копия помещается в соответствующую очередь. Время от времени (чаще, чем бывает сборка мусора) очереди просматривается дважды. На первом проходе счётчики объектов, ссылки на которые лежат в очередях, соответственно, увеличиваются и уменьшаются на 1 при каждой встрече их в очереди. На втором объекты, имеющие ноль ссылок уничтожаются.

При уничтожении вызывается деструктор. Если после его работы объект имеет ненулевое число ссылок, он оживает. Возможно, это следует допускать лишь один раз на объект.

 

 

 

Используются технологии uCoz