Basic bytecode spec
This commit is contained in:
+102
-2
@@ -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
|
||||
|
||||
+184
@@ -1,5 +1,189 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user