#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 {
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) {
/* --- */
+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);
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:
* 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
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!");
* 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);
}
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) {
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);
}
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 *<whatever>: 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;
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;
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);
}
#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