diff --git a/doc/bytecode.txt b/doc/bytecode.txt index 26b1267..fbcbd9f 100644 --- a/doc/bytecode.txt +++ b/doc/bytecode.txt @@ -1,3 +1,103 @@ -Each instruction is 4 bytes +VM data types: +| Type name | C type/Description | +|-----------+---------------------| +| i8 | int8_t | +| u8 | uint8_t | +| i16 | int16_t | +| u16 | uint16_t | +| i32 | int32_t | +| u32 | uint32_t | +| i64 | int64_t | +| u64 | uint64_t | +| ref | VM object reference | +|-----------+---------------------| -... TBD ... +Each instruction is 4 bytes: +| Offset | Meaning | +|--------+----------| +| 0x00 | Opcode | +| 0x01 | Argument | +|--------+----------| + +Integer opcodes: +| Opcode | Mnemonic | Argument count | Description | +|--------+----------+----------------+------------------| +| 0x04 | add | 2 | push (x + y) | +| 0x05 | sub | 2 | push (x - y) | +| 0x06 | mul | 2 | push (x * y) | +| 0x07 | div | 2 | push (x / y) | +| 0x08 | mod | 2 | push (x % y) | +| 0x09 | neg | 1 | push (-x) | +| 0x0A | and | 2 | push (x and y) | +| 0x0B | or | 2 | push (x or y) | +| 0x0C | not | 1 | push (~x) | +| 0x0D | xor | 2 | push (x xor y) | +| 0x0E | shl | 1 | push (x << imm) | +| 0x0F | shr | 1 | push (x >> imm) | +| 0x10 | addi | 1 | push (x + imm) | +| 0x11 | ud | - | undefined | +| 0x12 | eq | 2 | push (x == y) | +| 0x13 | neq | 2 | push (x != y) | +| 0x14 | gt | 2 | push (x > y) | +| 0x15 | ge | 2 | push (x >= y) | +| 0x16 | lt | 2 | push (x < y) | +| 0x17 | le | 2 | push (x <= y) | +| 0x18 | ori | 1 | push (x or imm) | +| 0x19 | andi | 1 | push (x and imm) | +| 0x1A | xori | 1 | push (x xor imm) | +| 0x1B | eqi | 1 | push (x == imm) | +|--------+----------+----------------+------------------| + +Integer casts: +| Opcode | Mnemonic | Description | +|--------+----------+---------------------------| +| 0x20 | cvt_i8 | | +| 0x21 | cvt_u8 | | +| 0x22 | cvt_i16 | | +| 0x23 | cvt_u16 | | +| 0x24 | cvt_i32 | | +| 0x25 | cvt_u32 | | +| 0x26 | cvt_i64 | | +| 0x27 | cvt_u64 | | +| 0x28 | bnorm | Normalize integer to bool | +|--------+----------+---------------------------| + +Constant loads: +| Opcode | Mnemonic | Description | +|--------+----------+----------------------| +| 0x30 | ldc | push constant | +| 0x31 | ldnil | push null ref | +| 0x32 | ldi | push immediate value | +|--------+----------+----------------------| + +List operations: +| Opcode | Mnemonic | +|--------+----------| +| 0x40 | car | +| 0x41 | cdr | +| 0x42 | cadr | +| 0x43 | cdar | +| 0x44 | cddr | +| 0x45 | cons | +|--------+----------| + +Reference operations: +| Opcode | Mnemonic | Description | +|--------+----------+-----------------| +| 0x46 | test | push (x == nil) | +| 0x47 | dup | push x; push x | +| 0x48 | drop | x; | +| 0x49 | typeid | push (typeid x) | +| 0x4A | sizeof | push (sizeof x) | +|--------+----------+-----------------| + +Debug operations: +| Opcode | Mnemonic | Description | +|--------+----------+--------------------------------------------------| +| 0xF0 | abort | Abort VM execution | +| 0xF1 | break | Pause VM for external examination | +| 0xF2 | peek1 | Trace information about stack head | +| 0xF3 | peek2 | Trace information about a pair of stack elements | +|--------+----------+--------------------------------------------------| + +TBD: variable operations diff --git a/src/main.c b/src/main.c index e6173ef..cde6b07 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,189 @@ #include +#include +#include +#include + +#define FLAG_REF (1ULL << 63) + +enum vm_type { + VT_CONS, +}; + +struct vm_value { + enum vm_type type; + size_t refcount; + union { + struct { + uintptr_t fat_ar, fat_dr; + } v_cons; + }; +}; + +struct vm_state { + uint64_t stack[4096]; + size_t sp; +}; + +static struct vm_value *vm_cons(uint64_t w0, uint64_t w1) { + struct vm_value *v = malloc(sizeof(struct vm_value)); + v->type = VT_CONS; + v->refcount = 0; + v->v_cons.fat_ar = w0; + v->v_cons.fat_dr = w1; + return v; +} + +static void vm_print(uint64_t w, int cdepth); + +static int ref_q(uint64_t w) { + return (w & FLAG_REF) != 0; +} + +static int null_q(uint64_t w) { + return w == FLAG_REF; +} + +static struct vm_value *getref(uint64_t w) { + assert(ref_q(w)); + return (struct vm_value *) (w << 1); +} + +static int cons_q(uint64_t w) { + if (ref_q(w)) { + return null_q(w) || getref(w)->type == VT_CONS; + } else { + return 0; + } +} + +static void vm_print_ref(struct vm_value *value, int cdepth) { + uint64_t w0; + if (!value) { + printf("nil"); + return; + } + switch (value->type) { + case VT_CONS: + if (!cdepth) { + printf("("); + } + vm_print(value->v_cons.fat_ar, 0); + w0 = value->v_cons.fat_dr; + if (!null_q(w0)) { + printf(" "); + if (cons_q(w0)) { + vm_print(w0, cdepth + 1); + } else { + printf(". "); + vm_print(w0, cdepth + 1); + } + } + if (!cdepth) { + printf(")"); + } + break; + } +} + +static void vm_print(uint64_t w, int cdepth) { + if (w & FLAG_REF) { + vm_print_ref((void *) (w << 1), cdepth); + } else { + if (w & (1ULL << 62)) { + w |= 1ULL << 63; + } + printf("%ld", (int64_t) w); + } +} + +static int64_t sximm(uint32_t in) { + if (in & 0x800000) { + return in | 0xFFFFFFFFFF000000; + } else { + return in; + } +} + +static void push_integer(struct vm_state *vm, int64_t w) { + assert(vm->sp > 0); + vm->stack[--vm->sp] = w & ~FLAG_REF; +} + +static uint64_t pop(struct vm_state *vm) { + return vm->stack[vm->sp++]; +} + +static uint64_t pop_integer(struct vm_state *vm) { + uint64_t w = pop(vm); + assert(!(w & FLAG_REF)); + if (w & (1ULL << 62)) { + w |= 1ULL << 63; + } + return w; +} + +static void push_ref(struct vm_state *vm, void *ref) { + assert(vm->sp >= 2); + uintptr_t addr = (uintptr_t) ref; + assert(!(addr & 1)); + if (sizeof(uintptr_t) == 8) { + vm->stack[--vm->sp] = (addr >> 1) | FLAG_REF; + } +} + +static int vm_eval(struct vm_state *vm, uint32_t opcode) { + uint64_t w0, w1; + switch (opcode >> 24) { + case 0x04: + w1 = pop_integer(vm); + w0 = pop_integer(vm); + push_integer(vm, w0 + w1); + return 0; + // + case 0x30: + push_integer(vm, sximm(opcode & 0xFFFFFF)); + return 0; + case 0x31: + push_ref(vm, NULL); + return 0; + // + case 0x45: + w1 = pop(vm); + w0 = pop(vm); + push_ref(vm, vm_cons(w0, w1)); + return 0; + // + case 0xF2: + w0 = vm->stack[vm->sp]; + if (opcode & 1) { + ++vm->sp; + } + + printf("trace: "); + vm_print(w0, 0); + printf("\n"); + return 0; + default: + fprintf(stderr, "Undefined opcode: 0x%02hhx\n", opcode >> 24); + abort(); + } +} + +static struct vm_state vm; +static const uint32_t bytecode[] = { + 0x30FFFFFF, // -1 + 0x30000001, // 1 + 0x31000000, // nil + 0x45000000, // cons + 0x45000000, // cons + 0xF2000001, // trace +}; int main(int argc, char **argv) { + vm.sp = 4096; + for (size_t i = 0; i < sizeof(bytecode) / 4; ++i) { + vm_eval(&vm, bytecode[i]); + } + return 0; }