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;
}
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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);
};
}
-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;
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"). */
}
}
+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
#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;
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);
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);
#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
#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);
}
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;
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;
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++);
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;
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:
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:
struct sbVmStackFrame *last_fp;
u8 *last_rp;
const sbVmBlock *block;
+ usize num_locals;
hV locals[];
} sbVmStackFrame;