From 2d8ecfcc9a1cf65104bd0f61542a969dc3bd2317 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 12 Mar 2023 22:27:43 -0700 Subject: RJIT: Implement invokeblock with ISeq --- lib/ruby_vm/rjit/insn_compiler.rb | 72 ++++++++++++++++++++++++++++++++++----- rjit_c.h | 3 +- rjit_c.rb | 3 +- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/lib/ruby_vm/rjit/insn_compiler.rb b/lib/ruby_vm/rjit/insn_compiler.rb index af9fe6226b..d8af57d844 100644 --- a/lib/ruby_vm/rjit/insn_compiler.rb +++ b/lib/ruby_vm/rjit/insn_compiler.rb @@ -1484,8 +1484,8 @@ module RubyVM::RJIT # Get call info cd = C.rb_call_data.new(jit.operand(0)) ci = cd.ci - _argc = C.vm_ci_argc(ci) - _flags = C.vm_ci_flag(ci) + argc = C.vm_ci_argc(ci) + flags = C.vm_ci_flag(ci) # Get block_handler cfp = jit.cfp @@ -1497,8 +1497,49 @@ module RubyVM::RJIT asm.incr_counter(:invokeblock_none) CantCompile elsif comptime_handler & 0x3 == 0x1 # VM_BH_ISEQ_BLOCK_P - asm.incr_counter(:invokeblock_iseq) - CantCompile + asm.comment('get local EP') + ep_reg = :rax + jit_get_lep(jit, asm, reg: ep_reg) + asm.mov(:rax, [ep_reg, C.VALUE.size * C::VM_ENV_DATA_INDEX_SPECVAL]) # block_handler_opnd + + asm.comment('guard block_handler type') + side_exit = side_exit(jit, ctx) + asm.mov(:rcx, :rax) + asm.and(:rcx, 0x3) # block_handler is a tagged pointer + asm.cmp(:rcx, 0x1) # VM_BH_ISEQ_BLOCK_P + tag_changed_exit = counted_exit(side_exit, :invokeblock_tag_changed) + jit_chain_guard(:jne, jit, ctx, asm, tag_changed_exit) + + comptime_captured = C.rb_captured_block.new(comptime_handler & ~0x3) + comptime_iseq = comptime_captured.code.iseq + + asm.comment('guard known ISEQ') + asm.and(:rax, ~0x3) # captured + asm.mov(:rax, [:rax, C.VALUE.size * 2]) # captured->iseq + asm.mov(:rcx, comptime_iseq.to_i) + asm.cmp(:rax, :rcx) + block_changed_exit = counted_exit(side_exit, :invokeblock_iseq_block_changed) + jit_chain_guard(:jne, jit, ctx, asm, block_changed_exit) + + jit_call_iseq_setup( + jit, ctx, asm, nil, flags, argc, comptime_iseq, :captured, + send_shift: 0, frame_type: C::VM_FRAME_MAGIC_BLOCK, + ) + #gen_send_iseq( + # jit, + # ctx, + # asm, + # ocb, + # comptime_iseq, + # ci, + # VM_FRAME_MAGIC_BLOCK, + # None, + # 0, + # None, + # flags, + # argc, + # Some(captured_opnd), + #) elsif comptime_handler & 0x3 == 0x3 # VM_BH_IFUNC_P asm.incr_counter(:invokeblock_ifunc) CantCompile @@ -3990,8 +4031,9 @@ module RubyVM::RJIT # Save caller SP and PC before pushing a callee frame for backtrace and side exits asm.comment('save SP to caller CFP') recv_idx = argc + (flags & C::VM_CALL_ARGS_BLOCKARG != 0 ? 1 : 0) # blockarg is not popped yet + recv_idx += (block_handler == :captured) ? 0 : 1 # receiver is not on stack when captured->self is used # Skip setting this to SP register. This cfp->sp will be copied to SP on leave insn. - asm.lea(:rax, ctx.sp_opnd(C.VALUE.size * -(1 + recv_idx))) # Pop receiver and arguments to prepare for side exits + asm.lea(:rax, ctx.sp_opnd(C.VALUE.size * -recv_idx)) # Pop receiver and arguments to prepare for side exits asm.mov([CFP, C.rb_control_frame_t.offsetof(:sp)], :rax) jit_save_pc(jit, asm, comment: 'save PC to caller CFP') @@ -4455,6 +4497,15 @@ module RubyVM::RJIT if prev_ep asm.mov(:rax, prev_ep.to_i | 1) # tagged prev ep asm.mov([SP, C.VALUE.size * (ep_offset - 1)], :rax) + elsif block_handler == :captured + # Set captured->ep, saving captured in :rcx for captured->self + ep_reg = :rcx + jit_get_lep(jit, asm, reg: ep_reg) + asm.mov(:rcx, [ep_reg, C.VALUE.size * C::VM_ENV_DATA_INDEX_SPECVAL]) # block_handler + asm.and(:rcx, ~0x3) # captured + asm.mov(:rax, [:rcx, C.VALUE.size]) # captured->ep + asm.or(:rax, 0x1) # GC_GUARDED_PTR + asm.mov([SP, C.VALUE.size * (ep_offset - 1)], :rax) elsif block_handler == C::VM_BLOCK_HANDLER_NONE asm.mov([SP, C.VALUE.size * (ep_offset - 1)], C::VM_BLOCK_HANDLER_NONE) elsif block_handler == C.rb_block_param_proxy @@ -4481,8 +4532,12 @@ module RubyVM::RJIT end asm.mov(:rax, iseq.to_i) asm.mov([CFP, cfp_offset + C.rb_control_frame_t.offsetof(:iseq)], :rax) - self_index = ctx.sp_offset - (1 + argc) # blockarg has been popped - asm.mov(:rax, [SP, C.VALUE.size * self_index]) + if block_handler == :captured + asm.mov(:rax, [:rcx]) # captured->self + else + self_index = ctx.sp_offset - (1 + argc) # blockarg has been popped + asm.mov(:rax, [SP, C.VALUE.size * self_index]) + end asm.mov([CFP, cfp_offset + C.rb_control_frame_t.offsetof(:self)], :rax) asm.lea(:rax, [SP, C.VALUE.size * ep_offset]) asm.mov([CFP, cfp_offset + C.rb_control_frame_t.offsetof(:ep)], :rax) @@ -4497,7 +4552,8 @@ module RubyVM::RJIT if iseq # Stub cfp->jit_return return_ctx = ctx.dup - return_ctx.stack_size -= argc # Pop args. blockarg has been popped + return_ctx.stack_size -= argc + ((block_handler == :captured) ? 0 : 1) # Pop args and receiver. blockarg has been popped + return_ctx.stack_size += 1 # push callee's return value return_ctx.sp_offset = 1 # SP is in the position after popping a receiver and arguments return_ctx.chain_depth = 0 branch_stub = BranchStub.new( diff --git a/rjit_c.h b/rjit_c.h index 0f3195b2c6..53f7c8e7a5 100644 --- a/rjit_c.h +++ b/rjit_c.h @@ -84,10 +84,11 @@ RJIT_RUNTIME_COUNTERS( invokesuper_same_me, invokeblock_none, - invokeblock_iseq, invokeblock_ifunc, invokeblock_symbol, invokeblock_proc, + invokeblock_tag_changed, + invokeblock_iseq_block_changed, getivar_megamorphic, getivar_not_heap, diff --git a/rjit_c.rb b/rjit_c.rb index a9522b7454..9840aee245 100644 --- a/rjit_c.rb +++ b/rjit_c.rb @@ -1316,10 +1316,11 @@ module RubyVM::RJIT # :nodoc: all invokesuper_me_changed: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokesuper_me_changed)")], invokesuper_same_me: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokesuper_same_me)")], invokeblock_none: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokeblock_none)")], - invokeblock_iseq: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokeblock_iseq)")], invokeblock_ifunc: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokeblock_ifunc)")], invokeblock_symbol: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokeblock_symbol)")], invokeblock_proc: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokeblock_proc)")], + invokeblock_tag_changed: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokeblock_tag_changed)")], + invokeblock_iseq_block_changed: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), invokeblock_iseq_block_changed)")], getivar_megamorphic: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), getivar_megamorphic)")], getivar_not_heap: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), getivar_not_heap)")], getivar_special_const: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), getivar_special_const)")], -- cgit v1.2.1