From: cassowarii Date: Thu, 23 Jul 2026 17:41:55 +0000 (-0700) Subject: EMIT and various util cleanup X-Git-Url: https://git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=700b94de3d44819c680e1dd6ec7dea4fafafbeeb;p=sarabande.git EMIT and various util cleanup --- diff --git a/src/compile/emit.c b/src/compile/emit.c index d2eef01..b73152b 100644 --- a/src/compile/emit.c +++ b/src/compile/emit.c @@ -2,10 +2,7 @@ #define STRING1(...) #__VA_ARGS__ #define STRING2(...) STRING1(__VA_ARGS__) -#define EMIT(...) do { \ - if (cm->debugmode) debug(STRING2(__VA_ARGS__) "\n"); \ - sbVmCompiler_write_code(cm, (u8[]) { __VA_ARGS__ }, sizeof((u8[]) { __VA_ARGS__ })); \ -} while (0) +#define EMIT(x) (emit(cm, x)) #define EARG(x) (emit_arg(cm, x)) struct labelpos { @@ -17,6 +14,7 @@ void compile_chunk(sbVmCompiler *cm, sbIrChunk *chunk); void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt); void compile_expr(sbVmCompiler *cm, sbIrExpr *expr); void compile_ref(sbVmCompiler *cm, sbIrExpr *expr, flag is_lref); +void compile_maybe_ref(sbVmCompiler *cm, sbIrExpr *expr, flag is_lref, sbOpcode op, sbOpcode op_ind); void compile_op(sbVmCompiler *cm, sbAstOp op); void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir, flag debugmode) { @@ -37,6 +35,11 @@ void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir, flag debugmode) { /* --- */ +void emit(sbVmCompiler *cm, sbOpcode opcode) { + if (cm->debugmode) debug("%s\n", g_opcode_names[opcode]); + sbVmCompiler_write_code(cm, (u8[]) { opcode }, 1); +} + void emit_arg(sbVmCompiler *cm, i64 actual_number) { if (cm->debugmode) debug(" arg %lld\n", (long long)actual_number); @@ -172,7 +175,10 @@ void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) { record_labelpos(cm, stmt->jump.label, sbVmCompiler_get_position(cm)); /* leave behind four zeroes at this offset that we can later put the * address into */ - EMIT(0, 0, 0, 0); + EARG(0); + EARG(0); + EARG(0); + EARG(0); } break; case IR_S_LABEL: @@ -220,18 +226,8 @@ void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) { * The reason for this has to do with assigning through nested data structures. * Doing it this way just makes the whole semantics feel more natural. It's similar * to returning a reference from indexing in C++, I guess. */ - if (is_implicit_ref(stmt->assign.where->op.left)) { - compile_ref(cm, stmt->assign.where->op.left, TRUE); - } else { - compile_expr(cm, stmt->assign.where->op.left); - } compile_expr(cm, stmt->assign.where->op.right); /* index we are using */ - if (is_implicit_ref(stmt->assign.where->op.left)) { - EMIT(BC_OP_INDEXLREF_IND); - } else { - /* THE PROLIFERATION OF THESE!!!!!!!!!! I DO NOT LIKE!!!! */ - EMIT(BC_OP_INDEXLREF); - } + compile_maybe_ref(cm, stmt->assign.where->op.left, TRUE, BC_OP_INDEXLREF, BC_OP_INDEXLREF_IND); EMIT(BC_REF_PUT); } else if (stmt->assign.where->type == IR_E_DOT) { /* same as op::index, essentially. the dot returns a reference to the inside of the @@ -239,13 +235,7 @@ void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) { compile_expr(cm, stmt->assign.where->dot.param); EMIT(BC_LD_IMM); EARG(1); - if (is_implicit_ref(stmt->assign.where->dot.target)) { - compile_ref(cm, stmt->assign.where->dot.target, TRUE); - EMIT(BC_DOT_IND); - } else { - compile_expr(cm, stmt->assign.where->dot.target); - EMIT(BC_DOT); - } + compile_maybe_ref(cm, stmt->assign.where->dot.target, TRUE, BC_DOT, BC_DOT_IND); EMIT(BC_REF_PUT); } else { PANIC("This type of assignment operation is not supported!"); @@ -302,7 +292,8 @@ void compile_list(sbVmCompiler *cm, sbIrExpr *expr) { * by one */ compile_expr(cm, elem); /* swap to put the number on top, then increment */ - EMIT(BC_SWAP, BC_OP_INCR); + EMIT(BC_SWAP); + EMIT(BC_OP_INCR); } else { /* blissfully unaware of the splat */ compile_expr(cm, elem); @@ -389,11 +380,6 @@ void compile_bind_list(sbVmCompiler *cm, sbIrBindList *list) { } void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) { - if (cm->debugmode) { - sbIr_print_expr(expr); - debug("\n"); - } - switch(expr->type) { case IR_E_OP: if (expr->op.type == AST_OP_REF) { @@ -411,20 +397,7 @@ void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) { case IR_E_CALL: /* calling convention: store argument count on stack */ compile_list(cm, expr->call.param); - if (is_implicit_ref(expr->call.func)) { - /* for expressions with reference type, call 'through' the reference */ - compile_ref(cm, expr->call.func, TRUE); - EMIT(BC_CALL_IND); - } else if (expr->call.func->type == IR_E_OP && expr->call.func->op.type == AST_OP_DEREF) { - /* for something like (*a)(...), call 'through' the reference as well - * (that is, evaluate the thing inside the * and call indirectly, instead - * of dereferencing it first) */ - compile_expr(cm, expr->call.func->op.left); - EMIT(BC_CALL_IND); - } else { - compile_expr(cm, expr->call.func); - EMIT(BC_CALL); - } + compile_maybe_ref(cm, expr->call.func, TRUE, BC_CALL, BC_CALL_IND); break; case IR_E_DOT: compile_expr(cm, expr->dot.param); @@ -521,46 +494,37 @@ void compile_ref(sbVmCompiler *cm, sbIrExpr *expr, flag is_lref) { } EARG(expr->var->slot_id); } else if (expr->type == IR_E_OP && expr->op.type == AST_OP_INDEX) { - /* TODO: Oh my god, it's 90F with no air conditioning, but can we PLEASE figure out - * a better way to structure this??!!?!?!?! */ - if (is_implicit_ref(expr->op.left)) { - compile_ref(cm, expr->op.left, is_lref); /* thing we are indexing into */ - } else { - compile_expr(cm, expr->op.left); /* thing we are indexing into */ - } compile_expr(cm, expr->op.right); /* index we are using */ if (is_lref) { - /* a[0] = ? / a::b = ? */ - if (is_implicit_ref(expr->op.left)) { - /* THIS SUCKS!!! */ - EMIT(BC_OP_INDEXLREF_IND); - } else { - EMIT(BC_OP_INDEXLREF); - } + compile_maybe_ref(cm, expr->op.left, TRUE, BC_OP_INDEXLREF, BC_OP_INDEXLREF_IND); } else { - /* &a[0] / &a::b */ - if (is_implicit_ref(expr->op.left)) { - EMIT(BC_OP_INDEXRREF_IND); - } else { - EMIT(BC_OP_INDEXRREF); - } + compile_maybe_ref(cm, expr->op.left, FALSE, BC_OP_INDEXRREF, BC_OP_INDEXRREF_IND); } } else if (expr->type == IR_E_DOT) { compile_expr(cm, expr->dot.param); EMIT(BC_LD_IMM); EARG(1); - if (is_implicit_ref(expr->dot.target)) { - compile_ref(cm, expr->dot.target, is_lref); - EMIT(BC_DOT_IND); - } else { - compile_expr(cm, expr->dot.target); - EMIT(BC_DOT); - } + compile_maybe_ref(cm, expr->dot.target, is_lref, BC_DOT, BC_DOT_IND); } else { PANIC("This type of reference operation is not supported!"); } } +void compile_maybe_ref(sbVmCompiler *cm, sbIrExpr *expr, flag is_lref, sbOpcode op, sbOpcode op_ind) { + if (is_implicit_ref(expr->dot.target)) { + compile_ref(cm, expr, is_lref); + EMIT(op_ind); + } else if (expr->type == IR_E_OP && expr->op.type == AST_OP_DEREF) { + /* explicit *: do thing indirectly 'through' the reference, + * instead of dereferencing and making a copy */ + compile_expr(cm, expr->op.left); + EMIT(op_ind); + } else { + compile_expr(cm, expr); + EMIT(op); + } +} + void compile_op(sbVmCompiler *cm, sbAstOp op) { switch (op) { case AST_OP_ADD: EMIT(BC_OP_ADD); break; @@ -569,11 +533,11 @@ void compile_op(sbVmCompiler *cm, sbAstOp op) { case AST_OP_FLDIV: EMIT(BC_OP_FLDIV); break; case AST_OP_MOD: EMIT(BC_OP_MOD); 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_NE: EMIT(BC_OP_EQ); EMIT(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_GT: EMIT(BC_OP_LE); EMIT(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; + case AST_OP_GE: EMIT(BC_OP_LT); EMIT(BC_OP_NOT); break; case AST_OP_NOT: EMIT(BC_OP_NOT); break; case AST_OP_AND: EMIT(BC_OP_AND); break; case AST_OP_OR: EMIT(BC_OP_OR); break; @@ -582,7 +546,7 @@ void compile_op(sbVmCompiler *cm, sbAstOp op) { case AST_OP_DEREF: EMIT(BC_OP_DEREF); break; /* op range is currently only used in rangeindex; just pass them to it directly */ case AST_OP_RANGE: break; - case AST_OP_DIVBY: EMIT(BC_OP_MOD, BC_LD_IMM); EARG(0); EMIT(BC_OP_EQ); break; + case AST_OP_DIVBY: EMIT(BC_OP_MOD); EMIT(BC_LD_IMM); EARG(0); EMIT(BC_OP_EQ); break; default: PANIC("unknown operation! (%lld / %c)", (long long)op, op); } diff --git a/src/global.h b/src/global.h index b4adf4b..46731f5 100644 --- a/src/global.h +++ b/src/global.h @@ -2,11 +2,25 @@ #define __SB_GLOBAL_H__ #ifdef DEBUG -#define PANIC(...) do { fprintf(stderr, "PANIC: " __VA_ARGS__); fprintf(stderr, "\n at " __FILE__ ":%d\n", __LINE__); abort(); } while (0) -#define CHECK(...) do { fprintf(stderr, "BUGCHECK: " __VA_ARGS__); fprintf(stderr, "\n at " __FILE__ ":%d\n", __LINE__); abort(); } while (0) +#define PANIC(...) do { \ + fflush(stdout); \ + fprintf(stderr, "PANIC: " __VA_ARGS__); \ + fprintf(stderr, "\n at " __FILE__ ":%d\n", __LINE__); \ + abort(); \ +} while (0) +#define CHECK(...) do { \ + fflush(stdout); \ + fprintf(stderr, "BUGCHECK: " __VA_ARGS__); \ + fprintf(stderr, "\n at " __FILE__ ":%d\n", __LINE__); \ + abort(); \ +} while (0) #define debug(...) printf(__VA_ARGS__) #else -#define PANIC(...) do { fprintf(stderr, "PANIC: " __VA_ARGS__); fprintf(stderr, "\n"); abort(); } while (0) +#define PANIC(...) do { \ + fprintf(stderr, "PANIC: " __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + abort(); \ +} while (0) #define CHECK(...) ((void)0) #define debug(...) ((void)0) #endif @@ -15,6 +29,7 @@ #include #include #include +#include typedef uint64_t u64; typedef int64_t i64; @@ -24,7 +39,7 @@ typedef uint16_t u16; typedef int16_t i16; typedef uint8_t u8; typedef int8_t i8; -typedef uint8_t flag; +typedef bool flag; typedef size_t usize; typedef ptrdiff_t isize; diff --git a/src/parse/token.h b/src/parse/token.h index 7a63b4b..c56eb70 100644 --- a/src/parse/token.h +++ b/src/parse/token.h @@ -91,9 +91,9 @@ typedef enum sbTokenType { } sbTokenType; typedef struct sbLexToken { - sbTokenType type; + sbTokenType type : 8; + i8 invisible; usize size; - flag invisible; i32 line; i32 start_col; i32 end_col; diff --git a/src/vm/bytecode.c b/src/vm/bytecode.c new file mode 100644 index 0000000..da63e67 --- /dev/null +++ b/src/vm/bytecode.c @@ -0,0 +1,7 @@ +#include "vm/bytecode.h" + +const char *g_opcode_names[] = { +#define X(A) #A, + BYTECODES +#undef X +}; diff --git a/src/vm/bytecode.h b/src/vm/bytecode.h index e96e17e..9fb4e14 100644 --- a/src/vm/bytecode.h +++ b/src/vm/bytecode.h @@ -3,70 +3,140 @@ #include "global.h" +#define BYTECODES \ + /* skip me */ \ + X(BC_NOP)\ + /* push immediate value to stack */ \ + X(BC_LD_IMM)\ + /* push constants[index] to stack */ \ + X(BC_LD_CONST)\ + /* look up key in context object and push result */ \ + X(BC_LD_CTX)\ + /* push value onto stack from variable */ \ + X(BC_LD_VAR)\ + /* push lref to variable onto stack */ \ + X(BC_LD_LREF)\ + /* push rref to variable onto stack */ \ + X(BC_LD_RREF)\ + /* push value onto stack from closure */ \ + X(BC_LD_UPVAL)\ + /* push pointer to closure value onto stack (to close over again) */ \ + X(BC_LD_UPREF)\ + /* push reference to function onto stack */ \ + X(BC_LD_BLK)\ + /* push nil onto stack */ \ + X(BC_LD_NIL)\ + /* push true onto stack */ \ + X(BC_LD_TRUE)\ + /* push false onto stack */ \ + X(BC_LD_FALSE)\ + /* pop value from stack into variable */ \ + X(BC_ST_VAR)\ + /* pop value from stack into closure */ \ + X(BC_ST_UPVAL)\ + /* decrement TOS and store NOS in variable */ \ + X(BC_ST_ARG)\ + /* pop value from stack */ \ + X(BC_POP)\ + /* pop N values from stack given by top number */ \ + X(BC_NPOP)\ + /* swap top two values on stack */ \ + X(BC_SWAP)\ + /* push return address and jump to new block */ \ + X(BC_CALL)\ + /* 'a->(...)' indirect call that can return a value */ \ + X(BC_CALL_IND)\ + /* 'a.b' call that should return a reference */ \ + X(BC_DOT)\ + /* 'a.b' indirect call that should return a reference */ \ + X(BC_DOT_IND)\ + /* return to calling block */ \ + X(BC_RET)\ + /* check number of function arguments = this */ \ + X(BC_NUMARG)\ + /* check number of function arguments > this */ \ + X(BC_MINARG)\ + /* unconditional local jump inside current block */ \ + X(BC_JMP)\ + /* jump if true */ \ + X(BC_JT)\ + /* jump if false */ \ + X(BC_JF)\ + /* like call)\ but jump to object method */ \ + X(BC_SEND)\ + /* assign through pointer */ \ + X(BC_REF_PUT)\ + /* == */ \ + X(BC_OP_EQ)\ + /* boolean not */ \ + X(BC_OP_NOT)\ + /* add */ \ + X(BC_OP_ADD)\ + /* subtract */ \ + X(BC_OP_SUB)\ + /* multiply */ \ + X(BC_OP_MUL)\ + /* true divide */ \ + X(BC_OP_DIV)\ + /* floor divide */ \ + X(BC_OP_FLDIV)\ + /* negative */ \ + X(BC_OP_NEG)\ + /* mod */ \ + X(BC_OP_MOD)\ + /* power */ \ + X(BC_OP_POW)\ + /* increment */ \ + X(BC_OP_INCR)\ + /* decrement */ \ + X(BC_OP_DECR)\ + /* dereference pointer */ \ + X(BC_OP_DEREF)\ + /* less than */ \ + X(BC_OP_LT)\ + /* less than or equal to */ \ + X(BC_OP_LE)\ + /* logical and */ \ + X(BC_OP_AND)\ + /* logical or */ \ + X(BC_OP_OR)\ + /* a[...] */ \ + X(BC_OP_INDEXVAL)\ + /* a[...] = ... */ \ + X(BC_OP_INDEXLREF)\ + /* a[...] = ... */ \ + X(BC_OP_INDEXLREF_IND)\ + /* &a[...] */ \ + X(BC_OP_INDEXRREF)\ + /* &a[...] */ \ + X(BC_OP_INDEXRREF_IND)\ + /* index [a..b] */ \ + X(BC_OP_RANGEINDEX)\ + /* create space in rstack for local variables */ \ + X(BC_ALLOC_VARS)\ + /* create closure from top elements of stack */ \ + X(BC_CLOSURE)\ + /* create list from count + list of values on stack */ \ + X(BC_LIST_GATHER)\ + /* create hash from count + list of pairs of keys/values on stack */ \ + X(BC_HASH_GATHER)\ + /* splat a list into a context with a count */ \ + X(BC_LIST_SPILL)\ + /* next two bytes are a 16-bit number */ \ + X(BC_LONG_NUM)\ + /* next four bytes are a 32-bit number */ \ + X(BC_VLONG_NUM)\ + /* next four bytes are a 32-bit number */ \ + X(BC_VVLONG_NUM)\ + /* terminate execution */ \ + X(BC_HALT) + typedef enum sbOpcode { - 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_LREF, // push lref to variable onto stack - BC_LD_RREF, // push rref to variable onto stack - BC_LD_UPVAL, // push value onto stack from closure - BC_LD_UPREF, // push pointer to closure value onto stack (to close over again) - 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 - BC_POP, // pop value from stack - BC_NPOP, // pop N values from stack given by top number - BC_SWAP, // swap top two values on stack - BC_CALL, // push return address and jump to new block - BC_CALL_IND, // 'a->(...)' indirect call that can return a value - BC_DOT, // 'a.b' call that should return a reference - BC_DOT_IND, // 'a.b' indirect call that should return a reference - 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_REF_PUT, // assign through pointer - BC_OP_EQ, // == - 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, // 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_OP_AND, // logical and - BC_OP_OR, // logical or - BC_OP_INDEXVAL, // a[...] - BC_OP_INDEXLREF, // a[...] = ... - BC_OP_INDEXLREF_IND, // a[...] = ... - BC_OP_INDEXRREF, // &a[...] - BC_OP_INDEXRREF_IND, // &a[...] - BC_OP_RANGEINDEX, // index [a..b] - BC_ALLOC_VARS, // create space in rstack for local variables - BC_CLOSURE, // create closure from top elements of stack - 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_LIST_SPILL, // splat a list into a context with a count - 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 +#define X(A) A, + BYTECODES +#undef X } sbOpcode; +extern const char *g_opcode_names[]; + #endif diff --git a/src/vm/exec.c b/src/vm/exec.c index 24d93d0..6b75f94 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -357,7 +357,9 @@ void execute_instruction(hVm vm) { } sbOpcode op = get_opcode(vm); - if (vm->debugmode) debug("op %02X ", op); +#ifdef DEBUG + if (vm->debugmode) debug("op %s ", g_opcode_names[op]); +#endif u64 param; usize count; hVal *v, *w, *x, res;