From: cassowarii Date: Tue, 30 Jun 2026 01:55:42 +0000 (-0700) Subject: janky beginnings of emitting bytecode X-Git-Url: https://www.git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=46d51f0aca62956b0bd5c8640d889ff95e083809;p=sarabande.git janky beginnings of emitting bytecode --- diff --git a/src/compile/emit.c b/src/compile/emit.c new file mode 100644 index 0000000..66b18cc --- /dev/null +++ b/src/compile/emit.c @@ -0,0 +1,151 @@ +#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"); + } +} diff --git a/src/compile/emit.h b/src/compile/emit.h new file mode 100644 index 0000000..f909625 --- /dev/null +++ b/src/compile/emit.h @@ -0,0 +1,23 @@ +#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); diff --git a/src/compile/ir.c b/src/compile/ir.c index ba06457..4a3150c 100644 --- a/src/compile/ir.c +++ b/src/compile/ir.c @@ -768,10 +768,9 @@ static void print_expr(sbIrExpr *e) { 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: diff --git a/src/main.c b/src/main.c index 272ae2e..7490375 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ #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) { @@ -45,8 +46,14 @@ 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(); @@ -60,7 +67,7 @@ int main(int argc, char **argv) { 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 */ @@ -84,12 +91,12 @@ int main(int argc, char **argv) { 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, @@ -97,11 +104,13 @@ int main(int argc, char **argv) { 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); @@ -110,6 +119,4 @@ int main(int argc, char **argv) { printf("%02X ", *p); } printf("\n"); - - sbVmBlock_deinitialize(&pb); } diff --git a/src/vm/block.c b/src/vm/block.c index fe6757e..804f476 100644 --- a/src/vm/block.c +++ b/src/vm/block.c @@ -2,31 +2,37 @@ #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) { @@ -45,18 +51,18 @@ void sbVmProgram_deinitialize(sbVmProgram *pm) { /* 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, diff --git a/src/vm/block.h b/src/vm/block.h index 6e9fb71..2e63326 100644 --- a/src/vm/block.h +++ b/src/vm/block.h @@ -1,3 +1,6 @@ +#ifndef __SARABANDE_BLOCK_H__ +#define __SARABANDE_BLOCK_H__ + #include "common.h" #define TEST_VM_BLK(bc, cn) ((sbVmBlock) { \ @@ -9,10 +12,11 @@ /* 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 { @@ -31,15 +35,17 @@ typedef struct sbVmProgram { 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); @@ -48,4 +54,6 @@ void sbVmProgram_deinitialize(sbVmProgram *pm); /* 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