--- /dev/null
+#include "compile/emit.h"
+
+#define STRING1(...) #__VA_ARGS__
+#define STRING2(...) STRING1(__VA_ARGS__)
+#define EMIT(...) do { printf(STRING2(__VA_ARGS__) "\n"); sbVmCompiler_write_code(cm, (u8[]) { __VA_ARGS__ }, sizeof((u8[]) { __VA_ARGS__ })); } while (0)
+#define EARG(x) (emit_arg(cm, x))
+
+struct labelpos {
+ sbIrLabel *label;
+ u32 offset;
+};
+
+void compile_chunk(sbVmCompiler *cm, sbIrChunk *chunk);
+void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt);
+void compile_expr(sbVmCompiler *cm, sbIrExpr *expr);
+
+void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir) {
+ sbVmCompiler cm = sbVmCompiler_create(4096, 4096);
+
+ int nchunks = ir->chunks.size / sizeof(sbIrChunk*);
+ for (int i = 0; i < nchunks; i++) {
+ sbIrChunk *chunk = ((sbIrChunk**)ir->chunks.data)[i];
+
+ compile_chunk(&cm, chunk);
+
+ sbVmProgram_add_block(vp, &cm);
+ sbVmCompiler_reset(&cm);
+ }
+
+ 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);
+ u8 buf[9];
+ if (number < 256) {
+ buf[0] = number;
+ sbVmCompiler_write_code(cm, buf, 1);
+ } else if (number < 65536) {
+ buf[0] = BC_LONG_NUM;
+ buf[1] = number >> 8;
+ buf[2] = number & 0xFF;
+ sbVmCompiler_write_code(cm, buf, 3);
+ } else if (number < (1LL << 32)) {
+ buf[0] = BC_VLONG_NUM;
+ buf[1] = (number >> 24) & 0xFF;;
+ buf[2] = (number >> 16) & 0xFF;
+ buf[3] = (number >> 8) & 0xFF;
+ buf[4] = number & 0xFF;
+ sbVmCompiler_write_code(cm, buf, 5);
+ } else {
+ buf[0] = BC_VVLONG_NUM;
+ buf[1] = (number >> 56) & 0xFF;;
+ buf[2] = (number >> 48) & 0xFF;;
+ buf[3] = (number >> 40) & 0xFF;;
+ buf[4] = (number >> 32) & 0xFF;;
+ buf[5] = (number >> 24) & 0xFF;;
+ buf[6] = (number >> 16) & 0xFF;
+ buf[7] = (number >> 8) & 0xFF;
+ buf[8] = number & 0xFF;
+ sbVmCompiler_write_code(cm, buf, 9);
+ }
+}
+
+void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) {
+ printf("%zu ", sbVmCompiler_get_position(cm));
+ switch (stmt->type) {
+ case IR_S_EXPR:
+ compile_expr(cm, stmt->expr);
+ EMIT(BC_POP);
+ break;
+ case IR_S_JUMP:
+ if (stmt->jump.label->found_yet) {
+ if (stmt->jump.condition == NULL) {
+ EMIT(BC_JMP);
+ } else {
+ compile_expr(cm, stmt->jump.condition);
+ if (stmt->jump.inverted) {
+ EMIT(BC_JF);
+ } else {
+ EMIT(BC_JT);
+ }
+ }
+ EARG(stmt->jump.label->block_position);
+ } else {
+ printf("haven't implemented forward jump yet!\n");
+ }
+ break;
+ case IR_S_LABEL:
+ stmt->label->found_yet = TRUE;
+ stmt->label->block_position = sbVmCompiler_get_position(cm);
+ printf("\n");
+ break;
+ case IR_S_RETURN:
+ if (stmt->expr) {
+ compile_expr(cm, stmt->expr);
+ }
+ EMIT(BC_RET);
+ break;
+ default:
+ printf("haven't implemented this yet!\n");
+ }
+}
+
+void compile_op(sbVmCompiler *cm, sbAstOp op);
+void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) {
+ switch(expr->type) {
+ case IR_E_OP:
+ compile_expr(cm, expr->op.left);
+ if (expr->op.right) {
+ compile_expr(cm, expr->op.right);
+ }
+ compile_op(cm, expr->op.type);
+ break;
+ default:
+ printf("(compile an expr)\n");
+ }
+}
+
+void compile_op(sbVmCompiler *cm, sbAstOp op) {
+ switch (op) {
+ case AST_OP_ADD: EMIT(BC_OP_ADD); break;
+ case AST_OP_SUB: EMIT(BC_OP_SUB); break;
+ default:
+ printf("unknown operation!\n");
+ }
+}
--- /dev/null
+#include "common.h"
+
+#include "compile/ir.h"
+#include "vm/block.h"
+
+/* Stage 6 of the compiler. The Final Stage.
+ * Take the IR that was produced from the ir module, and
+ * convert it into bytecode that our VM can understand. */
+
+/* Since control flow and variable scope was already
+ * linearized by the previous step, emitting these is
+ * fairly straightforward (except that we have to compile
+ * each block in two passes in order to compute the
+ * position of forward jumps.) */
+
+/* Expressions coming from the IR are still nested in a
+ * tree structure like the AST, because it's easier to
+ * flatten them in this step, where we are producing
+ * code for a stack machine. This is pretty simple; we just
+ * have to essentially convert the tree to "reverse polish
+ * notation." */
+
+void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir);
static void print_stmt(sbIrStmt *s) {
switch (s->type) {
case IR_S_ARG:
+ printf(" bind function argument to variable %zu\n", s->arg.var->slot_id);
if (s->arg.last) {
- printf(" bind last function argument to variable %zu\n", s->arg.var->slot_id);
- } else {
- printf(" bind function argument to variable %zu\n", s->arg.var->slot_id);
+ printf(" (no function arguments left on the stack now)\n");
}
break;
case IR_S_LABEL:
#include "data/hashtable.h"
#include "data/symbol.h"
#include "compile/ir.h"
+#include "compile/emit.h"
#include "vm/exec.h"
int main(int argc, char **argv) {
sbIrProgram_print(&ir);
}
+ sbVmProgram pm;
+ sbVmProgram_initialize(&pm, 65536);
+
+ sbEmit_compile_program(&pm, &ir);
+
sbIrProgram_deinitialize(&ir);
+ sbVmProgram_deinitialize(&pm);
sbSymbol_sys_deinit();
sbHash_sys_deinit();
sbString_sys_deinit();
sbVmProgram pm;
sbVmProgram_initialize(&pm, 65536);
- sbVmPartialBlock pb = sbVmBlock_create(4096, 4096);
+ sbVmCompiler pb = sbVmCompiler_create(4096, 4096);
u8 code1[] = {
BC_ALLOC_VARS, 0x02, /* 0 1 */
BC_HALT,
};
- sbVmBlock_add_constant(&pb, &HVINT(5));
- sbVmBlock_add_constant(&pb, &HVINT(9));
+ sbVmCompiler_add_constant(&pb, &HVINT(5));
+ sbVmCompiler_add_constant(&pb, &HVINT(9));
- sbVmBlock_write_code(&pb, code1, sizeof(code1));
+ sbVmCompiler_write_code(&pb, code1, sizeof(code1));
sbVmProgram_add_block(&pm, &pb);
- sbVmBlock_reset(&pb);
+ sbVmCompiler_reset(&pb);
u8 code2[] = {
BC_LD_CONST, 0x00,
BC_RET,
};
- sbVmBlock_add_constant(&pb, &HVINT(7));
+ sbVmCompiler_add_constant(&pb, &HVINT(7));
- sbVmBlock_write_code(&pb, code2, sizeof(code2));
+ sbVmCompiler_write_code(&pb, code2, sizeof(code2));
sbVmProgram_add_block(&pm, &pb);
- sbVmBlock_reset(&pb);
+ sbVmCompiler_reset(&pb);
+
+ sbVmCompiler_deinitialize(&pb);
sbVm_execute(&vm, &pm);
printf("%02X ", *p);
}
printf("\n");
-
- sbVmBlock_deinitialize(&pb);
}
#define INITIAL_PROGRAM_BLOCK_SIZE 32
-sbVmPartialBlock sbVmBlock_create(usize initial_bytecode_size, usize initial_constant_size) {
- sbVmPartialBlock pb = {0};
- sbBuffer_initialize(&pb.bytecode, initial_bytecode_size);
- sbBuffer_initialize(&pb.constants, initial_constant_size);
- return pb;
+sbVmCompiler sbVmCompiler_create(usize initial_bytecode_size, usize initial_constant_size) {
+ sbVmCompiler cm = {0};
+ sbBuffer_initialize(&cm.bytecode, initial_bytecode_size);
+ sbBuffer_initialize(&cm.constants, initial_constant_size);
+ sbBuffer_initialize(&cm.label_positions, 1024);
+ return cm;
}
-void sbVmBlock_reset(sbVmPartialBlock *pb) {
- sbBuffer_reset(&pb->bytecode);
- sbBuffer_reset(&pb->constants);
+void sbVmCompiler_reset(sbVmCompiler *cm) {
+ sbBuffer_reset(&cm->bytecode);
+ sbBuffer_reset(&cm->constants);
}
-void sbVmBlock_deinitialize(sbVmPartialBlock *pb) {
- sbBuffer_deinitialize(&pb->bytecode);
- sbBuffer_deinitialize(&pb->constants);
+void sbVmCompiler_deinitialize(sbVmCompiler *cm) {
+ sbBuffer_deinitialize(&cm->bytecode);
+ sbBuffer_deinitialize(&cm->constants);
+ sbBuffer_deinitialize(&cm->label_positions);
}
-void sbVmBlock_write_code(sbVmPartialBlock *pb, const u8 *data, usize length) {
- sbBuffer_append(&pb->bytecode, data, length);
+void sbVmCompiler_write_code(sbVmCompiler *cm, const u8 *data, usize length) {
+ sbBuffer_append(&cm->bytecode, data, length);
}
-void sbVmBlock_add_constant(sbVmPartialBlock *pb, hV *constant) {
+usize sbVmCompiler_get_position(sbVmCompiler *cm) {
+ return cm->bytecode.size;
+}
+
+void sbVmCompiler_add_constant(sbVmCompiler *cm, hV *constant) {
sbV_retain(constant);
- sbBuffer_append(&pb->constants, constant, sizeof(hV));
+ sbBuffer_append(&cm->constants, constant, sizeof(hV));
}
void sbVmProgram_initialize(sbVmProgram *pm, usize initial_arena_size) {
/* finalize a partial-block and add it to the program.
* copies the data, so the same partial-block object can be reset
* and reused after calling this function. */
-sbBlockId sbVmProgram_add_block(sbVmProgram *pm, sbVmPartialBlock *pb) {
- usize bytecode_length = pb->bytecode.size;
- usize constants_length = pb->constants.size;
+sbBlockId sbVmProgram_add_block(sbVmProgram *pm, sbVmCompiler *cm) {
+ usize bytecode_length = cm->bytecode.size;
+ usize constants_length = cm->constants.size;
while (bytecode_length % 8 != 0) bytecode_length ++;
u8 *block_data = sbArena_alloc(&pm->arena, bytecode_length + constants_length);
u8 *bytecode = &block_data[0];
- memcpy(bytecode, pb->bytecode.data, bytecode_length);
+ memcpy(bytecode, cm->bytecode.data, bytecode_length);
u8 *constants = &block_data[bytecode_length];
- memcpy(constants, pb->constants.data, constants_length);
+ memcpy(constants, cm->constants.data, constants_length);
sbVmBlock bk = {
.bytecode = bytecode,
+#ifndef __SARABANDE_BLOCK_H__
+#define __SARABANDE_BLOCK_H__
+
#include "common.h"
#define TEST_VM_BLK(bc, cn) ((sbVmBlock) { \
/* dynamic sized block that we can more easily add
* things to while compiling */
-typedef struct sbVmPartialBlock {
+typedef struct sbVmCompiler {
sbBuffer bytecode;
sbBuffer constants;
-} sbVmPartialBlock;
+ sbBuffer label_positions;
+} sbVmCompiler;
/* fixed size / frozen block of bytecode */
typedef struct sbVmBlock {
typedef usize sbBlockId;
-sbVmPartialBlock sbVmBlock_create(usize initial_bytecode_size, usize initial_constant_size);
+sbVmCompiler sbVmCompiler_create(usize initial_bytecode_size, usize initial_constant_size);
+
+void sbVmCompiler_reset(sbVmCompiler *pb);
-void sbVmBlock_reset(sbVmPartialBlock *pb);
+void sbVmCompiler_deinitialize(sbVmCompiler *pb);
-void sbVmBlock_deinitialize(sbVmPartialBlock *pb);
+void sbVmCompiler_write_code(sbVmCompiler *pb, const u8 *data, usize length);
-void sbVmBlock_write_code(sbVmPartialBlock *pb, const u8 *data, usize length);
+usize sbVmCompiler_get_position(sbVmCompiler *cm);
-void sbVmBlock_add_constant(sbVmPartialBlock *pb, hV *constant);
+void sbVmCompiler_add_constant(sbVmCompiler *pb, hV *constant);
void sbVmProgram_initialize(sbVmProgram *pm, usize initial_arena_size);
/* finalize a partial-block and add it to the program.
* copies the data, so the same partial-block object can be reset
* and reused after calling this function. */
-sbBlockId sbVmProgram_add_block(sbVmProgram *pm, sbVmPartialBlock *pb);
+sbBlockId sbVmProgram_add_block(sbVmProgram *pm, sbVmCompiler *pb);
+
+#endif