// // Instructions // enum{ INS_END, INS_POP, // // Generated using code_generating_script.py // INS_ADD_S64, INS_SUB_S64, INS_DIV_S64, INS_MUL_S64, INS_MOD_S64, INS_PUSH_S64, INS_ADD_S32, INS_SUB_S32, INS_DIV_S32, INS_MUL_S32, INS_MOD_S32, INS_PUSH_S32, INS_ADD_S16, INS_SUB_S16, INS_DIV_S16, INS_MUL_S16, INS_MOD_S16, INS_PUSH_S16, INS_ADD_S8, INS_SUB_S8, INS_DIV_S8, INS_MUL_S8, INS_MOD_S8, INS_PUSH_S8, INS_ADD_U64, INS_SUB_U64, INS_DIV_U64, INS_MUL_U64, INS_MOD_U64, INS_PUSH_U64, INS_ADD_U32, INS_SUB_U32, INS_DIV_U32, INS_MUL_U32, INS_MOD_U32, INS_PUSH_U32, INS_ADD_U16, INS_SUB_U16, INS_DIV_U16, INS_MUL_U16, INS_MOD_U16, INS_PUSH_U16, INS_ADD_U8, INS_SUB_U8, INS_DIV_U8, INS_MUL_U8, INS_MOD_U8, INS_PUSH_U8, INS_ADD_F32, INS_SUB_F32, INS_DIV_F32, INS_MUL_F32, INS_MOD_F32, INS_PUSH_F32, INS_ADD_F64, INS_SUB_F64, INS_DIV_F64, INS_MUL_F64, INS_MOD_F64, INS_PUSH_F64, // // **End** of generated using code_generating_script.py // }; // // Bytecode interpreter context // struct Bc{ U8 *ins_pointer; U8 *stack_pointer; U8 *stack_bottom; Arena instructions; Arena stack; }; #define C(type, data) ((type *)data)[0] // Cast value to type and unpack the pointer so you can write to it #include "bytecode_interpreter_generated.cpp" function Bc create_bytecode_interp(){ Bc b = {}; { arena_init(&b.instructions, "Bytecode instructions"_s); b.instructions.alignment = 1; // Commit arena_push_size(&b.instructions, 16); arena_clear(&b.instructions); b.ins_pointer = b.instructions.memory.data; } { arena_init(&b.stack, "Bytecode stack"_s); b.stack.alignment = 8; // Setup a 4 kilobyte stack arena_push_size(&b.stack, kib(4)); b.stack_pointer = b.stack_bottom = b.stack.memory.data; } return b; } force_inline void emit_pop(Bc *bc){ U8 *instruction = (U8 *)arena_push_size(&bc->instructions, sizeof(U8)); *instruction = INS_POP; } force_inline void emit_end(Bc *bc){ U8 *instruction = (U8 *)arena_push_size(&bc->instructions, sizeof(U8)); *instruction = INS_END; } #define ins_pop_t(b, T) (*((T *)ins_pop(b))) static void * ins_pop(Bc *b){ assert_msg(b->stack_pointer != b->stack_bottom, "Reached bottom of bytecode interpreter stack"); b->stack_pointer -= sizeof(U64); // @warning we don't do anything with type for now Ast_Type_Kind *type = (Ast_Type_Kind *)b->stack_pointer; unused(type); b->stack_pointer -= sizeof(U64); return b->stack_pointer; } function void run_bytecode_interp(Bc *b){ for(;;){ U8 instruction = *b->ins_pointer++; switch(instruction){ case INS_POP:{ void *value = ins_pop(b); S64 *s64 = (S64 *)value; F64 *f64 = (F64 *)value; U64 *u64 = (U64 *)value; unused(s64); unused(f64); unused(u64); } break; case INS_END:{ goto interp_loop_breakout; } break; // // Generated using code_generating_script.py // case INS_ADD_S64:{ S64 l = ins_pop_t(b, S64); S64 r = ins_pop_t(b, S64); S64 result = l + r; ins_push_s64(b, result); }break; case INS_SUB_S64:{ S64 l = ins_pop_t(b, S64); S64 r = ins_pop_t(b, S64); S64 result = l - r; ins_push_s64(b, result); }break; case INS_DIV_S64:{ S64 l = ins_pop_t(b, S64); S64 r = ins_pop_t(b, S64); S64 result = l / r; ins_push_s64(b, result); }break; case INS_MUL_S64:{ S64 l = ins_pop_t(b, S64); S64 r = ins_pop_t(b, S64); S64 result = l * r; ins_push_s64(b, result); }break; case INS_MOD_S64:{ S64 l = ins_pop_t(b, S64); S64 r = ins_pop_t(b, S64); S64 result = l % r; ins_push_s64(b, result); }break; case INS_PUSH_S64:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (S64 *)b->ins_pointer; b->ins_pointer += sizeof(S64); ins_push_s64(b, *value); } break; case INS_ADD_S32:{ S32 l = ins_pop_t(b, S32); S32 r = ins_pop_t(b, S32); S32 result = l + r; ins_push_s32(b, result); }break; case INS_SUB_S32:{ S32 l = ins_pop_t(b, S32); S32 r = ins_pop_t(b, S32); S32 result = l - r; ins_push_s32(b, result); }break; case INS_DIV_S32:{ S32 l = ins_pop_t(b, S32); S32 r = ins_pop_t(b, S32); S32 result = l / r; ins_push_s32(b, result); }break; case INS_MUL_S32:{ S32 l = ins_pop_t(b, S32); S32 r = ins_pop_t(b, S32); S32 result = l * r; ins_push_s32(b, result); }break; case INS_MOD_S32:{ S32 l = ins_pop_t(b, S32); S32 r = ins_pop_t(b, S32); S32 result = l % r; ins_push_s32(b, result); }break; case INS_PUSH_S32:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (S32 *)b->ins_pointer; b->ins_pointer += sizeof(S32); ins_push_s32(b, *value); } break; case INS_ADD_S16:{ S16 l = ins_pop_t(b, S16); S16 r = ins_pop_t(b, S16); S16 result = l + r; ins_push_s16(b, result); }break; case INS_SUB_S16:{ S16 l = ins_pop_t(b, S16); S16 r = ins_pop_t(b, S16); S16 result = l - r; ins_push_s16(b, result); }break; case INS_DIV_S16:{ S16 l = ins_pop_t(b, S16); S16 r = ins_pop_t(b, S16); S16 result = l / r; ins_push_s16(b, result); }break; case INS_MUL_S16:{ S16 l = ins_pop_t(b, S16); S16 r = ins_pop_t(b, S16); S16 result = l * r; ins_push_s16(b, result); }break; case INS_MOD_S16:{ S16 l = ins_pop_t(b, S16); S16 r = ins_pop_t(b, S16); S16 result = l % r; ins_push_s16(b, result); }break; case INS_PUSH_S16:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (S16 *)b->ins_pointer; b->ins_pointer += sizeof(S16); ins_push_s16(b, *value); } break; case INS_ADD_S8:{ S8 l = ins_pop_t(b, S8); S8 r = ins_pop_t(b, S8); S8 result = l + r; ins_push_s8(b, result); }break; case INS_SUB_S8:{ S8 l = ins_pop_t(b, S8); S8 r = ins_pop_t(b, S8); S8 result = l - r; ins_push_s8(b, result); }break; case INS_DIV_S8:{ S8 l = ins_pop_t(b, S8); S8 r = ins_pop_t(b, S8); S8 result = l / r; ins_push_s8(b, result); }break; case INS_MUL_S8:{ S8 l = ins_pop_t(b, S8); S8 r = ins_pop_t(b, S8); S8 result = l * r; ins_push_s8(b, result); }break; case INS_MOD_S8:{ S8 l = ins_pop_t(b, S8); S8 r = ins_pop_t(b, S8); S8 result = l % r; ins_push_s8(b, result); }break; case INS_PUSH_S8:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (S8 *)b->ins_pointer; b->ins_pointer += sizeof(S8); ins_push_s8(b, *value); } break; case INS_ADD_U64:{ U64 l = ins_pop_t(b, U64); U64 r = ins_pop_t(b, U64); U64 result = l + r; ins_push_u64(b, result); }break; case INS_SUB_U64:{ U64 l = ins_pop_t(b, U64); U64 r = ins_pop_t(b, U64); U64 result = l - r; ins_push_u64(b, result); }break; case INS_DIV_U64:{ U64 l = ins_pop_t(b, U64); U64 r = ins_pop_t(b, U64); U64 result = l / r; ins_push_u64(b, result); }break; case INS_MUL_U64:{ U64 l = ins_pop_t(b, U64); U64 r = ins_pop_t(b, U64); U64 result = l * r; ins_push_u64(b, result); }break; case INS_MOD_U64:{ U64 l = ins_pop_t(b, U64); U64 r = ins_pop_t(b, U64); U64 result = l % r; ins_push_u64(b, result); }break; case INS_PUSH_U64:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (U64 *)b->ins_pointer; b->ins_pointer += sizeof(U64); ins_push_u64(b, *value); } break; case INS_ADD_U32:{ U32 l = ins_pop_t(b, U32); U32 r = ins_pop_t(b, U32); U32 result = l + r; ins_push_u32(b, result); }break; case INS_SUB_U32:{ U32 l = ins_pop_t(b, U32); U32 r = ins_pop_t(b, U32); U32 result = l - r; ins_push_u32(b, result); }break; case INS_DIV_U32:{ U32 l = ins_pop_t(b, U32); U32 r = ins_pop_t(b, U32); U32 result = l / r; ins_push_u32(b, result); }break; case INS_MUL_U32:{ U32 l = ins_pop_t(b, U32); U32 r = ins_pop_t(b, U32); U32 result = l * r; ins_push_u32(b, result); }break; case INS_MOD_U32:{ U32 l = ins_pop_t(b, U32); U32 r = ins_pop_t(b, U32); U32 result = l % r; ins_push_u32(b, result); }break; case INS_PUSH_U32:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (U32 *)b->ins_pointer; b->ins_pointer += sizeof(U32); ins_push_u32(b, *value); } break; case INS_ADD_U16:{ U16 l = ins_pop_t(b, U16); U16 r = ins_pop_t(b, U16); U16 result = l + r; ins_push_u16(b, result); }break; case INS_SUB_U16:{ U16 l = ins_pop_t(b, U16); U16 r = ins_pop_t(b, U16); U16 result = l - r; ins_push_u16(b, result); }break; case INS_DIV_U16:{ U16 l = ins_pop_t(b, U16); U16 r = ins_pop_t(b, U16); U16 result = l / r; ins_push_u16(b, result); }break; case INS_MUL_U16:{ U16 l = ins_pop_t(b, U16); U16 r = ins_pop_t(b, U16); U16 result = l * r; ins_push_u16(b, result); }break; case INS_MOD_U16:{ U16 l = ins_pop_t(b, U16); U16 r = ins_pop_t(b, U16); U16 result = l % r; ins_push_u16(b, result); }break; case INS_PUSH_U16:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (U16 *)b->ins_pointer; b->ins_pointer += sizeof(U16); ins_push_u16(b, *value); } break; case INS_ADD_U8:{ U8 l = ins_pop_t(b, U8); U8 r = ins_pop_t(b, U8); U8 result = l + r; ins_push_u8(b, result); }break; case INS_SUB_U8:{ U8 l = ins_pop_t(b, U8); U8 r = ins_pop_t(b, U8); U8 result = l - r; ins_push_u8(b, result); }break; case INS_DIV_U8:{ U8 l = ins_pop_t(b, U8); U8 r = ins_pop_t(b, U8); U8 result = l / r; ins_push_u8(b, result); }break; case INS_MUL_U8:{ U8 l = ins_pop_t(b, U8); U8 r = ins_pop_t(b, U8); U8 result = l * r; ins_push_u8(b, result); }break; case INS_MOD_U8:{ U8 l = ins_pop_t(b, U8); U8 r = ins_pop_t(b, U8); U8 result = l % r; ins_push_u8(b, result); }break; case INS_PUSH_U8:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (U8 *)b->ins_pointer; b->ins_pointer += sizeof(U8); ins_push_u8(b, *value); } break; case INS_ADD_F32:{ F32 l = ins_pop_t(b, F32); F32 r = ins_pop_t(b, F32); F32 result = l + r; ins_push_f32(b, result); }break; case INS_SUB_F32:{ F32 l = ins_pop_t(b, F32); F32 r = ins_pop_t(b, F32); F32 result = l - r; ins_push_f32(b, result); }break; case INS_DIV_F32:{ F32 l = ins_pop_t(b, F32); F32 r = ins_pop_t(b, F32); F32 result = l / r; ins_push_f32(b, result); }break; case INS_MUL_F32:{ F32 l = ins_pop_t(b, F32); F32 r = ins_pop_t(b, F32); F32 result = l * r; ins_push_f32(b, result); }break; case INS_PUSH_F32:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (F32 *)b->ins_pointer; b->ins_pointer += sizeof(F32); ins_push_f32(b, *value); } break; case INS_ADD_F64:{ F64 l = ins_pop_t(b, F64); F64 r = ins_pop_t(b, F64); F64 result = l + r; ins_push_f64(b, result); }break; case INS_SUB_F64:{ F64 l = ins_pop_t(b, F64); F64 r = ins_pop_t(b, F64); F64 result = l - r; ins_push_f64(b, result); }break; case INS_DIV_F64:{ F64 l = ins_pop_t(b, F64); F64 r = ins_pop_t(b, F64); F64 result = l / r; ins_push_f64(b, result); }break; case INS_MUL_F64:{ F64 l = ins_pop_t(b, F64); F64 r = ins_pop_t(b, F64); F64 result = l * r; ins_push_f64(b, result); }break; case INS_PUSH_F64:{ // Fetch value from instruction // instructions are tightly packed so we // move pointer by the type size auto value = (F64 *)b->ins_pointer; b->ins_pointer += sizeof(F64); ins_push_f64(b, *value); } break; // // **End** of generated using code_generating_script.py // default: invalid_codepath; } } interp_loop_breakout:; } function void test_interpreter(){ Bc b = create_bytecode_interp(); emit_push_s64(&b, 1); emit_push_s8(&b, 3); emit_add_s64(&b); emit_pop(&b); // emit_push_f64(&b, 2.42f); // emit_push_u16(&b, 4); // emit_pop(&b); // emit_pop(&b); // emit_pop(&b); // emit_pop(&b); emit_end(&b); run_bytecode_interp(&b); }