# Bytecode Documentation ## Instructions Each instruction consists of a mnemonic and some number of arguments. The numeric value for each instruction is the same length, but the arguments can be different lengths, and each instruction can have a different number of arguments. In this document, the layout for data is specified by a number of fields separated by commas. When represented in raw bits, these fields are next to each other with no padding in between. For example: ```text length:u64, data:[i8] ``` represents an unsigned 64 bit integer field called "length" and a field called "data" which is an array of signed 8 bit integers. The value right before an array is the value which determines its length. The argument types are as follows: | Name | Description | |:------------------|:--------------------------------------| | i8, i16, i32, i64 | 8, 16, 32, or 64 bit signed integer | | u8, u16, u32, u64 | 8, 16, 32, or 64 bit unsigned integer | | double | Equivilant to the C type "double" | | reg | register (format: type:u8,which:u32) | | str | string (format: length:u64,data:[i8]) | ### Registers Most instructions take register numbers instead of direct arguments. Registers take a type and a number. For example "lexenv2" is the third (counting from 0) lexenv register or "arg0" is the first argument register. | Mnemonic | ID | Description | |:---------|:--:|:--------------------------------------------------------------| | val | 0 | General value registers (clobbered by calls) | | saved | 1 | Callee saved ragisters (val registers are clobbered by calls) | | arg | 2 | Function argument registers (clobbered by calls) | | ret | 3 | Function return value registers (clobbered by calls) | | lexenv | 4 | Lexical environment registers | | block | 5 | Block symbol registers | ### Instruction List - NIL dest:reg Load the literal nil into DEST - T def:reg Load the literal t into DEST - STRING dest:reg, value:str Load the string of LENGTH bytes from DATA into dest. - INT dest:reg, value:i64 Convert VALUE into an int object and store it into DEST - FLOAT dest:reg, value:double Convert VALUE into a float object and store it into DEST - CONS dest:reg, car:reg, cdr:reg Create a cons object with CAR and CDR and store it into REG. - LIST dest:reg, count:u64 Create a list from the first COUNT "arg" registers and store it into DEST. - VECTOR dest:reg, count:u64 Create a vector from the first COUNT "arg" registers and store it into DEST. - INTERN\_LIT reg:reg, name:str INTERN\_DYN reg:reg, name:reg These instructions convert the string literal or register containing a string NAME into a symbol and store the symbol into REG. - SYMBOL\_NAME dest:reg, sym:reg Store the name of SYM into DEST. - MOV dest:reg, src:reg Copy the value in the register SRC into the register DEST. - FUNCALL reg:reg, argc:u64 Call the function in REG. This should either be a function object or a symbol which has a value as a function. The return values are placed into the "ret" registers. ARGC is the number of "arg" registers that have been set for this function call. - RETVAL_COUNT count:u8 Declare that first COUNT return values have been set (if they have not been touched during this lexenv, they will be set to nil). Without this, the highest "ret" register written to is the number to use. Without this, assume one return value. - ENTER\_LEXENV LEAVE\_LEXENV Shift all the "lexenv" registers up by 1 and then create a new lexenv and store it into "lexenv0". LEAVE\_LEXENV does the opposite, restoring the last pushed levenv. - ENTER\_BLOCK sym:reg, count:u64 LEAVE\_BLOCK sym:reg Enter a new named block which is identified by the symbol in SYM. The block is COUNT instructions long. LEAVE\_BLOCK leaved the block identified by SYM. Adding a new block pushes SYM onto the "block" registers, much like PUSH\_LEXENV (which see). - SET\_VALUE sym:reg, value:reg Set the value as a variable of SYM to VALUE. - SET\_FUNCTION sym:reg, value:reg Set the value as a function of SYM to VALUE (value must be an actual function, not a symbol). - GET\_VALUE dest:reg, sym:reg Store the value as a variable of the symbol SYM into DEST. - GET\_FUNCTION dest:reg, sym:reg Store the value as a function of the symbol SYM into DEST. - NEWFUNCTION\_LIT dest:reg, count:u64 NEWFUNCTION\_DYN dest:reg, src:reg Create a new function object and store it into DEST. If the first case the next COUNT instructions are considered to be the function and are skipped. In the second case SRC should be a list or vector containing the bytecode for the function. - PUT sym:reg, key:reg, value:reg Associate KEY with VALUE in the plist of SYM. - GET dest:reg, sym:reg, key:reg Store the value associated with KEY in the plist of SYM into DEST. - AND dest:reg, count:u64, values:[reg] OR dest:reg, count:u64, values:[reg] XOR dest:reg, count:u64, values:[reg] NOT dest:reg, value:reg Perform a logical operation on each of VALUES. For example, the XOR instruction will exclusively or each of VALUES with the next value, and store the overall result into DEST. NOT is special as it can only take one value, of which it will take the logical negation and store it into DEST. - CJMP cond:reg, offset:i64 If the value in COND is truthy (not nil), skip the next OFFSET instructions. If OFFSET is negative, instead go back abs(OFFSET) instructions. CJMP is NOT counted as an instruction for the purposes of counting offsets. Therefore an OFFSET of -2 means restart execute at the instruction above the instruction above this CJMP. - CAR dest:reg, cons:reg CDR dest:reg, cons:reg Store the car or cdr of CONS into DEST. - SETCAR cons:reg, value:reg SETCDR cons:reg, value:reg Store VALUE into the car or cdr of CONS. - GETELT\_LIT dest:reg, seq:reg, index:u64 GETELT\_DYN dest:reg, seq:reg, index:reg Store the value at INDEX in SEQ (a list or vector) into DEST. - SETELT\_LIT seq:reg, index:u64, value:reg SETELT\_DYN seq:reg, index:reg, value:reg Store VALUE into the index numbered INDEX of SEQ (a list or vector). - EQ\_TWO dest:reg, val1:reg, val2:reg EQ\_N dest:reg, count:u64 Compare VAL1 and VAL2, if they are the same object (or symbols with the same name) store T into DEST, otherwise, store NIL. In the case of EQ\_N, if the first COUNT "arg" registers are the same object (or symbols with the same name), store T into DEST, otherwise, store NIL. - NUM\_GT dest:reg, val1:reg, val2:reg NUM\_GE dest:reg, val1:reg, val2:reg NUM\_EQ dest:reg, val1:reg, val2:reg NUM\_LE dest:reg, val1:reg, val2:reg NUM\_LT dest:reg, val1:reg, val2:reg Compare VAL1 and VAL2, which must be numbers, and compare their values. If they pass, store T into DEST, otherwise store NIL. ## Mnemonic Conversion Table | Mnemonic | Number | |:-----------------|:------:| | STRING\_LIT | 0 | | STRING\_DYN | 1 | | INT | 2 | | FLOAT | 3 | | CONS | 4 | | LSIT\_LIT | 5 | | LSIT\_DYN | 6 | | VECTOR\_LIT | 7 | | VECTOR\_DYN | 8 | | INTERN\_LIT | 9 | | INTERN\_DYN | 10 | | SYMBOL\_NAME | 11 | | MOV | 12 | | FUNCALL | 13 | | RETVAL\_COUNT | 14 | | ENTER\_LEXENV | 15 | | LEAVE\_ELEXENV | 16 | | ENTER\_BLOCK | 17 | | LEAVE\_BLOCK | 18 | | SET\_VALUE | 19 | | SET\_FUNCTION | 20 | | GET\_VALUE | 21 | | GET\_FUNCTION | 22 | | NEWFUNCTION\_LIT | 23 | | NEWFUNCTION\_DYN | 24 | | PUT | 25 | | GET | 26 | | AND | 27 | | OR | 28 | | XOR | 29 | | NOT | 30 | | CJMP | 31 | | CAR | 32 | | CDR | 33 | | SETCAR | 34 | | SETCDR | 35 | | GETELT\_LIT | 36 | | GETELT\_DYN | 37 | | SETELT\_LIT | 38 | | SETELT\_DYN | 39 | | EQ\_TWO | 40 | | EQ\_N | 41 | | NUM\_GT | 42 | | NUM\_GE | 43 | | NUM\_EQ | 44 | | NUM\_LE | 45 | | NUM\_LT | 46 | ## Examples This: ```lisp (format t "Hello World~%") ``` Compiles into: ```text INTERN_LIT val0, "Hello World~%" ``` This: ```lisp (defun foo (bar &key baz &rest qux) "FOO each of BAR and BAZ as well as each of QUX." (let (some-val (genval bar)) (foo-internal some-val :baz baz qux))) (foo 10 :baz 20) ``` Compiles into: ```text NEWFUNCTION val0, 13 ;; not counting this instruction ENTER_LEXENV INTERN_LIT saved0, "foo" ENTER_BLOCK saved0, 9 ;; not counting this instruction INTERN_LIT val0, "genval" ;; NOTE bar already in arg0 FUNCALL val0 ;; NOTE val0 clobbered here INTERN_LIT val0, "foo-internal" MOV arg0, ret0 MOV arg3, arg2 MOV arg2, arg1 INTERN_LIT arg1, ":baz" FUNCALL val0 LEAVE_BLOCK saved0 LEAVE_LEXENV INTERN_LIT val1, "foo" SET_FUNCTION val1, val0 INT arg0, 10 INTERN_LIT arg1, ":baz" INT arg2, 20 FUNCALL val1 ```