static void vm_save_frame(vm_context_t *ctx, uint8_t *pc) { vm_callframe_t *frame; frame = vm_alloc(sizeof(*frame)); frame->return_pc = pc; frame->locals = ctx->locals; frame->dstack_top = vm_stack_top(ctx->dstack); vm_stack_push(ctx->cstack, (vm_operand_t)frame); ctx->locals = NULL; }
int thread_start(struct object *o) { struct thread *t; struct frame *frame; struct vm_stack *vm_stack; struct operand_stack *operand_stack; t = object_get_thread(o); vm_stack = t->vm_stack; if (t == NULL) { fprintf(stderr, "mvm: thread not initialized!\n"); mvm_halt(); } if (t->state != new_state) { fprintf(stderr, "mvm: thread cannot be started more than once!\n"); mvm_halt(); } t->ref = object_get_ref(o); #ifdef DMP if (t->dmp != NULL) thread_dmp_thread_creation(t->dmp); #endif vm_stack_push(vm_stack, "thread_start", 0, 0, 0, 0, 0); frame = vm_stack_peek(vm_stack); operand_stack = frame_get_operand_stack(frame); operand_stack_push(operand_stack, t->ref); if (thread_pthread_create(t, thread_run0) != 0) mvm_halt(); return 0; }
/* Emulate one VM insn. */ void vm_step(vm_context_t *ctx) { #include "vm_opcodes.switch.tab" vm_operand_t r0, r1; vm_operand_t buf[3]; uint8_t *pc = ctx->pc; unsigned ops_in, ops_out = 0; uint8_t opcode; opcode = *pc; DBGPRINT("vm_step: %08x -> %02x ", pc, opcode); pc += 1; ops_in = opcode >> 6; /* 2 highest bits make for operand count. */ opcode &= 0x3F; DBGPRINT("(%s / %d)\n", vm_insn_to_name[opcode], ops_in); vm_stack_pop3(ctx->dstack, buf, ops_in); goto *(&&op_invalid + offtab[opcode]); op_invalid: vm_panic("vm_step: unknown opcode."); op_ADD: r0 = ARG2 + ARG1; goto push_1; op_SUB: r0 = ARG2 - ARG1; goto push_1; op_MULU: { TARGET_MULU(ARG1, ARG2, r0, r1); goto push_2; } op_MULS: { TARGET_MULS(ARG1, ARG2, r0, r1); goto push_2; } op_DIVU: { TARGET_DIVU(ARG2, ARG1); goto push_2; } op_DIVS: { TARGET_DIVS(ARG2, ARG1); goto push_2; } op_AND: r0 = ARG1 & ARG2; goto push_1; op_OR: r0 = ARG1 | ARG2; goto push_1; op_XOR: r0 = ARG1 ^ ARG2; goto push_1; op_NOT: r0 = ~ARG1; goto push_1; op_LSL: r0 = ARG2 << ARG1; goto push_1; op_LSR: r0 = ((vm_uoperand_t)ARG2) >> ARG1; goto push_1; op_ASR: r0 = ((vm_soperand_t)ARG2) >> ARG1; goto push_1; op_CMP_LT: r0 = ((vm_soperand_t)ARG1) < ((vm_soperand_t)ARG2); goto push_1; op_CMP_GT: r0 = ((vm_soperand_t)ARG1) > ((vm_soperand_t)ARG2); goto push_1; op_CMP_B: r0 = ((vm_uoperand_t)ARG1) < ((vm_uoperand_t)ARG2); goto push_1; op_CMP_A: r0 = ((vm_uoperand_t)ARG1) > ((vm_uoperand_t)ARG2); goto push_1; op_CMP_EQ: r0 = ARG2 == ARG1; goto push_1; op_LDC_0: r0 = 0; goto push_1; op_LDC_1: r0 = 1; goto push_1; op_LDC_2: r0 = 2; goto push_1; op_LDC_UB: r0 = (vm_uoperand_t)*(uint8_t *)(pc); pc += 1; goto push_1; op_LDC_SB: r0 = (vm_soperand_t)*(int8_t *)(pc); pc += 1; goto push_1; op_LDC_W: r0 = vm_fetch32_ua(pc); pc += 4; goto push_1; op_LEA: r0 = vm_fetch32_ua(pc); DBGPRINT("vm_step: PCREL offset %08x -> ", r0); pc += 4; r0 += (vm_uoperand_t)pc; DBGPRINT("%08x\n", r0); goto push_1; op_LDM_UB: r0 = (vm_uoperand_t)*(uint8_t *)(ARG1); goto push_1; op_LDM_SB: r0 = (vm_soperand_t)*(int8_t *)(ARG1); goto push_1; op_LDM_UH: r0 = (vm_uoperand_t)*(uint16_t *)(ARG1); goto push_1; op_LDM_SH: r0 = (vm_soperand_t)*(int16_t *)(ARG1); goto push_1; op_LDM_W: r0 = *(vm_operand_t *)(ARG1); goto push_1; op_STM_B: *(uint8_t *)(ARG1) = (uint8_t)ARG2; DBGPRINT("vm_step: %08x -> %08x\n", ARG2, ARG1); goto push_none; op_STM_H: *(uint16_t *)(ARG1) = (uint16_t)ARG2; DBGPRINT("vm_step: %08x -> %08x\n", ARG2, ARG1); goto push_none; op_STM_W: *(vm_operand_t *)(ARG1) = ARG2; DBGPRINT("vm_step: %08x -> %08x\n", ARG2, ARG1); goto push_none; op_LOCALS: { uint8_t count = *(uint8_t *)(pc); pc += 1; ctx->locals = (vm_locals_t *)vm_alloc(sizeof(vm_locals_t) + (count - 1) * sizeof(vm_operand_t)); ctx->locals->count = count; goto push_none; } op_LDLOC: { uint8_t index = *(uint8_t *)(pc); pc += 1; if (!ctx->locals) { vm_panic("vm_step: accessing locals where none have been allocated."); } if (index >= ctx->locals->count) { vm_panic("vm_step: local index out of bounds."); } r0 = ctx->locals->data[index]; goto push_1; } op_STLOC: { uint8_t index = *(uint8_t *)(pc); pc += 1; if (!ctx->locals) { vm_panic("vm_step: accessing locals where none have been allocated."); } if (index >= ctx->locals->count) { vm_panic("vm_step: local index out of bounds."); } ctx->locals->data[index] = ARG1; goto push_none; } op_DUP: r1 = r0 = ARG1; goto push_2; op_SWAP: { r1 = ARG1; r0 = ARG2; goto push_2; } op_POP: goto push_none; op_BR: { int8_t offset = *(int8_t *)(pc); pc += 1; pc = pc + offset; goto push_none; } op_BR_T: { int8_t offset = *(int8_t *)(pc); pc += 1; if (ARG1) { pc = pc + offset; } goto push_none; } op_BR_F: { int8_t offset = *(int8_t *)(pc); pc += 1; if (!ARG1) { pc = pc + offset; } goto push_none; } op_CALL: { int32_t offset = (int32_t)vm_fetch32_ua(pc); pc += 4; vm_save_frame(ctx, pc); pc += offset; DBGPRINT("\n"); goto push_none; } op_RET: { vm_callframe_t *frame; vm_free(ctx->locals); frame = (vm_callframe_t *)vm_stack_pop(ctx->cstack); ctx->locals = frame->locals; pc = frame->return_pc; DBGPRINT("vm_step: stack balance on return: %d\n\n", frame->dstack_top - vm_stack_top(ctx->dstack)); vm_free(frame); goto push_none; } op_ICALL: { vm_save_frame(ctx, pc); pc = (uint8_t *)ARG1; goto push_none; } op_IJMP: { pc = (uint8_t *)ARG1; goto push_none; } op_NCALL: { int index = vm_fetch16_ua(pc); /* thunk index */ pc += 2; if (!ctx->module->ncalls_table) { vm_panic("vm_step: no ncalls defined, but a ncall insn encountered."); } vm_thunk_t thunk = (vm_thunk_t)ctx->module->ncalls_table[index]; DBGPRINT("vm_step: NCALL: calling %d @%08X\n", index, thunk); thunk(ctx); goto push_none; } push_2: vm_stack_push(ctx->dstack, r1); push_1: vm_stack_push(ctx->dstack, r0); push_none: ctx->pc = pc; return; }