Введение
Виртуальная машина Dis обеспечивает окружение исполнения для программ, запущенных в операционной системе Inferno. Это виртуальная машина типа CISC, с трехоперандными инструкциями и архитектурой память-в-память. Код может интерпретироваться Си-библиотекой и компилироваться во время исполнения в машинные инструкции целевой архитектуры (JIT).
Этот документ описывает виртуальную машину неформально. Отдельный документ, написанный Winterbottom и Pike[2] описывает ее дизайн. Формат объектных файлов Dis также описан здесь.
Методы адресации
Размер операнда
Размеры операндов определены следующим образом: байт (byte) - 8 бит, слово (word) - 32 бита, плавающее (float) - 64 бита. Размер операнда каждой инструкции явно задан кодом операнда. Размер операнда и тип определятся последним символом мнемоникой инструкции:
W слово (word), 32 бита, знаковое B байт (byte), 8 бит, беззнаковое F плавающее (float), 64 бита, формат IEEE L большое (big), 64 бита, знаковое P указатель C Unicode-строка, закодированная в UTF-8 M память MP память, содержащая указатели
Еще два типа операндов определены для предоставления "укороченных" ("short") типов языкам, отличным от Limbo: знаковые 16-битные целые, называемые 'short word', и 32-битные числа с плавающей точкой, называемые 'short float' или 'short real'. Их поддержка ограничена конвертированием в/из слов (word) или чисел с плавающей точкой; the instructions are marked below with a dagger (FIXME).
Организация памяти
Память, выделяемая потоку, разделена на несколько отдельных регионов. Сегмент кода может содержать либо декодированный поток инструкций виртуальной машины, пригодный для исполнения интерпретатором, либо только что скопилированный код для целевого процессора. Ни один тип сегмента кода не может быть адресован из набора инструкций. На уровне объектного кода, значения PC - смещение (PC values are offsets), подсчитанное в инструкциях от начала пространства кода.
Сегмент данных - это линейный массив байт, адресуемый с помощью 32-битных указателей. Слова хранятся в представлении низлежащего процессора. Типы данных, длиннее, чем байт, должны храниться по адресам, выравненным согласно размеру сегмента данных. Поток, исполняющий модуль, имеет доступ к двум регионам адресуемого сегмента данных. Указатель модуля (module pointer, регистр mp) определяет регион глобального хранилища индивидуального модуля, указатель фрейма (frame pointer, регистр fp) определяет текущий фрейм (frame) потока. Фреймы выделяются динамически из стека инструкциями вызова функции и возврата. Стек расширяется автоматически из кучи (heap).
Регистры mp и fp не могут быть адресованы напрямую, поэтому могут быть изменены только инструкциями вызова функции и возврата.
Effective Addresses
Each instruction can potentially address three operands. The source and destination operands are general, but the middle operand can use any address mode except double indirect. If the middle operand of a three address instruction is omitted, it is assumed to be the same as the destination operand. The general operands generate an effective address from three basic modes: immediate, indirect and double indirect. The assembler syntax for each mode is:
10(fp) 30-bitsigned indirect from fp 20(mp) 30-bitsigned indirect from mp $0x123 30-bitsigned immediate value 10(20(fp)) two 16-bitunsigned offsets double indirect from fp 10(20(mp)) two 16-bitunsigned offsets double indirect from mp
Сборка мусора
The Dis machine performs both reference counted and real time mark and sweep garbage collection. This hyrbrid approach allows code to be generated in several styles: pure reference counted, mark and sweep, or a hybrid of the two approaches. Compiler writers have the freedom to choose how specific types are han- dled by the machine to optimize code for performance or language implementation. Instruction selection determines which algorithm will be applied to specific types. When using reference counting, pointers are a special operand type and should only be manipulated using the pointer instructions in order to ensure the correct functioning of the garbage collector. Every memory location that stores a pointer must be known to the interpreter so that it can be initialized and deallocated correctly. The information is transmitted in the form of type descriptors in the object module. Each type descriptor contains a bit vector for a particular type where each bit corresponds to a word in memory. Type descriptors are generated automatically by the Limbo compiler. The assembler syntax for a type descriptor is:
desc $10, 132, "001F"
The first parameter is the descriptor number, the second is the size in bytes, and the third a pointer map. The map contains a list of hex bytes where each byte maps eight 32 bit words. The most significant bit rep- resents the lowest memory address. A one bit indicates a pointer in memory. The map need not have an entry for every byte and unspecified bytes are assumed zero. Throughout this description, the symbolic constant H refers to a nil pointer.
Набор инструкций
addx - Сложение
Синтаксис:
addb src1, src2, dst addf src1, src2, dst addw src1, src2, dst addl src1, src2, dst
Функция:
dst = src1 + src2
Иструкции типа add высчитывают сумму операндов, адресуемых src1 и src2, и помещают результат в операнд dst. В случае addb результат обрезается до восьми бит
addc - Сложение строк
Синтаксис:
addc src1, src2, dst
Функция:
dst = src1 + src2
Инструкция addc связывает две UTF-строки, адресуемые src1 и src2; результат помещается по адресу dst. Если оба указателя равны H, результат будет строкой нулевого размера, вместо H. (Бред какой-то: If both pointers are H the result will be a zero length string rather than H.)
alt - Alternate between communications
Синтаксис:
alt src, dst
The alt instruction selects between a set of channels ready to communicate. The src argument is the address of a structure of the following form: � -3-
struct Alt { int nsend; /* Number of senders */ int nrecv; /* Number of receivers */ struct { Channel* c; /* Channel */ void* val; /* Address of lval/rval */ } entry[]; };
The vector is divided into two sections; the first lists the channels ready to send values, the second lists channels either ready to receive or an array of channels each of which may be ready to receive. The counts of the sender and receiver channels are stored as the first and second words addressed by src. An alt instruction proceeds by testing each channel for readiness to communicate. A ready channel is added to a list. If the list is empty after each channel has been considered, the thread blocks at the alt instruction waiting for a channel to become ready; otherwise, a channel is picked at random from the ready set. The alt instruction then uses the selected channel to perform the communication using the val address as either a source for send or a destination for receive. The numeric index of the selected vector element is placed in dst.
andx - Логическое И (AND)
Синтаксис:
andb src1, src2, dst andw src1, src2, dst andl src1, src2, dst
Функция:
dst = src1 & src2
Инструкция вычисляет побитовое И (NAD) двух операндов, адресуемых src1 и src2 и сохраняет результат в операнд dst.
beqx - Ветвление если равно
Синтаксис:
beqb src1, src2, dst beqc src1, src2, dst beqf src1, src2, dst beqw src1, src2, dst beql src1, src2, dst
Функция:
if src1 == src2 then pc = dst
Если операнд src1 равен операнду src2, то управление передается по значению программного счетчика определяемого операндом dst (по сути аналог команды je на x86 - прим. пер.).
bgex - Ветвление если больше или равно
Синтаксис:
bgeb src1, src2, dst bgec src1, src2, dst bgef src1, src2, dst bgew src1, src2, dst bgel src1, src2, dst
Функция:
if src1 >= src2 then pc = dst
Если операнд src1 больше или равен операнду src2, то управление передается по значению программного счетчика определяемого операндом dst. Эта инструкция осуществляет знаковое сравнение.
bgtx - Ветвление если больше
Синтаксис:
bgtb src1, src2, dst bgtc src1, src2, dst bgtf src1, src2, dst bgtw src1, src2, dst bgtl src1, src2, dst
Функция:
if src1 > src2 then pc = dst
Если операнд src1 больше операнда src2, то управление передается по значению программного счетчика определяемого операндом dst. Эта инструкция осуществляет знаковое сравнение.
blex - Ветвление если меньше или равно
Синтаксис:
bleb src1, src2, dst blec src1, src2, dst blef src1, src2, dst blew src1, src2, dst blel src1, src2, dst
Функция:
if src1 <= src2 then pc = dst
Если операнд src1 меньше или равен операнду src2, то управление передается по значению программного счетчика определяемого операндом dst. Эта инструкция осуществляет знаковое сравнение.
bltx - Ветвление если меньше
Синтаксис:
bltb src1, src2, dst bltc src1, src2, dst bltf src1, src2, dst bltw src1, src2, dst bltl src1, src2, dst
Функция:
if src1 < src2 then pc = dst
Если операнд src1 меньше операнда src2, то управление передается по значению программного счетчика определяемого операндом dst.
bnex - Ветвление если не равно
Синтаксис:
bneb src1, src2, dst bnec src1, src2, dst bnef src1, src2, dst bnew src1, src2, dst bnel src1, src2, dst
Функция:
if src1 != src2 then pc = dst
Если операнд src1 неравен операнду src2, то управление передается по значению программного счетчика определяемого операндом dst.
call - Вызов локальной функции
Синтаксис:
call src, dst
Функция:
link(src) = pc frame(src) = fp mod(src) = 0 fp = src pc = dst
Инструкция call осуществляет вызов функции находящейся в том же модуле. Аргумент src определяет кадр созданный new. Текущее значение pc сохраняется в link(src), текущее значение fp сохраняется в frame(src), а регистр ссылки на модуль устанавливается в 0. Значение fp становится равным src и управление передается по значению программного счетчика определяемого операндом dst.
case - Case compare integer and branch
Syntax: case src, dst Function: pc = 0..i: dst[i].pc where dst[i].lo >= src && dst[i].hi < src
The case instruction jumps to a new location specified by a range of values. The dst operand points to a table in memory containing a table of i values. Each value is three words long: the first word specifies a low value, the second word specifies a high value, and the third word specifies a program counter. The first word of the table gives the number of entries. The case instruction searches the table for the first matching value where the src operand is greater than or equal to the low word and less than the high word. Control is transferred to the program counter stored in the first word of the matching entry. � -5-
casec - Case compare string and branch
Syntax: casec src, dst Function: pc = 0..i: dst[i].pc where dst[i].lo >= src && dst[i].hi < src
The casec instruction jumps to a new location specified by a range of string constants. The table is the same as described for the case instruction.
consx - Выделить новый элемент списка
Синтаксис:
consb src, dst consc src, dst consf src, dst consl src, dst consm src, dst consmp src, dst consp src, dst consw src, dst
Функция:
p = new(src, dst) dst = p
Инструкция cons добавляет новый элемент к голове списка (list). Новый список создается из операнда src и указателя на голову существующего списка, определенного dst. Результирующий список записывается обратно в dst.
cvtac - Конвертировать массив байт в строку
Синтаксис:
cvtac src, dst
Функция:
dst = string(src)
Операнд src должен быть массивом байт, который конвертируется в строку символов и сохраняется в dst. Новая строка - это копия всех байт в src.
cvtbw - Конвертировать байт в слово
Синтаксис:
cvtbw src, dst
Функция:
dst = src & 0xff
Байт получаемый из операнда src расширяется до размера слова и сохраняется в операнде dst.
cvtca - Конвертировать строку в массив байт
Синтаксис:
cvtca src, dst
Функция:
dst = array(src)
Операнд src должен быть строкой, которая конвертируется в массив байт и сохраняется в dst. Новый массив - это копия всех символов в src.
cvtcf - Конвертировать строку в действительное
Синтаксис:
cvtcf src, dst
Функция:
dst = (float)src
Строка, адресуемая операндом src, конвертируется в значение с плавающей запятой и сохраняется в операнде dst. Предшествующие пробелы игнорируются; конвертирование прекращается на первом символе строки не являющемся представлением значения с плавающей запятой.
cvtcl - Конвертировать строку в большое целое
Синтаксис:
cvtcl src, dst
Функция:
dst = (big)src
Строка, адресуемая операндом src конвертируется в большое целое (big) и сохраняется в операнд dst. Начальные пробелы игнорируются; конвертирование прекращается на первом символе строки не являющемся представлением числа.
cvtcw - Конвертировать строку в слово
Синтаксис:
cvtcw src, dst
Функция:
dst = (int)src
Строка, адресуемая операндом src, конвертируется в слово и сохраняется в операнде dst. Начальные пробелы после возможного знака игнорируются; конвертирование прекращается на первом символе строки не являющемся цифрой.
cvtfc - Конвертировать действительное в строку
Синтаксис:
cvtfc src, dst
Функция:
dst = string(src)
Значение с плавающей запятой, адресуемое операндом src, конвертируется в строку и сохраняется в операнде dst. Строка - это вещественное представление значения.
cvtfw - Конвертировать действительное в слово
Синтаксис:
cvtfw src, dst
Функция:
dst = (int)src
Значение с плавающей запятой адресуемое операндом src конвертируется в слово и сохраняется в операнде dst. Округление происходит к ближайшему целому.
cvtfl - Конвертировать действительное в большое целое
Синтаксис:
cvtfl src, dst
Функция:
dst = (big)src
Значение с плавающей запятой адресуемое операндом src конвертируется в большое целое и сохраняется в операнде dst. Округление происходит к ближайшему целому.
cvtfr - Конвертировать действительное в короткое действительное
Синтаксис:
cvtfr src, dst
Функция:
dst = (short float)src
Значение с плавающей запятой, адресуемое операндом src, конвертируется в короткое(32 бита) значение с плавающей запятой и сохраняется в операнде dst. Округление происходит к ближайшему целому.
cvtlc - Конвертировать большое целое в строку
Синтаксис:
cvtlc src, dst
Функция:
dst = string(src)
Большое целое, адресуемое операндом src, конвертируется в строку и сохраняется в операнде dst. Строка - это десятичное представление большого целого.
cvtlw - Конвертировать большое целое в слово
Синтаксис:
cvtlw src, dst
Функция:
dst = (int)src
Большое целое, адресуемое операндом src, конвертируется в слово и сохраняется в операнде dst.
cvtsw - Конвертировать короткое слово в слово
Синтаксис:
cvtsw src, dst
Функция:
dst = (int)src
Короткое слово, адресуемое операндом dst, конвертируется в слово и сохраняется в операнде dst.
cvtwb - Конвертировать слово в байт
Синтаксис:
cvtwb src, dst
Функция:
dst = (byte)src;
Операнд src конвертируется в байт и сохраняется в операнде dst.
cvtwc - Конвертировать слово в строку
Синтаксис:
cvtwc src, dst
Функция:
dst = string(src)
Слово, адресуемое операндом src, конвертируется в строку и сохраняется в операнде dst. Строка - это десятичное представление слова.
cvtwl - Конвертировать слово в большое целое
Синтаксис:
cvtwl src, dst
Функция:
dst = (big)src;
Слово, адресуемое операндом src, конвертируется в большое целое и сохраняется в операнде dst.
cvtwf - Конвертировать слово в действительное
Синтаксис:
cvtwf src, dst
Функция:
dst = (float)src;
Слово, адресуемое операндом src, конвертируется в значение с плавающей запятой и сохраняется в операнде dst.
cvtws - Конвертировать слово в короткое слово
Синтаксис:
cvtws src, dst
Функция:
dst = (short)src;
Слово, адресуемое операндом src, конвертируется в короткое слово и сохраняется в операнде dst.
cvtlf - Конвертировать большое целое в действительное
Синтаксис:
cvtlf src, dst
Функция:
dst = (float)src;
Большое целое, адресуемое операндом src, конвертируется в значение с плавающей запятой и сохраняется в операнде dst.
cvtrf - Конвертировать короткое действительное в действительное
Синтаксис:
cvtrf src, dst
Функция:
dst = (float)src;
Короткое(32 бита) значение с плавающей запятой, адресуемое операндом src, конвертируется в 64-х битное значение с плавающей запятой и сохраняется в операнде dst.
divx - Деление
Syntax: divb src1, src2, dst divf src1, src2, dst divw src1, src2, dst divl src1, src2, dst Function: dst = src2/src1
The src2 operand is divided by the src1 operand and the quotient is stored in the dst operand. Division by zero causes the thread to terminate.
eclr - Очистка стека исключений
Синтаксис:
eclr
Обработка исключений реализуется системными примитивами, описание которых не входит в данный документ. Инструкция eclr зарезервирована для внутреннего использования реализацией, применяется для отмены обработчиков исключений при возвращении из функции; не встречается в потоке инструкций.
exit - Завершение потока
Синтаксис:
exit
Функция:
exit()
Выполняет завершение потока. Все удерживаемые в стеке ресурсы освобождаются.
frame - Allocate frame for local call
Syntax: frame src1, src2 Function: src2 = fp + src1->size initmem(src2, src1);
The frame instruction creates a new stack frame for a call to a function in the same module. The frame is ini- tialized according to the type descriptor supplied as the src1 operand. A pointer to the newly created frame is stored in the src2 operand.
goto - Computed goto
Syntax: goto src, dst Function: pc = dst[src]
The goto instruction performs a computed goto. The src operand must be an integer index into a table of PC values specified by the dst operand.
headx - Голова списка
Синтаксис:
headb src, dst headf src, dst headm src, dst headmp src, dst headp src, dst headw src, dst headl src, dst
Функция:
dst = hd src
Инструкции head создают копию первого элемента данных, находящегося в списке. Операнд src должен быть списком правильного типа. Первый элемент копируется в операнд dst. Список не модифицируется.
indc - Index by character
Syntax: indc src1, src2, dst Function: dst = src1[src2]
The indc instruction indexes Unicode strings. The src1 instruction must be a string. The src2 operand must be an integer specifying the origin-0 index in src1 of the (Unicode) character to store in the dst operand.
indx - Array index
Syntax: indx src1, dst, src2 Function: dst = &src1[src2]
The indx instruction computes the effective address of an array element. The src1 operand must be an array created by the newa instruction. The src2 operand must be an integer. The effective address of the src2 element of the array is stored in the dst operand.
indx - Index by type
Syntax: indb src1, dst, src2 indw src1, dst, src2 indf src1, dst, src2 indl src1, dst, src2 Function: dst = src1[src2]
� -9-
The indb, indw, indf and indl instructions index arrays of the basic types. The src1 operand must be an array created by the newa instruction. The src2 operand must be a non-negativeinteger index less than the array size. The value of the element at the index is loaded into the dst operand.
insc - Insert character into string
Syntax: insc src1, src2, dst Function: src1[src2] = dst
The insc instruction inserts a character into an existing string. The index in src2 must be a non-negative integer less than the length of the string plus one. (The character will be appended to the string if the index is equal to the string's length.) The src1 operand must be a string (or nil). The character to insert must be a valid 16-bitunicode value represented as a word.
jmp - Безусловное ветвление
Синтаксис:
jmp dst
Функция:
pc = dst
Управление передается в область определенную операндом dst.
lea - Загрузить эффективный адрес
Синтаксис:
lea src, dst
Функция:
dst = &src
Инструкция lea вычисляет эффективный адрес операнда src и сохраняет его в операнде dst.
lena - Длина массива
Синтаксис:
lena src, dst
Функция:
dst = nelem(src)
Инструкция lena вычисляет длину массива, заданного операндом src и сохраняет ее в операнде dst.
lenc - Длина строки
Syntax: lenc src, dst Function: dst = utflen(src)
The lenc instruction computes the number of characters in the UTF string addressed by the src operand and stores it in the dst operand.
lenl - Длина списка
Syntax: lenl src, dst Function: dst = 0; for(l = src; l; l = tl l) dst++;
The lenl instruction computes the number of elements in the list addressed by the src operand and stores the result in the dst operand.
load - Загрузить модуль
Syntax: load src1, src2, dst Function: dst = load src2 src1
The load instruction loads a new module into the heap. The module might optionally be compiled into machine code depending on the module header. The src1 operand is a pathname to the file containing the object code for the module. The src2 operand specifies the address of a linkage descriptor for the module (see below). A reference to the newly loaded module is stored in the dst operand. If the module could not be loaded for any reason, then dst will be set to H. The linkage descriptor referenced by the src2 operand is a table in data space that lists the functions imported by the current module from the module to be loaded. It has the following layout: � - 10 -
int nentries; struct { /* word aligned */ int sig; byte name[]; /* UTF encoded name, 0-terminated */ } entry[];
The nentries value gives the number of entries in the table and can be zero. It is followed by that many linkage entries. Each entry is aligned on a word boundary; there can therefore be padding before each structure. The entry names the imported function in the UTF-encodedstring in name, which is terminated by a byte containing zero. The MD5 hash of the function's type signature is given in the value sig. For each entry, load instruction checks that a function with the same name in the newly loaded exists, with the same signature. Otherwise the load will fail and dst will be set to H.
The entries in the linkage descriptor form an array of linkage records (internal to the virtual machine) associated with the module pointer returned in dst, that is indexed by operators mframe, mcall and mspawn to refer to functions in that module. The linkage scheme provides a level of indirection that allows a module to be loaded using any module declaration that is a valid subset of the implementation module's declaration, and allows entry points to be added to modules without invalidating calling modules.
lsrx - Логический сдвиг вправо
Syntax: lsrw src1, src2, dst lsrl src1, src2, dst Function: dst = (unsigned)src2 >> src1
The lsr instructions shift the src2 operand right by the number of bits specified by the src1 operand, replacing the vacated bits by 0, and store the result in the dst operand. Shift counts less than 0 or greater than the number of bits in the object have undefined results. This instruction is included for support of languages other than Limbo, and is not used by the Limbo compiler.
mcall - Межмодульный вызов
Syntax: mcall src1, src2, src3 Function: link(src1) = pc frame(src1) = fp mod(src1) = current_moduleptr current_moduleptr = src3->moduleptr fp = src1 pc = src3->links[src2]->pc
The mcall instruction calls a function in another module. The first argument specifies a new frame for the called procedure and must have been built using the mframe instruction. The src3 operand is a module reference generated by a successful load instruction. The src2 operand specifies the index for the called function in the array of linkage records associated with that module reference (see the load instruction).
mframe - Allocate inter-module frame
Syntax: mframe src1, src2, dst Function: dst = fp + src1->links[src2]->t->size initmem(dst, src1->links[src2])
The mframe instruction allocates a new frame for a procedure call into another module. The src1 operand specifies the location of a module pointer created as the result of a successful load instruction. The src2 operand specifies the index for the called function in the array of linkage records associated with that mod- ule pointer (see the load instruction). A pointer to the initialized frame is stored in dst. The src2 operand specifies the linkage number of the function to be called in the module specified by src1.
mnewz - Allocate object given type from another module
Syntax: mnewz src1, src2, dst Function: dst = malloc(src1->types[src2]->size) initmem(dst, src1->types[src2]->map)
The mnewz instruction allocates and initializes storage to a new area of memory. The src1 operand speci- fies the location of a module pointer created as the result of a successful load instruction. The size of the � - 11 -
new memory area and the location of pointers within it are specified by the src2 operand, which gives a type descriptor number within that module. Space not occupied by pointers is initialized to zero. A pointer to the initialized object is stored in dst. This instruction is not used by Limbo; it was added to implement other languages.
modx - Modulus
Syntax: modb src1, src2, dst modw src1, src2, dst modl src1, src2, dst Function: dst = src2 % src1
The modulus instructions compute the remainder of the src2 operand divided by the src1 operand and store the result in dst. The operator preserves the condition that the absolute value of a%b is less than the absolute value of b; (a/b)*b + a%b is always equal to a.
movx - Move scalar
Syntax: movb src, dst movw src, dst movf src, dst movl src, dst Function: dst = src
The move operators perform assignment. The value specified by the src operand is copied to the dst operand.
movm: move memory
Syntax: movm src1, src2, dst Function: memmove(&dst, &src1, src2)
The movm instruction copies memory from the src1 operand to the dst operand for src2 bytes. The src1 and dst operands specify the effective address of the memory rather than a pointer to the memory.
movmp - Move memory and update reference counts
Syntax: movmp src1, src2, dst Function: decmem(&dst, src2) memmove(&dst, &src1, src2->size) incmem(&src, src2)
The movmp instructions performs the same function as the movm instruction but increments the reference count of pointers contained in the data type. For each pointer specified by the src2 type descriptor, the corresponding pointer reference count in the destination is decremented. The movmp instruction then copies memory from the src1 operand to the dst operand for the number of bytes described by the type descrip- tor. For each pointer specified by the type descriptor the corresponding pointer reference count in the source is incremented.
movp - Move pointer
Syntax: movp src, dst Function: destroy(dst) dst = src incref(src)
The movp instruction copies a pointer adjusting the reference counts to reflect the new pointers.
movpc - Move program counter
Syntax: movpc src, dst Function: dst = PC(src);
The movpc instruction computes the actual address of an immediate PC value. The dst operand is set to the actual machine address of the instruction addressed by the src operand. This instruction must be used to calculate PC values for computed branches. � - 12 -
mspawn - Module spawn function
Syntax: mspawn src1, src2, src3 Function: fork(); if(child){ link(src1) = 0 frame(src1) = 0 mod(src1) = src3->moduleptr current_moduleptr = src3->moduleptr fp = src1 pc = src3->links[src2]->pc }
The mspawn instruction creates a new thread, which starts executing a function in another module. The first argument specifies a new frame for the called procedure and must have been built using the mframe instruction. The src3 operand is a module reference generated by a successful load instruction. The src2 operand specifies the index for the called function in the array of linkage records associated with that module reference (see the load instruction above).
mulx - Умножение
Синтаксис:
mulb src1, src2, dst mulw src1, src2, dst mulf src1, src2, dst mull src1, src2, dst
Функция:
dst = src1 * src2
Выполняет умножение операнда src1 на операнд src2 и помещает результат в операнд dst.
nbalt - Non blocking alternate
Syntax: nbalt src, dst
The nbalt instruction has the same operands and function as alt , except that if no channel is ready to communicate, the instruction does not block. When no channels are ready, control is transferred to the PC in the last element of the table addressed by dst.
negf - Negate real
Syntax: negf src, dst Function: dst = -src
The floating point value addressed by the src operand is negated and stored in the dst operand.
new, newz - Выделить объект
Syntax: new src, dst newz src, dst Function: dst = malloc(src->size); initmem(dst, src->map);
The new instruction allocates and initializes storage to a new area of memory. The size and locations of pointers are specified by the type descriptor number given as the src operand. A pointer to the newly allo- cated object is placed in dst. Any space not occupied by pointers has undefined value. The newz instruction additionally guarantees that all non-pointervalues are set to zero. It is not used by Limbo.
newa, newaz - Выделить массив
Syntax: newa src1, src2, dst newaz src1, src2, dst Function: dst = malloc(src2->size * src1); for(i = 0; i < src1; i++) initmem(dst + i*src2->size, src2->map);
� - 13 -
The newa instruction allocates and initializes an array. The number of elements is specified by the src1 operand. The type of each element is specified by the type descriptor number given as the src2 operand. Space not occupied by pointers has undefined value. The newaz instruction additionally guarantees that all non-pointervalues are set to zero; it is not used by Limbo.
newcx - Выделить канал
Syntax: newcw dst newcb dst newcl dst newcf dst newcp dst newcm src, dst newcmp src, dst Function: dst = new(Channel)
The newc instruction allocates a new channel of the specified type and stores a reference to the channel in dst. For the newcm instruction the source specifies the number of bytes of memory used by values sent on the channel (see the movm instruction above). For the newcmp instruction the first operand specifies a type descriptor giving the length of the structure and the location of pointers within the structure (see the movmp instruction above).
orx - Логическое ИЛИ (OR)
Syntax: orb src1, src2, dst orw src1, src2, dst orl src1, src2, dst Function: dst = src1 | src
These instructions compute the bitwise OR of the two operands addressed by src1 and src2 and store the result in the dst operand.
recv - Получить из канала
Синтаксис
recv src, dst
Функция:
dst = <-src
The recv instruction receives a value from some other thread on the channel specified by the src operand. Communication is synchronous, so the calling thread will block until a corresponding send or alt is per- formed on the channel. The type of the received value is determined by the channel type and the dst operand specifies where to place the received value.
ret - Возврат из функции
Syntax: ret Function: npc = link(fp) mod = mod(fp) fp = frame(fp) pc = npc
The ret instruction returns control to the instruction after the call of the current function.
send - Отправить в канал
Синтаксис:
send src, dst
dst <-= src
The send instruction sends a value from this thread to some other thread on the channel specified by the dst operand. Communication is synchronous so the calling thread will block until a corresponding recv or alt is performed on the channel. The type of the sent value is determined by the channel type and the dst operand specifies where to retrieve the sent value. � - 14 -
shlx - Арифметический сдвиг влево
Syntax: shlb src1, src2, dst shlw src1, src2, dst shll src1, src2, dst Function: dst = src2 << src1
The shl instructions shift the src2 operand left by the number of bits specified by the src1 operand and store the result in the dst operand. Shift counts less than 0 or greater than the number of bits in the object have undefined results.
shrx - Арифметический сдвиг вправо
Syntax: shrb src1, src2, dst shrw src1, src2, dst shrl src1, src2, dst Function: dst = src2 >> src1
The shr instructions shift the src2 operand right by the number of bits specified by the src1 operand and store the result in the dst operand. Shift counts less than 0 or greater than the number of bits in the object have undefined results.
slicea - Срез массива
Syntax: slicea src1, src2, dst Function: dst = dst[src1:src2]
The slicea instruction creates a new array, which contains the elements from the index at src1 to the index src2-1. The new array is a reference array which points at the elements in the initial array. The initial array will remain allocated until both arrays are no longer referenced.
slicec - Срез строки
Syntax: slicec src1, src2, dst Function: dst = dst[src1:src2]
The slicec instruction creates a new string, which contains characters from the index at src1 to the index src2-1. Unlike slicea , the new string is a copy of the elements from the initial string.
slicela - Assign to array slice
Syntax: slicela src1, src2, dst Function: dst[src2:] = src1
The src1 and dst operands must be arrays of equal types. The src2 operand is a non-negative integer index. The src1 array is assigned to the array slice dst[src2:]; src2 + nelem(src1) must not exceed nelem(dst).
spawn - Функция spawn
Syntax: spawn src, dst Function: fork(); if(child) dst(src);
The spawn instruction creates a new thread and calls the function specified by the dst operand. The argu- ment frame passed to the thread function is specified by the src operand and should have been created by the frame instruction.
subx - Вычетание
Syntax: subb src1, src2, dst subf src1, src2, dst subw src1, src2, dst subl src1, src2, dst Function: dst = src2 - src1
� - 15 -
The sub instructions subtract the operands addressed by src1 and src2 and stores the result in the dst operand. For subb, the result is truncated to eight bits.
tail - Хвост списка
Syntax: tail src, dst Function: dst = src->next
The tail instruction takes the list specified by the src operand and creates a reference to a new list with the head removed, which is stored in the dst operand.
tcmp - Сравнение типов
Syntax: tcmp src, dst Function: if(typeof(src) != typeof(dst)) error("typecheck");
The tcmp instruction compares the types of the two pointers supplied by the src and dst operands. The comparison will succeed if the two pointers were created from the same type descriptor or the src operand is nil; otherwise, the program will error. The dst operand must be a valid pointer.
xorx - Исключающее ИЛИ (XOR)
Syntax: xorb src1, src2, dst xorw src1, src2, dst xorl src1, src2, dst Function: dst = src1 ^ src2
These instructions compute the bitwise exclusive-ORof the two operands addressed by src1 and src2 and store the result in the dst operand.
Формат объектного файла
Объектный файл определяет один модуль. Файл имеет следующую структуру:
Objfile { Header; Code_section; Type_section; Data_section; Module_name; Link_section; };
Следующие типы данных используются в описании кодировки файла:
OP encoded integer operand, encoding selected by the two most significant bits as follows: 00 signed 7 bits, 1 byte 10 signed 14 bits, 2 bytes 11 signed 30 bits, 4 bytes B unsigned byte W 32 bit signed integer F canonicalized 64-bitIEEE754 floating point value SO 16 bit unsigned small offset from register SI 16 bit signed immediate value LO 30 bit signed large offset from register
All binary values are encoded in two's complement format, most significant byte first. � - 16 -
The Header Section
Header { OP: magic_number; Signature; OP: runtime_flag; OP: stack_extent; OP: code_size; OP: data_size; OP: type_size; OP: link_size; OP: entry_pc; OP: entry_type; };
The magic number is defined as 819248 (symbolically XMAGIC), for modules that have not been signed cryp- tographically, and 923426 (symbolically SMAGIC), for modules that contain a signature. On the Inferno sys- tem, the symbolic names XMAGIC and SMAGIC are defined by the C include file /include/isa.h and the Limbo module /module/dis.m.
The signature field is only present if the magic number is SMAGIC. It has the form:
Signature { OP: length; array[length] of byte: signature; };
A digital signature is defined by a length, followed by an array of untyped bytes. Data within the signature should identify the signing authority, algorithm, and data to be signed. The runtime_flag is a bit mask that defines various execution options for a Dis module. The flags cur- rently defined are:
MUSTCOMPILE = 1<<0 DONTCOMPILE = 1<<1 SHAREMP = 1<<2
The MUSTCOMPILE flag indicates that a load instruction should draw an error if the implementation is unable to compile the module into native instructions using a just-in-time
compiler.
The DONTCOMPILE flag indicates that the module should not be compiled into native instructions, even though it is the default for the runtime environment. This flag may be set to allow debugging or to save memory. The SHAREMP flag indicates that each instance of the module should use the same module data for all instances of the module. There is no implicit synchronization between threads using the shared data. The stack_extent value indicates the number of bytes by which the thread stack of this module should be extended in the event that procedure calls exhaust the allocated stack. While stack extension is transparent to programs, increasing this value may improve the efficiency of execution at the expense of using more memory. The code_size is a count of the number of instructions stored in the Code_section. The data_size gives the size in bytes of the module's global data, which is initialized by evaluating the contents of the data section. The type_size is a count of the number of type descriptors stored in the Type_section. The link_size is a count of the number of external linkage directives stored in the Link_section. The entry_pc is an integer index into the instruction stream that is the default entry point for this module. The entry_pc should point to the first instruction of a function. Instructions are numbered from a pro- gram counter value of zero. The entry_type is the index of the type descriptor that corresponds to the function entry point set by entry_pc. � - 17 -
The Code Section
The code section describes a sequence of instructions for the virtual machine. An instruction is encoded as follows:
Instruction { B: opcode; B: address_mode; Middle_data; Source_data; Dest_data; };
The opcode specifies the instruction to execute, encoded as follows:
00 nop 20 headb 40 mulw 60 blew 80 shrl 01 alt 21 headw 41 mulf 61 bgtw 81 bnel 02 nbalt 22 headp 42 divb 62 bgew 82 bltl 03 goto 23 headf 43 divw 63 beqf 83 blel 04 call 24 headm 44 divf 64 bnef 84 bgtl 05 frame 25 headmp 45 modw 65 bltf 85 bgel 06 spawn 26 tail 46 modb 66 blef 86 beql 07 runt 27 lea 47 andb 67 bgtf 87 cvtlf 08 load 28 indx 48 andw 68 bgef 88 cvtfl 09 mcall 29 movp 49 orb 69 beqc 89 cvtlw 0A mspawn 2A movm 4A orw 6A bnec 8A cvtwl 0B mframe 2B movmp 4B xorb 6B bltc 8B cvtlc 0C ret 2C movb 4C xorw 6C blec 8C cvtcl 0D jmp 2D movw 4D shlb 6D bgtc 8D headl 0E case 2E movf 4E shlw 6E bgec 8E consl 0F exit 2F cvtbw 4F shrb 6F slicea 8F newcl 10 new 30 cvtwb 50 shrw 70 slicela 90 casec 11 newa 31 cvtfw 51 insc 71 slicec 91 indl 12 newcb 32 cvtwf 52 indc 72 indw 92 movpc 13 newcw 33 cvtca 53 addc 73 indf 93 tcmp 14 newcf 34 cvtac 54 lenc 74 indb 94 mnewz 15 newcp 35 cvtwc 55 lena 75 negf 95 cvtrf 16 newcm 36 cvtcw 56 lenl 76 movl 96 cvtfr 17 newcmp 37 cvtfc 57 beqb 77 addl 97 cvtws 18 send 38 cvtcf 58 bneb 78 subl 98 cvtsw 19 recv 39 addb 59 bltb 79 divl 99 lsrw 1A consb 3A addw 5A bleb 7A modl 9A lsrl 1B consw 3B addf 5B bgtb 7B mull 9B eclr 1C consp 3C subb 5C bgeb 7C andl 9C newz 1D consf 3D subw 5D beqw 7D orl 9D newaz 1E consm 3E subf 5E bnew 7E xorl 1F consmp 3F mulb 5F bltw 7F shll
The address_mode byte specifies the addressing mode of each of the three operands: middle, source and destination. The source and destination operands are encoded by three bits and the middle operand by two bits. The bits are packed as follows:
bit 7 6 5 4 3 2 1 0 m1 m0 s2 s1 s0 d2 d1 d0
The middle operand is encoded as follows:
00 none no middle operand 01 $SI small immediate 10 SO(FP) small offset indirect from FP 11 SO(MP) small offset indirect from MP
The source and destination operands are encoded as follows: � - 18 -
000 LO(MP) offset indirect from MP 001 LO(FP) offset indirect from FP 010 $OP 30 bit immediate 011 none no operand 100 SO(SO(MP)) double indirect from MP 101 SO(SO(FP)) double indirect from FP 110 reserved 111 reserved
The middle_data field is only present if the middle operand specifier of the address_mode is not `none'. If the field is present it is encoded as an OP. The source_data and dest_data fields are present only if the corresponding address_mode field is not `none'. For offset indirect and immediate modes the field contains a single OP. For double indirect modes the values are encoded as two OP values: the first value is the register indirect offset, and the second value is the final indirect offset. The offsets for double indirect addressing cannot be larger than 16 bits.
The Type Section
The type section contains type descriptors describing the layout of pointers within data types. The format of each descriptor is:
Type_descriptor { OP: desc_number; OP: size; OP: number_ptrs; array[number_ptrs] of B: map; };
The desc_number is a small integer index used to identify the descriptor to instructions such as new. The size field is the size in bytes of the memory described by this type. The number_ptrs field gives the size in bytes of the map array. The map array is a bit vector where each bit corresponds to a word in memory. The most significant bit cor- responds to the lowest address. For each bit in the map, the word at the corresponding offset in the type is a pointer iff the bit is set to 1.
The Data Section
The data section encodes the contents of the MP data for the module. The section contains a sequence of items; each item contains a control byte and an offset into the section, followed by one or more data items.
A control byte of zero marks the end of the data section. Otherwise, it gives the type of data to be loaded and selects between two representations of an item:
Short_item { B: code; OP: offset; array[code & 16rF] of type[code>>4]: data; }; Long_item { B: code; OP: count; OP: offset; array[ndata] of type[code>>4]: data; };
A Short_item is generated for 15 or fewer items, otherwise a Long_item is generated. In a Long_item the count field (bottom 4 bits of code) is set to zero and the count follows as an OP. The top 4 bits of code determine the type of the datum. The defined values are: � - 19 -
0001 8 bit bytes 0010 32 bit words 0011 utf encoded string 0100 real value IEEE754 canonical representation 0101 Array 0110 Set array address 0111 Restore load address 1000 64 bit big
The byte, word, real and big operands are encoded as sequences of bytes (of appropriate length) in big- endian form, converted to native format before being stored in the data space. The `string' code takes a UTF-encodedsequence of count bytes, which is converted to an array of 16-bitUnicode values stored in an implementation-dependentstructure on the heap; a 4-bytepointer to the string descriptor is stored in the data space. The `array' code takes two 4-byteoperands: the first is the index of the array's type descriptor in the type section; the second is the length of the array to be created. The result in memory is a 4-byte pointer to an implementation-dependentarray descriptor in the heap. Each item's data is stored at the address formed by adding the offset in that item to a base address main- tained by the loader. Initially that address is the base of the data space of the module instance. A new base for loading subsequent items can be set or restored by the following operations, used to initialize arrays. The `set array index' item must appear immediately following an `array' item. Its operand is a 4-bytebig- endian integer that gives an index into that array, at which address subsequent data should be loaded; the previous load address is stacked internally. Subsequent data will be loaded at offsets from the new base address. The `restore load address' item has no operands; it pops a load address from the internal address stack and makes that the new base address.
The Module Name
The module name immediately follows the data section. It contains the name of the implementation mod- ule, in UTF encoding, terminated by a zero byte.
The Link Section
The link section contains an array of external linkage items: the list of functions exported by this module. Each item describes one exported function in the following form:
Linkage_item { OP: pc; OP: desc_number; W: sig; array[] of byte: name; };
The pc is the instruction number of the function's entry point. The desc_number is the index, in the type section, of the type descriptor for the function's stack frame. The sig word is a 32-bithash of the function's type signature. Finally, the name of the function is stored as a variable length array of bytes in UTF-8 encoding, with the end of the array marked by a zero byte. The names of member functions of an exported adt are qualified by the name of the adt. The next linkage item, if any, follows immediately.
Symbol Table File Format
The object file format does not include type information for debuggers. The Limbo compiler can optionally produce a separate symbol table file. Its format is defined in the entry sbl(6) of [1].
Ссылки
- Inferno Programmer's Manual (Third Edition), Volume 1 ('the manual'), Vita Nuova Holdings Limited, June 2000.
- P Winterbottom and R Pike, "The Design of the Inferno Virtual Machine", reprinted in this volume.