CC=gcc
CFLAGS=-Wall -g -DDEBUG -fsanitize=undefined
-#CFLAGS=-Wall -O3 -flto -DGLEW_STATIC -DDEBUG -fsanitize=undefined
+#CFLAGS=-Wall -O3 -flto
LIBS=
SRC := src
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -I$(SRC) $(OBJECTS) $(LIBS) -o $@
-.PHONY: parsedebug
+.PHONY: parsedebug optimized
parsedebug: CFLAGS += -DPARSEDEBUG
parsedebug: build/a.out
+optimized: CFLAGS=-Wall -O3 -flto
+optimized: build/a.out
+
$(OBJ)/%.o: $(SRC)/%.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(LIBS) -I$(SRC) -c $< -o $@
sbVmCompiler_deinitialize(&cm);
}
-void compile_chunk(sbVmCompiler *cm, sbIrChunk *chunk) {
- //sbVmCompiler_add_constant(&cm, &HVINT(5));
- //sbVmCompiler_add_constant(&cm, &HVINT(9));
-
- printf("\n--block--\n");
-
- int nstmts = chunk->stmts.size / sizeof(sbIrStmt*);
- for (int i = 0; i < nstmts; i++) {
- sbIrStmt *stmt = ((sbIrStmt**)chunk->stmts.data)[i];
-
- compile_stmt(cm, stmt);
- }
-}
-
-/* when compiling one line at a time, we don't know the position
- * of labels we haven't seen yet. we leave a 4-byte slot for a blank
- * address, and say "please put the address of this label here later!" */
-void record_labelpos(sbVmCompiler *cm, sbIrLabel *label, u32 offset) {
- struct labelpos lp = {
- };
-
- sbBuffer_append(&cm->label_positions, &lp, sizeof(struct labelpos));
-}
+/* --- */
void emit_arg(sbVmCompiler *cm, usize number) {
- printf(" arg %zu\n", number);
+ printf(" arg %zu\n", number);
u8 buf[9];
if (number < 256) {
buf[0] = number;
}
}
+
+void compile_chunk(sbVmCompiler *cm, sbIrChunk *chunk) {
+ //sbVmCompiler_add_constant(&cm, &HVINT(5));
+ //sbVmCompiler_add_constant(&cm, &HVINT(9));
+
+ printf("\n--block %d--\n", chunk->id);
+
+ int nstmts = chunk->stmts.size / sizeof(sbIrStmt*);
+ if (chunk->id > 0) {
+ /* don't check args for initial block
+ * (note: when we have modules or whatever, this might
+ * be an incorrect way to do this) */
+ EMIT(BC_NUMARG);
+ EARG(chunk->num_args);
+ }
+ for (int i = 0; i < nstmts; i++) {
+ sbIrStmt *stmt = ((sbIrStmt**)chunk->stmts.data)[i];
+
+ compile_stmt(cm, stmt);
+ }
+}
+
+/* when compiling one line at a time, we don't know the position
+ * of labels we haven't seen yet. we leave a 4-byte slot for a blank
+ * address, and say "please put the address of this label here later!" */
+void record_labelpos(sbVmCompiler *cm, sbIrLabel *label, u32 offset) {
+ struct labelpos lp = {
+ };
+
+ sbBuffer_append(&cm->label_positions, &lp, sizeof(struct labelpos));
+}
+
void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) {
- printf("%zu ", sbVmCompiler_get_position(cm));
+ printf("%3zu ", sbVmCompiler_get_position(cm));
switch (stmt->type) {
case IR_S_EXPR:
compile_expr(cm, stmt->expr);
stmt->label->block_position = sbVmCompiler_get_position(cm);
printf("\n");
break;
+ case IR_S_ASSIGN:
+ compile_expr(cm, stmt->assign.expr);
+ EMIT(BC_ST_VAR);
+ EARG(stmt->assign.var->slot_id);
+ break;
+ case IR_S_ARG:
+ EMIT(BC_ST_ARG);
+ EARG(stmt->arg.var->slot_id);
+ break;
case IR_S_RETURN:
if (stmt->expr) {
compile_expr(cm, stmt->expr);
void compile_op(sbVmCompiler *cm, sbAstOp op);
void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) {
+ sbIrExpr *E1;
+ int count = 0;
switch(expr->type) {
case IR_E_OP:
compile_expr(cm, expr->op.left);
}
compile_op(cm, expr->op.type);
break;
+ case IR_E_CALL:
+ E1 = expr->call.param;
+ while (E1) {
+ count ++;
+ compile_expr(cm, E1->param.this);
+ E1 = E1->param.next;
+ }
+ EMIT(BC_LD_IMM);
+ EARG(count); /* calling convention: store argument count on stack */
+ compile_expr(cm, expr->call.func);
+ EMIT(BC_CALL);
+ break;
+ case IR_E_VAR:
+ EMIT(BC_LD_VAR);
+ EARG(expr->var->slot_id);
+ break;
+ case IR_E_FUNC:
+ EMIT(BC_LD_BLK);
+ EARG(expr->func->id);
+ break;
+ case IR_E_VALUE:
+ if (expr->value.type == IT_NIL) {
+ EMIT(BC_LD_NIL);
+ } else if (expr->value.type == IT_INTEGER) {
+ EMIT(BC_LD_IMM);
+ EARG(expr->value.integer);
+ } else {
+ printf("(some value)\n");
+ }
+ break;
default:
printf("(compile an expr)\n");
}
switch (op) {
case AST_OP_ADD: EMIT(BC_OP_ADD); break;
case AST_OP_SUB: EMIT(BC_OP_SUB); break;
+ case AST_OP_MUL: EMIT(BC_OP_MUL); break;
+ case AST_OP_FLDIV: EMIT(BC_OP_FLDIV); break;
+ case AST_OP_EQ: EMIT(BC_OP_EQ); break;
+ case AST_OP_NE: EMIT(BC_OP_EQ, BC_OP_NOT); break;
+ case AST_OP_LT: EMIT(BC_OP_LT); break;
+ case AST_OP_GT: EMIT(BC_OP_LE, BC_OP_NOT); break;
+ case AST_OP_LE: EMIT(BC_OP_LE); break;
+ case AST_OP_GE: EMIT(BC_OP_LT, BC_OP_NOT); break;
default:
printf("unknown operation!\n");
}
sbBuffer_append(&arg_vars, &v1, sizeof(sbIrVariable*));
v1->initialized = TRUE;
param_comma = param_comma->seq.right;
+ ck->num_args ++;
}
while (arg_vars.size > 0) {
typedef struct sbIrChunk {
struct sbIrProgram *program;
i32 id;
+ i16 num_args;
+ flag variadic;
i32 label_count;
i32 variable_count;
i32 lowest_var_id;
if (a->type == IT_INTEGER && b->type == IT_INTEGER) {
return sbV_int(sbInteger_sum(a->integer, b->integer));
} else {
- PANIC("todo");
+ PANIC("todo (add type %llu and type %llu)", a->type, b->type);
}
}
return HVBOOL(FALSE);
}
}
+
+hV sbV_lt(const hV *a, const hV *b) {
+ if (a->type == IT_INTEGER && b->type == IT_INTEGER) {
+ if (a->integer < b->integer) {
+ return HVBOOL(TRUE);
+ } else {
+ return HVBOOL(FALSE);
+ }
+ } else {
+ PANIC("todo");
+ }
+}
+
+hV sbV_le(const hV *a, const hV *b) {
+ if (a->type == IT_INTEGER && b->type == IT_INTEGER) {
+ if (a->integer <= b->integer) {
+ return HVBOOL(TRUE);
+ } else {
+ return HVBOOL(FALSE);
+ }
+ } else {
+ PANIC("todo");
+ }
+}
hV sbV_decr(const hV *a);
hV sbV_eq(const hV *a, const hV *b);
+
+hV sbV_lt(const hV *a, const hV *b);
+
+hV sbV_le(const hV *a, const hV *b);
};
}
+hV sbV_function(u64 id) {
+ return (hV) {
+ .type = IT_FUNCTION,
+ .data = id,
+ };
+}
+
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;
#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 })
+#define HVFUNC(i) ((hV) { .type = IT_FUNCTION, .data = i })
typedef u64 hHash;
typedef u64 hString;
hInteger integer;
u64 boolean;
double float_val;
+ u64 data;
};
} hV;
hV sbV_hash(hHash hash);
hV sbV_int(hInteger i);
hV sbV_boolean(flag b);
+hV sbV_function(u64 id);
flag sbV_c_eq(const hV *a, const hV *b);
flag sbV_c_falsy(const hV *a);
#include "vm/exec.h"
int main(int argc, char **argv) {
- if (argc == 2) {
+ if (argc >= 2) {
sbString_sys_init();
sbHash_sys_init();
sbSymbol_sys_init();
sbParser pr;
sbParser_initialize(&pr);
- sbAst parse_result = sbParser_parse_file(&pr, argv[1]);
+ const char *filename;
+ flag debugmode = FALSE;
+ if (argc == 3 && sbstrncmp(argv[1], "-D", 2) == 0) {
+ filename = argv[2];
+ debugmode = TRUE;
+ } else if (argc == 2) {
+ filename = argv[1];
+ } else {
+ printf("usage: %s [-D] <filename>\n", argv[0]);
+ printf("usage: -D = bytecode debugger\n");
+ return 0;
+ }
+
+ sbAst parse_result = sbParser_parse_file(&pr, filename);
if (parse_result == NULL) {
- fprintf(stderr, "fatal error: Could not open script '%s'\n", argv[1]);
+ fprintf(stderr, "fatal error: Could not open script '%s'\n", filename);
return -2;
} else if (parse_result->type == AST_ERROR) {
- fprintf(stderr, "fatal error: Could not run '%s' due to syntax errors.\n", argv[1]);
+ fprintf(stderr, "fatal error: Could not run '%s' due to syntax errors.\n", filename);
return -1;
}
sbParser_deinitialize(&pr);
if (ir.error_count > 0) {
- fprintf(stderr, "Could not run '%s' due to errors.\n", argv[1]);
+ fprintf(stderr, "Could not run '%s' due to errors.\n", filename);
return -3;
} else {
// print out program
sbIrProgram_deinitialize(&ir);
+ sbVm vm = {0};
+ sbVm_initialize(&vm, 1048576, 1048576, debugmode);
+
+ sbVm_execute(&vm, &pm);
+
+ printf("Stack result: ");
+ for (u8 *p = vm.stack; p < vm.sp; p++) {
+ printf("%02X ", *p);
+ }
+ printf("\n");
+
sbVmProgram_deinitialize(&pm);
+ sbVm_deinitialize(&vm);
sbSymbol_sys_deinit();
sbHash_sys_deinit();
sbString_sys_deinit();
} else {
fprintf(stderr, "please provide a file as input\n");
}
-
- sbVm vm = {0};
- sbVm_initialize(&vm, 1048576, 1048576);
-
- sbVmProgram pm;
- sbVmProgram_initialize(&pm, 65536);
-
- sbVmCompiler pb = sbVmCompiler_create(4096, 4096);
-
- 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,
- };
-
- sbVmCompiler_add_constant(&pb, &HVINT(5));
- sbVmCompiler_add_constant(&pb, &HVINT(9));
-
- sbVmCompiler_write_code(&pb, code1, sizeof(code1));
- sbVmProgram_add_block(&pm, &pb);
- sbVmCompiler_reset(&pb);
-
- u8 code2[] = {
- BC_LD_CONST, 0x00,
- BC_OP_ADD,
- BC_RET,
- };
-
- sbVmCompiler_add_constant(&pb, &HVINT(7));
-
- sbVmCompiler_write_code(&pb, code2, sizeof(code2));
- sbVmProgram_add_block(&pm, &pb);
- sbVmCompiler_reset(&pb);
-
- sbVmCompiler_deinitialize(&pb);
-
- sbVm_execute(&vm, &pm);
-
- printf("Stack result: ");
- for (u8 *p = vm.stack; p < vm.sp; p++) {
- printf("%02X ", *p);
- }
- printf("\n");
}
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_LD_BLK, // push reference to function onto stack
+ BC_LD_NIL, // push nil onto stack
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_ST_ARG, // decrement TOS and store NOS in variable
+ BC_POP, // pop value from stack
+ BC_NPOP, // pop N values from stack given by top number
BC_CALL, // push return address and jump to new block
BC_RET, // return to calling block
+ BC_NUMARG, // check number of function arguments = this
+ BC_MINARG, // check number of function arguments > this
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_EQ, // ==
BC_OP_NOT, // boolean not
BC_OP_ADD, // add
BC_OP_SUB, // subtract
BC_OP_DIV, // true divide
BC_OP_FLDIV, // floor divide
BC_OP_NEG, // negative
- BC_OP_MOD = 0x18, // mod
+ BC_OP_MOD, // mod
BC_OP_POW, // power
BC_OP_INCR, // increment
BC_OP_DECR, // decrement
BC_OP_DEREF, // dereference pointer
+ BC_OP_LT, // less than
+ BC_OP_LE, // less than or equal to
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
void call_block(hVm vm, usize block_id);
void execute_instruction(hVm vm);
-void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize) {
+void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize, flag debugmode) {
*vm = (sbVm) {0};
vm->stack = malloc(stacksize);
vm->stacksize = stacksize;
vm->sp = vm->stack;
vm->fp = (sbVmStackFrame*)vm->rstack;
vm->rp = vm->rstack;
+
+ vm->debugmode = debugmode;
}
void sbVm_deinitialize(hVm vm) {
while (vm->running) {
execute_instruction(vm);
+ if (vm->debugmode) {
+ printf("Stack: ");
+ for (u8 *p = vm->stack; p < vm->sp; p++) {
+ printf("%02X ", *p);
+ }
+ printf("\n");
+ }
}
return VM_STAT_SUCCESS;
sbVmStackFrame *frame = (sbVmStackFrame*)vm->fp;
- vm->fp = frame->last_fp;
- vm->rp = frame->last_rp;
- vm->ip = frame->return_addr;
+ if (frame->return_addr == NULL) {
+ /* returning to address 0 means quit */
+ vm->running = FALSE;
+ } else {
+ vm->fp = frame->last_fp;
+ vm->rp = frame->last_rp;
+ vm->ip = frame->return_addr;
+ }
}
void debug_print_hv(const hV *value) {
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;
case BC_ST_VAR:
param = get_param(vm);
v = peek_stack(vm, 0);
return;
case BC_ST_UPVAL:
PANIC("todo");
+ case BC_ST_ARG:
+ param = get_param(vm);
+ v = pop_stack(vm); /* argument count */
+ if (v->type != IT_INTEGER) {
+ /* internal error! this should be generated correctly */
+ PANIC("calling convention violation: number of args should be an integer");
+ }
+ w = pop_stack(vm); /* argument value */
+ store_local(vm, param, w);
+ /* we track the number of arguments remaining, for variadic functions later */
+ v->integer --;
+ if (v->integer > 0) {
+ /* if last integer, don't put the 0 count back on the stack */
+ push_stack(vm, v);
+ }
+ return;
case BC_POP:
v = pop_stack(vm);
return;
return;
case BC_CALL:
v = pop_stack(vm);
- call_block(vm, v->integer);
+ if (v->type != IT_FUNCTION) {
+ /* We need to figure out exception support or some such.
+ * User error should not panic. */
+ PANIC("attempt to call a non-function value");
+ }
+ call_block(vm, v->data);
+ return;
+ case BC_NUMARG:
+ param = get_param(vm);
+ v = pop_stack(vm);
+ if (v->type != IT_INTEGER) {
+ /* internal error! this should be generated correctly */
+ PANIC("calling convention violation: number of args should be an integer");
+ }
+ if (v->integer != param) {
+ /* This should be an exception. */
+ PANIC("wrong number of arguments passed to function.");
+ }
+ push_stack(vm, v);
return;
case BC_JMP:
param = get_param(vm);
push_stack(vm, &HVBOOL(FALSE));
}
return;
+ 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;
+ 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;
case BC_OP_ADD:
v = peek_stack(vm, 1);
w = peek_stack(vm, 0);
PANIC("todo");
case BC_LONG_NUM:
case BC_VLONG_NUM:
- PANIC("illegal opcode $%02X at position $%08zX\n", op, (usize)vm->ip);
+ PANIC("illegal opcode $%02X at position $%016zX", op, (usize)vm->ip);
default:
- PANIC("unrecognized opcode $%02X at position $%08zX\n", op, (usize)vm->ip);
+ PANIC("unrecognized opcode $%02X at position $%016zX", op, (usize)vm->ip);
}
}
sbVmProgram *program;
flag running;
+ flag debugmode;
} sbVm;
typedef sbVm *hVm;
-void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize);
+void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize, flag debugmode);
void sbVm_deinitialize(hVm vm);