inline ExceptionContinuation unwind(STATE, CallFrame* call_frame) { VMThreadState* th = state->vm()->thread_state(); switch(th->raise_reason()) { case cException: if(UnwindSite* unwind_site = call_frame->unwind) { if(unwind_site->unwind_type() == UnwindSite::eRescue) { return cExceptionRescue; } else if(unwind_site->unwind_type() == UnwindSite::eEnsure) { return cExceptionEnsure; } } call_frame->scope->flush_to_heap(state); break; case cBreak: // If we're trying to break to here, we're done! if(th->destination_scope() == call_frame->scope->on_heap()) { stack_push(th->raise_value()); th->clear_break(); // Don't return here, because we want to continue running this method. return cExceptionBreak; } // Fall through. case cReturn: case cCatchThrow: case cThreadKill: // Nonlocal return, run ensure blocks. while(UnwindSite* unwind_site = call_frame->unwind) { if(unwind_site->unwind_type() == UnwindSite::eEnsure) { return cExceptionEnsure; } else { call_frame->pop_unwind(); } } // Ok, no ensures to run. if(th->raise_reason() == cReturn) { call_frame->scope->flush_to_heap(state); // If we're trying to return to here, we're done! if(th->destination_scope() == call_frame->scope->on_heap()) { stack_push(th->raise_value()); th->clear_return(); return cExceptionReturn; } } else { // Not for us! call_frame->scope->flush_to_heap(state); } break; case cExit: call_frame->scope->flush_to_heap(state); break; case cFiberCancel: break; case cNone: Exception::interpreter_error(state, "no exception available for unwind handler"); break; } // switch return cExceptionUnwind; }
Object* MachineCode::debugger_interpreter_continue(STATE, MachineCode* const mcode, CallFrame* const call_frame, int sp, InterpreterState& is, UnwindInfoSet& thread_unwinds) { #include "vm/gen/instruction_locations.hpp" GCTokenImpl gct; opcode* stream = mcode->opcodes; Object** stack_ptr = call_frame->stk + sp; UnwindInfoSet unwinds(thread_unwinds); continue_to_run: try { #undef DISPATCH #define DISPATCH \ if(Object* bp = call_frame->find_breakpoint(state)) { \ if(!Helpers::yield_debugger(state, gct, call_frame, bp)) goto exception; \ } \ goto *insn_locations[stream[call_frame->inc_ip()]]; #undef next_int #undef cache_ip #undef flush_ip #define next_int ((opcode)(stream[call_frame->inc_ip()])) #define cache_ip(which) #define flush_ip() #include "vm/gen/instruction_implementations.hpp" } catch(TypeError& e) { flush_ip(); Exception* exc = Exception::make_type_error(state, e.type, e.object, e.reason); exc->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc); call_frame->scope->flush_to_heap(state); return NULL; } catch(const RubyException& exc) { exc.exception->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc.exception); return NULL; } // No reason to be here! rubinius::bug("Control flow error in interpreter"); exception: VMThreadState* th = state->vm()->thread_state(); // switch(th->raise_reason()) { case cException: if(unwinds.has_unwinds()) { UnwindInfo info = unwinds.pop(); stack_position(info.stack_depth); call_frame->set_ip(info.target_ip); cache_ip(info.target_ip); goto continue_to_run; } else { call_frame->scope->flush_to_heap(state); return NULL; } case cBreak: // If we're trying to break to here, we're done! if(th->destination_scope() == call_frame->scope->on_heap()) { stack_push(th->raise_value()); th->clear_break(); goto continue_to_run; // Don't return here, because we want to loop back to the top // and keep running this method. } // Otherwise, fall through and run the unwinds case cReturn: case cCatchThrow: case cThreadKill: // Otherwise, we're doing a long return/break unwind through // here. We need to run ensure blocks. while(unwinds.has_unwinds()) { UnwindInfo info = unwinds.pop(); if(info.for_ensure()) { stack_position(info.stack_depth); call_frame->set_ip(info.target_ip); cache_ip(info.target_ip); // Don't reset ep here, we're still handling the return/break. goto continue_to_run; } } // Ok, no ensures to run. if(th->raise_reason() == cReturn) { call_frame->scope->flush_to_heap(state); // If we're trying to return to here, we're done! if(th->destination_scope() == call_frame->scope->on_heap()) { Object* val = th->raise_value(); th->clear_return(); return val; } else { // Give control of this exception to the caller. return NULL; } } else { // It's cBreak thats not for us! call_frame->scope->flush_to_heap(state); // Give control of this exception to the caller. return NULL; } case cExit: call_frame->scope->flush_to_heap(state); return NULL; default: break; } // switch rubinius::bug("Control flow error in interpreter"); return NULL; }
Object* MachineCode::interpreter(STATE, MachineCode* const mcode, InterpreterCallFrame* const call_frame) { #include "vm/gen/instruction_locations.hpp" if(unlikely(state == 0)) { MachineCode::instructions = const_cast<void**>(insn_locations); return NULL; } InterpreterState is; GCTokenImpl gct; register void** ip_ptr = mcode->addresses; Object** stack_ptr = call_frame->stk - 1; UnwindInfoSet unwinds; continue_to_run: try { #undef DISPATCH #define DISPATCH goto **ip_ptr++ #undef next_int #define next_int ((opcode)(*ip_ptr++)) #define cache_ip(which) ip_ptr = mcode->addresses + which #define flush_ip() call_frame->calculate_ip(ip_ptr) #include "vm/gen/instruction_implementations.hpp" } catch(TypeError& e) { flush_ip(); Exception* exc = Exception::make_type_error(state, e.type, e.object, e.reason); exc->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc); call_frame->scope->flush_to_heap(state); return NULL; } catch(const RubyException& exc) { exc.exception->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc.exception); return NULL; } // There is no reason to be here. Either the bytecode loop exits, // or it jumps to exception; rubinius::bug("Control flow error in interpreter"); // If control finds it's way down here, there is an exception. exception: VMThreadState* th = state->vm()->thread_state(); // switch(th->raise_reason()) { case cException: if(unwinds.has_unwinds()) { UnwindInfo info = unwinds.pop(); stack_position(info.stack_depth); call_frame->set_ip(info.target_ip); cache_ip(info.target_ip); goto continue_to_run; } else { call_frame->scope->flush_to_heap(state); return NULL; } case cBreak: // If we're trying to break to here, we're done! if(th->destination_scope() == call_frame->scope->on_heap()) { stack_push(th->raise_value()); th->clear_break(); goto continue_to_run; // Don't return here, because we want to loop back to the top // and keep running this method. } // Otherwise, fall through and run the unwinds case cReturn: case cCatchThrow: case cThreadKill: // Otherwise, we're doing a long return/break unwind through // here. We need to run ensure blocks. while(unwinds.has_unwinds()) { UnwindInfo info = unwinds.pop(); if(info.for_ensure()) { stack_position(info.stack_depth); call_frame->set_ip(info.target_ip); cache_ip(info.target_ip); // Don't reset ep here, we're still handling the return/break. goto continue_to_run; } } // Ok, no ensures to run. if(th->raise_reason() == cReturn) { call_frame->scope->flush_to_heap(state); // If we're trying to return to here, we're done! if(th->destination_scope() == call_frame->scope->on_heap()) { Object* val = th->raise_value(); th->clear_return(); return val; } else { // Give control of this exception to the caller. return NULL; } } else { // Not for us! call_frame->scope->flush_to_heap(state); // Give control of this exception to the caller. return NULL; } case cExit: call_frame->scope->flush_to_heap(state); return NULL; default: break; } // switch rubinius::bug("Control flow error in interpreter"); return NULL; }
Object* MachineCode::uncommon_interpreter(STATE, MachineCode* const mcode, CallFrame* const call_frame, int32_t entry_ip, native_int sp, CallFrame* const method_call_frame, jit::RuntimeDataHolder* rd, UnwindInfoSet& thread_unwinds) { MachineCode* mc = method_call_frame->compiled_code->machine_code(); if(++mc->uncommon_count > state->shared().config.jit_deoptimize_threshold) { if(state->shared().config.jit_uncommon_print) { std::cerr << "[[[ Deoptimizing uncommon method ]]]\n"; call_frame->print_backtrace(state); std::cerr << "Method Call Frame:\n"; method_call_frame->print_backtrace(state); } mc->uncommon_count = 0; mc->deoptimize(state, method_call_frame->compiled_code, rd); } #include "vm/gen/instruction_locations.hpp" opcode* stream = mcode->opcodes; InterpreterState is; GCTokenImpl gct; Object** stack_ptr = call_frame->stk + sp; UnwindInfoSet unwinds(thread_unwinds); continue_to_run: try { #undef DISPATCH #define DISPATCH goto *insn_locations[stream[call_frame->inc_ip()]]; #undef next_int #undef cache_ip #undef flush_ip #define next_int ((opcode)(stream[call_frame->inc_ip()])) #define cache_ip(which) #define flush_ip() #include "vm/gen/instruction_implementations.hpp" } catch(TypeError& e) { flush_ip(); Exception* exc = Exception::make_type_error(state, e.type, e.object, e.reason); exc->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc); call_frame->scope->flush_to_heap(state); return NULL; } catch(const RubyException& exc) { exc.exception->locations(state, Location::from_call_stack(state, call_frame)); state->raise_exception(exc.exception); return NULL; } // No reason to be here! rubinius::bug("Control flow error in interpreter"); exception: VMThreadState* th = state->vm()->thread_state(); // switch(th->raise_reason()) { case cException: if(unwinds.has_unwinds()) { UnwindInfo info = unwinds.pop(); stack_position(info.stack_depth); call_frame->set_ip(info.target_ip); cache_ip(info.target_ip); goto continue_to_run; } else { call_frame->scope->flush_to_heap(state); return NULL; } case cBreak: // If we're trying to break to here, we're done! if(th->destination_scope() == call_frame->scope->on_heap()) { stack_push(th->raise_value()); th->clear_break(); goto continue_to_run; // Don't return here, because we want to loop back to the top // and keep running this method. } // Otherwise, fall through and run the unwinds case cReturn: case cCatchThrow: case cThreadKill: // Otherwise, we're doing a long return/break unwind through // here. We need to run ensure blocks. while(unwinds.has_unwinds()) { UnwindInfo info = unwinds.pop(); if(info.for_ensure()) { stack_position(info.stack_depth); call_frame->set_ip(info.target_ip); cache_ip(info.target_ip); // Don't reset ep here, we're still handling the return/break. goto continue_to_run; } } // Ok, no ensures to run. if(th->raise_reason() == cReturn) { call_frame->scope->flush_to_heap(state); // If we're trying to return to here, we're done! if(th->destination_scope() == call_frame->scope->on_heap()) { Object* val = th->raise_value(); th->clear_return(); return val; } else { // Give control of this exception to the caller. return NULL; } } else { // It's cBreak thats not for us! call_frame->scope->flush_to_heap(state); // Give control of this exception to the caller. return NULL; } case cExit: call_frame->scope->flush_to_heap(state); return NULL; default: break; } // switch rubinius::bug("Control flow error in interpreter"); return NULL; }