/* --- */
-void emit_arg(sbVmCompiler *cm, usize number) {
- printf(" arg %zu\n", number);
+void emit_arg(sbVmCompiler *cm, i64 actual_number) {
+ printf(" arg %lld\n", actual_number);
u8 buf[9];
- if (number < 253) {
+ u64 number = actual_number;
+ if (0 <= actual_number && actual_number < 253) {
buf[0] = number;
sbVmCompiler_write_code(cm, buf, 1);
- } else if (number < 65536) {
+ } else if (-32768 < actual_number && actual_number < 32768) {
buf[0] = BC_LONG_NUM;
+ if (actual_number < 0) {
+ number = actual_number + 65536;
+ }
buf[1] = number >> 8;
buf[2] = number & 0xFF;
sbVmCompiler_write_code(cm, buf, 3);
- } else if (number < (1LL << 32)) {
+ } else if (-(1L << 31) < actual_number && actual_number < (1L << 31)) {
buf[0] = BC_VLONG_NUM;
+ if (actual_number < 0) {
+ number = actual_number + (1LL << 32);
+ }
buf[1] = (number >> 24) & 0xFF;;
buf[2] = (number >> 16) & 0xFF;
buf[3] = (number >> 8) & 0xFF;
sbVmCompiler_write_code(cm, buf, 5);
} else {
buf[0] = BC_VVLONG_NUM;
+ number = (u64)actual_number;
buf[1] = (number >> 56) & 0xFF;;
buf[2] = (number >> 48) & 0xFF;;
buf[3] = (number >> 40) & 0xFF;;
EARG(chunk->num_args);
}
- EMIT(BC_ALLOC_VARS);
- EARG(chunk->variable_count);
+ if (chunk->variable_count > 0) {
+ EMIT(BC_ALLOC_VARS);
+ EARG(chunk->variable_count);
+ }
for (int i = 0; i < nstmts; i++) {
sbIrStmt *stmt = ((sbIrStmt**)chunk->stmts.data)[i];
case IR_E_VALUE:
if (expr->value.type == IT_NIL) {
EMIT(BC_LD_NIL);
- } else if (expr->value.type == IT_INTEGER) {
+ } else if (expr->value.type == IT_BOOLEAN && expr->value.boolean) {
+ EMIT(BC_LD_TRUE);
+ } else if (expr->value.type == IT_BOOLEAN) {
+ EMIT(BC_LD_FALSE);
+ } else if (expr->value.type == IT_INTEGER && expr->value.integer < (2 << 16)) {
EMIT(BC_LD_IMM);
EARG(expr->value.integer);
} else {
- printf("(some value)\n");
+ u32 constant_index = sbVmCompiler_add_constant(cm, &expr->value);
+ EMIT(BC_LD_CONST);
+ EARG(constant_index);
}
break;
default:
hInteger sbInteger_mul(hInteger a, hInteger b) {
if (!is_bigint(a) && !is_bigint(b)) {
- if (b < SARABANDE_INT_MAX / a && b > SARABANDE_INT_MIN / a) {
+ flag zero_ok = (a == 0);
+ flag pos_ok = (a > 0 && b < SARABANDE_INT_MAX / a && b > SARABANDE_INT_MIN / a);
+ flag neg_ok = (a < 0 && b < SARABANDE_INT_MIN / a && b > SARABANDE_INT_MAX / a);
+ if (zero_ok || pos_ok || neg_ok) {
return sbInteger_new(a * b);
}
}
/* --- */
flag is_bigint(hInteger n) {
- return (n & FLAG_BIGINT);
+ /* bigint flag is the second bit after the sign bit, normally for smaller
+ * numbers this bit is equal to the sign bit, so we see if it's NOT equal
+ * as a flag that a number is a bigint */
+ return (n > 0 && (n & FLAG_BIGINT)) || (n < 0 && !(n & FLAG_BIGINT));
}
static intblk *alloc_new_block() {
return token_size;
}
+static void read_integer(hScanner sc, sbLexToken *new_token, flag negative) {
+ new_token->type = T_INTEGER;
+
+ i64 intval = 0;
+ int base = 10;
+ int num_done = FALSE;
+ char ch = PEEK;
+
+ if (ch == '0') {
+ /* skip a leading zero, and see if it has some base indicator */
+ ch = NEXT;
+ if (ch == 'b') {
+ ch = NEXT;
+ base = 2;
+ } else if (ch == 'o') {
+ ch = NEXT;
+ base = 8;
+ } else if (ch == 'x') {
+ ch = NEXT;
+ base = 16;
+ } else if (is_space(ch) || ch == '\n') {
+ /* it was literally just the number 0 (probably) */
+ num_done = TRUE;
+ } else {
+ new_token->type = T_BADNUMBER;
+ num_done = TRUE;
+ }
+ }
+
+ if (!num_done) {
+ do {
+ /* TODO: promote to bigint, don't allow overflow */
+ intval *= base;
+ intval += base_digit_value(ch);
+ do {
+ /* skip over underscores in numeric literals */
+ ch = NEXT;
+ } while (ch == '_');
+ } while (is_base_digit(ch, base));
+
+ /* if we are now looking at still a character that's a letter or digit, throw error */
+ if (is_digit(ch) || is_alpha(ch)) {
+ new_token->type = T_BADNUMBER;
+ NEXT;
+ } else {
+ new_token->i = intval;
+ if (negative) {
+ new_token->i *= -1;
+ }
+ }
+ }
+}
+
static sbLexToken compute_next_token(hScanner sc) {
int ch_int = PEEK;
unsigned char ch = (unsigned char)ch_int;
} else if (ch == '=') {
new_token.type = T_MINUSEQUALS;
NEXT;
+ } else if (is_digit(ch)) {
+ /* negative number literal */
+ read_integer(sc, &new_token, TRUE);
} else {
new_token.type = T_MINUS;
}
new_token.symb = sbSymbol_from_bytes(sc->dynamic_buffer.data, sc->dynamic_buffer.size - 1);
new_token.size = token_size;
} else if (is_digit(ch)) {
- new_token.type = T_INTEGER;
-
- i64 intval = 0;
- int base = 10;
- int num_done = FALSE;
-
- if (ch == '0') {
- /* skip a leading zero, and see if it has some base indicator */
- ch = NEXT;
- if (ch == 'b') {
- ch = NEXT;
- base = 2;
- } else if (ch == 'o') {
- ch = NEXT;
- base = 8;
- } else if (ch == 'x') {
- ch = NEXT;
- base = 16;
- } else if (is_space(ch) || ch == '\n') {
- /* it was literally just the number 0 (probably) */
- num_done = TRUE;
- } else {
- new_token.type = T_BADNUMBER;
- num_done = TRUE;
- }
- }
-
- if (!num_done) {
- do {
- /* TODO: promote to bigint, don't allow overflow */
- intval *= base;
- intval += base_digit_value(ch);
- do {
- /* skip over underscores in numeric literals */
- ch = NEXT;
- } while (ch == '_');
- } while (is_base_digit(ch, base));
-
- /* if we are now looking at still a character that's a letter or digit, throw error */
- if (is_digit(ch) || is_alpha(ch)) {
- new_token.type = T_BADNUMBER;
- NEXT;
- } else {
- new_token.i = intval;
- }
- }
+ read_integer(sc, &new_token, FALSE);
} else {
new_token.type = T_ERROR;
NEXT;
return cm->bytecode.size;
}
-void sbVmCompiler_add_constant(sbVmCompiler *cm, hV *constant) {
+u32 sbVmCompiler_add_constant(sbVmCompiler *cm, hV *constant) {
sbV_retain(constant);
-
sbBuffer_append(&cm->constants, constant, sizeof(hV));
+ return cm->constants.size / sizeof(hV) - 1;
}
void sbVmProgram_initialize(sbVmProgram *pm, usize initial_arena_size) {
sbVmBlock bk = {
.bytecode = bytecode,
- .bytecode_length = bytecode_length,
+ .bytecode_end = bytecode + bytecode_length,
.constants = (hV*)constants,
.constants_count = constants_length / sizeof(hV),
};
/* fixed size / frozen block of bytecode */
typedef struct sbVmBlock {
const u8 *bytecode;
- usize bytecode_length;
+ const u8 *bytecode_end;
const hV *constants;
usize constants_count;
} sbVmBlock;
usize sbVmCompiler_get_position(sbVmCompiler *cm);
-void sbVmCompiler_add_constant(sbVmCompiler *pb, hV *constant);
+u32 sbVmCompiler_add_constant(sbVmCompiler *cm, hV *constant);
void sbVmProgram_initialize(sbVmProgram *pm, usize initial_arena_size);
BC_LD_UPVAL, // push value onto stack from closure
BC_LD_BLK, // push reference to function onto stack
BC_LD_NIL, // push nil onto stack
+ BC_LD_TRUE, // push true onto stack
+ BC_LD_FALSE, // push false onto stack
BC_ST_VAR, // pop value from stack into variable
BC_ST_UPVAL, // pop value from stack into closure
BC_ST_ARG, // decrement TOS and store NOS in variable
}
sbOpcode get_opcode(hVm vm) {
+ if (vm->ip >= vm->fp->block->bytecode_end) PANIC("execution outside defined code area");
return *(vm->ip++);
}
+void next_byte(hVm vm, u64 *result) {
+ u8 byte = *(vm->ip++);
+#ifdef DEBUG
+ if (vm->debugmode) printf("%02X ", byte);
+#endif
+ *result <<= 8;
+ *result |= byte;
+}
+
/* small numbers that are parameters to opcodes can
* just be encoded directly as 1 byte. numbers of 16-bit
* 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 */
-u64 get_param(hVm vm) {
+i64 get_param(hVm vm) {
u64 result = *((u8*)vm->ip++);
+#ifdef DEBUG
+ if (vm->debugmode) printf("%02llX ", result);
+#endif
+ i64 signed_result = result;
if (result == BC_LONG_NUM) {
- result = *(vm->ip++);
- result <<= 8;
- result |= *(vm->ip++);
+ result = 0;
+ next_byte(vm, &result);
+ next_byte(vm, &result);
+ signed_result = result;
+ if (result > (1 << 15)) signed_result = result - (1 << 16);
} else if (result == BC_VLONG_NUM) {
- result = *(vm->ip++);
- result <<= 8;
- result |= *(vm->ip++);
- result <<= 8;
- result |= *(vm->ip++);
- result <<= 8;
- result |= *(vm->ip++);
+ printf("here now\n");
+ result = 0;
+ next_byte(vm, &result); // 0
+ next_byte(vm, &result); // 1
+ next_byte(vm, &result); // 2
+ next_byte(vm, &result); // 3
+ signed_result = result;
+ if (result > (1L << 31)) signed_result = result - (1L << 32);
} 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++);
+ result = 0;
+ next_byte(vm, &result); // 0
+ next_byte(vm, &result); // 1
+ next_byte(vm, &result); // 2
+ next_byte(vm, &result); // 3
+ next_byte(vm, &result); // 4
+ next_byte(vm, &result); // 5
+ next_byte(vm, &result); // 6
+ next_byte(vm, &result); // 7
+ signed_result = (i64)result;
}
- return result;
+ return signed_result;
}
/* execute one instruction! wow! */
void execute_instruction(hVm vm) {
sbOpcode op = get_opcode(vm);
+#ifdef DEBUG
+ if (vm->debugmode) printf("op %02X ", op);
+#endif
u64 param;
hV *v, *w, res;
switch (op) {
case BC_NOP:
- return;
+ break;
case BC_HALT:
vm->running = FALSE;
- return;
+ break;
case BC_LD_IMM:
param = get_param(vm);
push_stack(vm, &HVINT(param));
- return;
+ break;
case BC_LD_CONST:
param = get_param(vm);
push_stack(vm, &vm->fp->block->constants[param]);
- return;
+ break;
case BC_LD_CTX:
PANIC("todo");
case BC_LD_VAR:
param = get_param(vm);
push_stack(vm, &vm->fp->locals[param]);
- return;
+ break;
case BC_LD_UPVAL:
/* Hey, was there upval in there? */
PANIC("todo");
case BC_LD_BLK:
param = get_param(vm);
push_stack(vm, &HVFUNC(param));
- return;
+ break;
case BC_LD_NIL:
push_stack(vm, &HVNIL);
- return;
+ break;
case BC_ST_VAR:
param = get_param(vm);
v = peek_stack(vm, 0);
store_local(vm, param, v);
pop_stack(vm);
- return;
+ break;
case BC_ST_UPVAL:
PANIC("todo");
case BC_ST_ARG:
/* if last integer, don't put the 0 count back on the stack */
push_stack(vm, v);
}
- return;
+ break;
case BC_POP:
v = pop_stack(vm);
- return;
+ break;
case BC_NPOP:
param = get_param(vm);
npop_stack(vm, param);
- return;
+ break;
case BC_CALL:
v = pop_stack(vm);
if (v->type != IT_FUNCTION) {
PANIC("attempt to call a non-function value");
}
call_block(vm, v->data);
- return;
+ break;
case BC_NUMARG:
param = get_param(vm);
v = pop_stack(vm);
PANIC("wrong number of arguments passed to function.");
}
push_stack(vm, v);
- return;
+ break;
case BC_JMP:
param = get_param(vm);
vm->ip = &vm->fp->block->bytecode[param];
- return;
+ break;
case BC_JT:
param = get_param(vm);
v = pop_stack(vm);
if (!sbV_c_falsy(v)) {
vm->ip = &vm->fp->block->bytecode[param];
}
- return;
+ break;
case BC_JF:
param = get_param(vm);
v = pop_stack(vm);
if (sbV_c_falsy(v)) {
vm->ip = &vm->fp->block->bytecode[param];
}
- return;
+ break;
case BC_RET:
return_from_block(vm);
- return;
+ break;
case BC_SEND:
PANIC("todo");
case BC_OP_EQ:
res = sbV_eq(v, w);
npop_stack(vm, 2);
push_stack(vm, &res);
- return;
+ break;
case BC_OP_NOT:
v = pop_stack(vm);
if (sbV_c_falsy(v)) {
} else {
push_stack(vm, &HVBOOL(FALSE));
}
- return;
+ break;
case BC_OP_LT:
v = peek_stack(vm, 1);
w = peek_stack(vm, 0);
res = sbV_lt(v, w);
npop_stack(vm, 2);
push_stack(vm, &res);
- return;
+ break;
case BC_OP_LE:
v = peek_stack(vm, 1);
w = peek_stack(vm, 0);
res = sbV_le(v, w);
npop_stack(vm, 2);
push_stack(vm, &res);
- return;
+ break;
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;
+ break;
case BC_OP_SUB:
v = peek_stack(vm, 1);
w = peek_stack(vm, 0);
res = sbV_sub(v, w);
npop_stack(vm, 2);
push_stack(vm, &res);
- return;
+ break;
case BC_OP_MUL:
v = peek_stack(vm, 1);
w = peek_stack(vm, 0);
res = sbV_mul(v, w);
npop_stack(vm, 2);
push_stack(vm, &res);
- return;
+ break;
case BC_OP_DIV:
PANIC("todo");
case BC_OP_FLDIV:
res = sbV_floordiv(v, w);
npop_stack(vm, 2);
push_stack(vm, &res);
- return;
+ break;
case BC_OP_NEG:
case BC_OP_MOD:
case BC_OP_POW:
res = sbV_incr(v);
pop_stack(vm);
push_stack(vm, &res);
- return;
+ break;
case BC_OP_DECR:
v = peek_stack(vm, 0);
res = sbV_decr(v);
pop_stack(vm);
push_stack(vm, &res);
- return;
+ break;
case BC_OP_DEREF:
PANIC("todo");
case BC_ALLOC_VARS:
+ /* TODO check for overflow */
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) */
/* we allocate one more variable at 0 for internal use */
vm->rp += (param + 1) * sizeof(hV);
vm->fp->num_locals += param;
- return;
+ break;
case BC_LIST_GATHER:
case BC_HASH_GATHER:
PANIC("todo");
default:
PANIC("unrecognized opcode $%02X at position $%016zX", op, (usize)vm->ip);
}
+#ifdef DEBUG
+ if (vm->debugmode) printf("\n");
+#endif
}
} sbVmStackFrame;
typedef struct sbVm {
- u8 *stack; /* for calculations */
- u8 *rstack; /* for locals and return addresses, like FORTH */
+ u8 *stack; /* for calculations */
+ u8 *rstack; /* for locals and return addresses, like FORTH */
- const u8 *ip; /* instruction pointer */
+ const u8 *ip; /* instruction pointer */
sbVmStackFrame *fp; /* frame pointer */
- u8 *sp; /* stack pointer */
- u8 *rp; /* rstack pointer */
+ u8 *sp; /* stack pointer */
+ u8 *rp; /* rstack pointer */
usize stacksize; /* to detect overflow, save these */
usize rstacksize; /* ^^ */