From 4ed5fb650199b88c7b2bafcb7b3add2019014985 Mon Sep 17 00:00:00 2001 From: cassowarii Date: Sun, 28 Jun 2026 12:13:23 -0700 Subject: [PATCH] conditional jumps and such in VM --- src/data/hashtable.c | 2 +- src/data/operations.c | 59 +++++++++++++++++ src/data/operations.h | 15 +++++ src/data/value.c | 12 +++- src/data/value.h | 5 +- src/main.c | 41 +++++++++--- src/vm/bytecode.h | 66 ++++++++++--------- src/vm/exec.c | 171 +++++++++++++++++++++++++++++++++++++------------- src/vm/exec.h | 1 + 9 files changed, 287 insertions(+), 85 deletions(-) create mode 100644 src/data/operations.c create mode 100644 src/data/operations.h diff --git a/src/data/hashtable.c b/src/data/hashtable.c index 89244f6..6cef9a3 100644 --- a/src/data/hashtable.c +++ b/src/data/hashtable.c @@ -207,7 +207,7 @@ static hashentry *find_entry_in_array(hashentry *entries, usize length, hV *key) move ++; } usize index = start % length; - while (!sbV_eq(&entries[index].key, key) && entries[index].key.type != IT_NOTHING) { + while (!sbV_c_eq(&entries[index].key, key) && entries[index].key.type != IT_NOTHING) { index += move; index %= length; } diff --git a/src/data/operations.c b/src/data/operations.c new file mode 100644 index 0000000..aca8317 --- /dev/null +++ b/src/data/operations.c @@ -0,0 +1,59 @@ +#include "data/operations.h" + +#include "data/integer.h" + +hV sbV_add(const hV *a, const hV *b) { + if (a->type == IT_INTEGER && b->type == IT_INTEGER) { + return sbV_int(sbInteger_sum(a->integer, b->integer)); + } else { + PANIC("todo"); + } +} + +hV sbV_sub(const hV *a, const hV *b) { + if (a->type == IT_INTEGER && b->type == IT_INTEGER) { + return sbV_int(sbInteger_diff(a->integer, b->integer)); + } else { + PANIC("todo"); + } +} + +hV sbV_mul(const hV *a, const hV *b) { + if (a->type == IT_INTEGER && b->type == IT_INTEGER) { + return sbV_int(sbInteger_mul(a->integer, b->integer)); + } else { + PANIC("todo"); + } +} + +hV sbV_floordiv(const hV *a, const hV *b) { + if (a->type == IT_INTEGER && b->type == IT_INTEGER) { + return sbV_int(sbInteger_floordiv(a->integer, b->integer)); + } else { + PANIC("todo"); + } +} + +hV sbV_incr(const hV *a) { + if (a->type == IT_INTEGER) { + return sbV_int(a->integer + 1); + } else { + PANIC("todo"); + } +} + +hV sbV_decr(const hV *a) { + if (a->type == IT_INTEGER) { + return sbV_int(a->integer - 1); + } else { + PANIC("todo"); + } +} + +hV sbV_eq(const hV *a, const hV *b) { + if (sbV_c_eq(a, b)) { + return HVBOOL(TRUE); + } else { + return HVBOOL(FALSE); + } +} diff --git a/src/data/operations.h b/src/data/operations.h new file mode 100644 index 0000000..ed97ea2 --- /dev/null +++ b/src/data/operations.h @@ -0,0 +1,15 @@ +#include "common.h" + +hV sbV_add(const hV *a, const hV *b); + +hV sbV_sub(const hV *a, const hV *b); + +hV sbV_mul(const hV *a, const hV *b); + +hV sbV_floordiv(const hV *a, const hV *b); + +hV sbV_incr(const hV *a); + +hV sbV_decr(const hV *a); + +hV sbV_eq(const hV *a, const hV *b); diff --git a/src/data/value.c b/src/data/value.c index 68f40d0..8fdb686 100644 --- a/src/data/value.c +++ b/src/data/value.c @@ -51,7 +51,7 @@ hV sbV_int(hInteger i) { }; } -flag sbV_eq(const hV *a, const hV *b) { +flag sbV_c_eq(const hV *a, const hV *b) { if (a->type != b->type) return FALSE; if (a->type == ITX_TOMBSTONE || b->type == ITX_TOMBSTONE) return FALSE; if (a->type == IT_NOTHING || b->type == IT_NOTHING) return FALSE; @@ -72,6 +72,9 @@ flag sbV_eq(const hV *a, const hV *b) { case IT_STRING: /* strings can be compared for equality character by character */ return sbString_eq(a->string, b->string); + case IT_INTEGER: + /* TODO: we need to handle bigints properly. for now, just check int values normally */ + return a->integer == b->integer; case IT_HASH: /* hashes should (but currently don't) check that they are the same by value. * right now, we'll just see if they point to the same hash ("is"). */ @@ -81,6 +84,13 @@ flag sbV_eq(const hV *a, const hV *b) { } } +flag sbV_c_falsy(const hV *a) { + /* only nil and boolean false are false */ + if (a->type == IT_NIL) return TRUE; + if (a->type == IT_BOOLEAN && !a->boolean) return TRUE; + return FALSE; +} + void sbV_retain(const hV *a) { /* for some classes (e.g. nil, float), we don't need to retain them at all * because they are trivially copiable. some classes (integer, string) need diff --git a/src/data/value.h b/src/data/value.h index 94a032d..7526178 100644 --- a/src/data/value.h +++ b/src/data/value.h @@ -5,6 +5,7 @@ #define HVINT(n) ((hV) { .type = IT_INTEGER, .integer = n }) #define HVSTR(s) ((hV) { .type = IT_STRING, .string = OBJSL(s) }) +#define HVBOOL(b) ((hV) { .type = IT_BOOLEAN, .boolean = b }) typedef u64 hHash; typedef u64 hString; @@ -45,8 +46,10 @@ hV sbV_symbol(hSymbol sym); hV sbV_float(double fl); hV sbV_hash(hHash hash); hV sbV_int(hInteger i); +hV sbV_boolean(flag b); -flag sbV_eq(const hV *a, const hV *b); +flag sbV_c_eq(const hV *a, const hV *b); +flag sbV_c_falsy(const hV *a); void sbV_retain(const hV *a); void sbV_release(const hV *a); diff --git a/src/main.c b/src/main.c index d4d09e9..fd2caf7 100644 --- a/src/main.c +++ b/src/main.c @@ -42,19 +42,46 @@ int main(int argc, char **argv) { sbVmPartialBlock pb = sbVmBlock_create(4096, 4096); - u8 code[] = { - BC_LD_CONST, 0x00, - BC_LD_CONST, 0x01, - BC_OP_ADD, + u8 code1[] = { + BC_ALLOC_VARS, 0x02, /* 0 1 */ + BC_LD_IMM, 0x00, /* var1 = 0 2 3 */ + BC_ST_VAR, 0x01, /* ... 4 5 */ + BC_LD_CONST, 0x01, /* var0 = 9 6 7 */ + BC_ST_VAR, 0x00, /* ... 8 9 */ + BC_LD_CONST, 0x00, /* (LABEL) var1 += 5 ; 10 11 */ + BC_LD_VAR, 0x01, /* ... */ + BC_OP_ADD, /* ... */ + BC_ST_VAR, 0x01, /* ... */ + BC_LD_VAR, 0x00, /* var0 = var0 - 1 */ + BC_OP_DECR, /* ... */ + BC_ST_VAR, 0x00, /* ... */ + BC_LD_VAR, 0x00, /* if var0 != 0 */ + BC_LD_IMM, 0x00, /* ... */ + BC_OP_EQ, /* ... */ + BC_JF, 0x0A, /* ...then goto LABEL */ + BC_LD_VAR, 0x01, /* return var1 */ + BC_LD_IMM, 0x01, /* ... */ BC_HALT, }; - sbVmBlock_write_code(&pb, code, sizeof(code)); - sbVmBlock_add_constant(&pb, &HVINT(5)); - sbVmBlock_add_constant(&pb, &HVINT(11)); + sbVmBlock_add_constant(&pb, &HVINT(9)); + + sbVmBlock_write_code(&pb, code1, sizeof(code1)); + sbVmProgram_add_block(&pm, &pb); + sbVmBlock_reset(&pb); + + u8 code2[] = { + BC_LD_CONST, 0x00, + BC_OP_ADD, + BC_RET, + }; + + sbVmBlock_add_constant(&pb, &HVINT(7)); + sbVmBlock_write_code(&pb, code2, sizeof(code2)); sbVmProgram_add_block(&pm, &pb); + sbVmBlock_reset(&pb); sbVm_execute(&vm, &pm); diff --git a/src/vm/bytecode.h b/src/vm/bytecode.h index 6f3fe4f..299434d 100644 --- a/src/vm/bytecode.h +++ b/src/vm/bytecode.h @@ -4,36 +4,42 @@ #include "global.h" typedef enum sbOpcode { - BC_NOP = 0, - BC_LD_IMM, /* push immediate value to stack */ - BC_LD_CONST, /* push constants[index] to stack */ - BC_LD_CTX, /* look up key in context object and push result */ - BC_LD_VAR, /* push value onto stack from variable */ - BC_LD_UPVAL, /* push value onto stack from closure */ - BC_ST_VAR, /* pop value from stack into variable */ - BC_ST_UPVAL, /* pop value from stack into closure */ - BC_POP, /* pop value from stack */ - BC_CALL, /* push return address and jump to new block */ - BC_JMP, /* local jump inside current block */ - BC_RET, /* return to calling block */ - BC_SEND, /* like call, but jump to object method (maybe this is the same as call?) */ - BC_OP_ADD, - BC_OP_SUB, - BC_OP_MUL, - BC_OP_DIV = 0x10, - BC_OP_FLDIV, - BC_OP_NEG, - BC_OP_MOD, - BC_OP_POW, - BC_OP_DEREF, - BC_ALLOC_VARS, - BC_LIST_NEW, - BC_HASH_NEW, - BC_LIST_ADD, - BC_HASH_ADD, - BC_LONG_NUM = 253, - BC_VLONG_NUM = 254, - BC_HALT = 255, + BC_NOP = 0x00, // skip me + BC_LD_IMM, // push immediate value to stack + BC_LD_CONST, // push constants[index] to stack + BC_LD_CTX, // look up key in context object and push result + BC_LD_VAR, // push value onto stack from variable + BC_LD_UPVAL, // push value onto stack from closure + BC_ST_VAR, // pop value from stack into variable + BC_ST_UPVAL, // pop value from stack into closure + BC_POP = 0x08, // pop value from stack + BC_NPOP, // pop value from stack + BC_CALL, // push return address and jump to new block + BC_RET, // return to calling block + BC_JMP, // unconditional local jump inside current block + BC_JT, // jump if true + BC_JF, // jump if false + BC_SEND, // like call, but jump to object method + BC_OP_EQ = 0x10, // == + BC_OP_NOT, // boolean not + BC_OP_ADD, // add + BC_OP_SUB, // subtract + BC_OP_MUL, // multiply + BC_OP_DIV, // true divide + BC_OP_FLDIV, // floor divide + BC_OP_NEG, // negative + BC_OP_MOD = 0x18, // mod + BC_OP_POW, // power + BC_OP_INCR, // increment + BC_OP_DECR, // decrement + BC_OP_DEREF, // dereference pointer + BC_ALLOC_VARS, // create space in rstack for local variables + BC_LIST_GATHER, // create list from count + list of values on stack + BC_HASH_GATHER, // create hash from count + list of pairs of keys/values on stack + BC_LONG_NUM = 0xFC, // next two bytes are a 16-bit number + BC_VLONG_NUM = 0xFD, // next four bytes are a 32-bit number + BC_VVLONG_NUM = 0xFE, // next four bytes are a 32-bit number + BC_HALT = 0xFF, // terminate execution } sbOpcode; #endif diff --git a/src/vm/exec.c b/src/vm/exec.c index f485622..fef3fd1 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -1,6 +1,6 @@ #include "vm/exec.h" -#include "data/integer.h" +#include "data/operations.h" void call_block(hVm vm, usize block_id); void execute_instruction(hVm vm); @@ -56,6 +56,10 @@ void call_block(hVm vm, usize block_id) { } void return_from_block(hVm vm) { + for (usize i = 0; i < vm->fp->num_locals; i++) { + sbV_release(&vm->fp->locals[i]); + } + sbVmStackFrame *frame = (sbVmStackFrame*)vm->fp; vm->fp = frame->last_fp; @@ -63,6 +67,17 @@ void return_from_block(hVm vm) { vm->ip = frame->return_addr; } +void debug_print_hv(const hV *value) { + printf("%016llx %016llx\n", *(u64*)value, *((u64*)value+1)); +} + +void store_local(hVm vm, usize local_index, const hV *value) { + /* release whatever was in the slot before */ + sbV_release(&vm->fp->locals[local_index]); + sbV_retain(value); + vm->fp->locals[local_index] = *value; +} + void push_stack(hVm vm, const hV *value) { sbV_retain(value); *(hV*)vm->sp = *value; @@ -75,17 +90,30 @@ hV *pop_stack(hVm vm) { return (hV*)vm->sp; } +hV *npop_stack(hVm vm, usize count) { + vm->sp -= count * sizeof(hV); + for (usize i = 0; i < count; i++) { + sbV_release(&((hV*)vm->sp)[i]); + } + return (hV*)vm->sp; +} + +hV *peek_stack(hVm vm, usize offset) { + return (hV*)(vm->sp - (offset + 1) * sizeof(hV)); +} + sbOpcode get_opcode(hVm vm) { return *(vm->ip++); } /* small numbers that are parameters to opcodes can * just be encoded directly as 1 byte. numbers of 16-bit - * scale can be encoded as FE + byte + byte, numbers of - * 32 bit scale can be FF + byte + byte + byte + byte, + * scale can be encoded as FC + bytebyte, numbers of + * 32 bit scale can be FD + bytebytebytebyte, + * 64 bit can be FE + bytebytebytebytebytebytebytebyte, * in big-endian format */ -u32 get_param(hVm vm) { - u32 result = *((u8*)vm->ip++); +u64 get_param(hVm vm) { + u64 result = *((u8*)vm->ip++); if (result == BC_LONG_NUM) { result = *(vm->ip++); @@ -99,6 +127,22 @@ u32 get_param(hVm vm) { result |= *(vm->ip++); result <<= 8; result |= *(vm->ip++); + } else if (result == BC_VVLONG_NUM) { + result = *(vm->ip++); + result <<= 8; + result |= *(vm->ip++); + result <<= 8; + result |= *(vm->ip++); + result <<= 8; + result |= *(vm->ip++); + result <<= 8; + result |= *(vm->ip++); + result <<= 8; + result |= *(vm->ip++); + result <<= 8; + result |= *(vm->ip++); + result <<= 8; + result |= *(vm->ip++); } return result; @@ -108,7 +152,7 @@ u32 get_param(hVm vm) { void execute_instruction(hVm vm) { sbOpcode op = get_opcode(vm); u32 param; - hV *v, a, b, c; + hV *v, *w, res; switch (op) { case BC_NOP: @@ -135,82 +179,119 @@ void execute_instruction(hVm vm) { PANIC("todo"); case BC_ST_VAR: param = get_param(vm); - vm->fp->locals[param] = *pop_stack(vm); + v = peek_stack(vm, 0); + store_local(vm, param, v); + pop_stack(vm); return; case BC_ST_UPVAL: PANIC("todo"); case BC_POP: v = pop_stack(vm); - sbV_release(v); return; - case BC_CALL: + case BC_NPOP: param = get_param(vm); - call_block(vm, param); + npop_stack(vm, param); + return; + case BC_CALL: + v = pop_stack(vm); + call_block(vm, v->integer); return; case BC_JMP: param = get_param(vm); vm->ip = &vm->fp->block->bytecode[param]; return; + case BC_JT: + param = get_param(vm); + v = pop_stack(vm); + if (!sbV_c_falsy(v)) { + vm->ip = &vm->fp->block->bytecode[param]; + } + return; + case BC_JF: + param = get_param(vm); + v = pop_stack(vm); + if (sbV_c_falsy(v)) { + vm->ip = &vm->fp->block->bytecode[param]; + } + return; case BC_RET: return_from_block(vm); return; case BC_SEND: PANIC("todo"); - case BC_OP_ADD: - b = *pop_stack(vm); - a = *pop_stack(vm); - if (a.type == IT_INTEGER && b.type == IT_INTEGER) { - c = sbV_int(sbInteger_sum(a.integer, b.integer)); - push_stack(vm, &c); + case BC_OP_EQ: + v = peek_stack(vm, 1); + w = peek_stack(vm, 0); + res = sbV_eq(v, w); + npop_stack(vm, 2); + push_stack(vm, &res); + return; + case BC_OP_NOT: + v = pop_stack(vm); + if (sbV_c_falsy(v)) { + push_stack(vm, &HVBOOL(TRUE)); } else { - PANIC("todo"); + push_stack(vm, &HVBOOL(FALSE)); } return; + case BC_OP_ADD: + v = peek_stack(vm, 1); + w = peek_stack(vm, 0); + res = sbV_add(v, w); + npop_stack(vm, 2); + push_stack(vm, &res); + return; case BC_OP_SUB: - b = *pop_stack(vm); - a = *pop_stack(vm); - if (a.type == IT_INTEGER && b.type == IT_INTEGER) { - c = sbV_int(sbInteger_diff(a.integer, b.integer)); - push_stack(vm, &c); - } else { - PANIC("todo"); - } + v = peek_stack(vm, 1); + w = peek_stack(vm, 0); + res = sbV_sub(v, w); + npop_stack(vm, 2); + push_stack(vm, &res); return; case BC_OP_MUL: - b = *pop_stack(vm); - a = *pop_stack(vm); - if (a.type == IT_INTEGER && b.type == IT_INTEGER) { - c = sbV_int(sbInteger_mul(a.integer, b.integer)); - push_stack(vm, &c); - } else { - PANIC("todo"); - } + v = peek_stack(vm, 1); + w = peek_stack(vm, 0); + res = sbV_mul(v, w); + npop_stack(vm, 2); + push_stack(vm, &res); return; case BC_OP_DIV: PANIC("todo"); case BC_OP_FLDIV: - b = *pop_stack(vm); - a = *pop_stack(vm); - if (a.type == IT_INTEGER && b.type == IT_INTEGER) { - c = sbV_int(sbInteger_floordiv(a.integer, b.integer)); - push_stack(vm, &c); - } else { - PANIC("todo"); - } + v = peek_stack(vm, 1); + w = peek_stack(vm, 0); + res = sbV_floordiv(v, w); + npop_stack(vm, 2); + push_stack(vm, &res); return; case BC_OP_NEG: case BC_OP_MOD: case BC_OP_POW: + PANIC("todo"); + case BC_OP_INCR: + v = peek_stack(vm, 0); + res = sbV_incr(v); + pop_stack(vm); + push_stack(vm, &res); + return; + case BC_OP_DECR: + v = peek_stack(vm, 0); + res = sbV_decr(v); + pop_stack(vm); + push_stack(vm, &res); + return; case BC_OP_DEREF: PANIC("todo"); case BC_ALLOC_VARS: param = get_param(vm); + /* have to set new rstack space to 0 so we don't accidentally decrement + * the ref count of variables from a previous stack frame (or of just garbage) */ + memset(vm->rp, 0, param * sizeof(hV)); vm->rp += param * sizeof(hV); + vm->fp->num_locals += param; return; - case BC_LIST_NEW: - case BC_HASH_NEW: - case BC_LIST_ADD: - case BC_HASH_ADD: + case BC_LIST_GATHER: + case BC_HASH_GATHER: PANIC("todo"); case BC_LONG_NUM: case BC_VLONG_NUM: diff --git a/src/vm/exec.h b/src/vm/exec.h index c3341f7..6fa4406 100644 --- a/src/vm/exec.h +++ b/src/vm/exec.h @@ -47,6 +47,7 @@ typedef struct sbVmStackFrame { struct sbVmStackFrame *last_fp; u8 *last_rp; const sbVmBlock *block; + usize num_locals; hV locals[]; } sbVmStackFrame; -- 1.8.3.1