Basic bytecode spec

This commit is contained in:
2021-04-05 16:32:07 +03:00
parent 3e74914bd8
commit 2663bf65d1
2 changed files with 286 additions and 2 deletions
+102 -2
View File
@@ -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
View File
@@ -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;
}