diff --git a/lib/ruby_vm/rjit/insn_compiler.rb b/lib/ruby_vm/rjit/insn_compiler.rb index bcff9e452590a4..8422428f2b8a06 100644 --- a/lib/ruby_vm/rjit/insn_compiler.rb +++ b/lib/ruby_vm/rjit/insn_compiler.rb @@ -18,7 +18,7 @@ def compile(jit, ctx, asm, insn) asm.incr_counter(:rjit_insns_count) asm.comment("Insn: #{insn.name}") - # 74/102 + # 75/102 case insn.name when :nop then nop(jit, ctx, asm) when :getlocal then getlocal(jit, ctx, asm) @@ -82,7 +82,7 @@ def compile(jit, ctx, asm, insn) when :invokesuper then invokesuper(jit, ctx, asm) when :invokeblock then invokeblock(jit, ctx, asm) when :leave then leave(jit, ctx, asm) - # throw + when :throw then throw(jit, ctx, asm) when :jump then jump(jit, ctx, asm) when :branchif then branchif(jit, ctx, asm) when :branchunless then branchunless(jit, ctx, asm) @@ -1308,7 +1308,37 @@ def leave(jit, ctx, asm) EndBlock end - # throw + # @param jit [RubyVM::RJIT::JITState] + # @param ctx [RubyVM::RJIT::Context] + # @param asm [RubyVM::RJIT::Assembler] + def throw(jit, ctx, asm) + throw_state = jit.operand(0) + asm.mov(:rcx, ctx.stack_pop(1)) # throwobj + + # THROW_DATA_NEW allocates. Save SP for GC and PC for allocation tracing as + # well as handling the catch table. However, not using jit_prepare_routine_call + # since we don't need a patch point for this implementation. + jit_save_pc(jit, asm) # clobbers rax + jit_save_sp(ctx, asm) + + # rb_vm_throw verifies it's a valid throw, sets ec->tag->state, and returns throw + # data, which is throwobj or a vm_throw_data wrapping it. When ec->tag->state is + # set, JIT code callers will handle the throw with vm_exec_handle_exception. + asm.mov(C_ARGS[0], EC) + asm.mov(C_ARGS[1], CFP) + asm.mov(C_ARGS[2], throw_state) + # asm.mov(C_ARGS[3], :rcx) # same reg + asm.call(C.rb_vm_throw) + + asm.comment('exit from throw') + asm.pop(SP) + asm.pop(EC) + asm.pop(CFP) + + # return C_RET as C_RET + asm.ret + EndBlock + end # @param jit [RubyVM::RJIT::JITState] # @param ctx [RubyVM::RJIT::Context] @@ -2810,10 +2840,10 @@ def jit_equality_specialized(jit, ctx, asm, gen_eq) def jit_prepare_routine_call(jit, ctx, asm) jit.record_boundary_patch_point = true jit_save_pc(jit, asm) - jit_save_sp(jit, ctx, asm) + jit_save_sp(ctx, asm) end - # Note: This clobbers :rax + # NOTE: This clobbers :rax # @param jit [RubyVM::RJIT::JITState] # @param asm [RubyVM::RJIT::Assembler] def jit_save_pc(jit, asm, comment: 'save PC to CFP') @@ -2823,10 +2853,9 @@ def jit_save_pc(jit, asm, comment: 'save PC to CFP') asm.mov([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax) end - # @param jit [RubyVM::RJIT::JITState] # @param ctx [RubyVM::RJIT::Context] # @param asm [RubyVM::RJIT::Assembler] - def jit_save_sp(jit, ctx, asm) + def jit_save_sp(ctx, asm) if ctx.sp_offset != 0 asm.comment('save SP to CFP') asm.lea(SP, ctx.sp_opnd) diff --git a/rjit_c.c b/rjit_c.c index 793af25f6aa986..54dcb8569db463 100644 --- a/rjit_c.c +++ b/rjit_c.c @@ -494,6 +494,7 @@ extern bool rb_vm_defined(rb_execution_context_t *ec, rb_control_frame_t *reg_cf extern bool rb_vm_ic_hit_p(IC ic, const VALUE *reg_ep); extern rb_event_flag_t rb_rjit_global_events; extern void rb_vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IVC ic); +extern VALUE rb_vm_throw(const rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t throw_state, VALUE throwobj); #include "rjit_c.rbinc" diff --git a/rjit_c.rb b/rjit_c.rb index 2446fda175ef23..3e9550c27a0cbb 100644 --- a/rjit_c.rb +++ b/rjit_c.rb @@ -580,6 +580,10 @@ def C.rb_vm_splat_array Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_vm_splat_array) } end + def C.rb_vm_throw + Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_vm_throw) } + end + def C.rjit_full_cfunc_return Primitive.cexpr! %q{ SIZET2NUM((size_t)rjit_full_cfunc_return) } end diff --git a/tool/rjit/bindgen.rb b/tool/rjit/bindgen.rb index 292f7e4b9cffd6..d5922084691e3d 100755 --- a/tool/rjit/bindgen.rb +++ b/tool/rjit/bindgen.rb @@ -515,6 +515,7 @@ def push_target(target) rjit_str_neq_internal rjit_record_exit_stack rb_ivar_defined + rb_vm_throw ], types: %w[ CALL_DATA