diff options
author | Takashi Kokubun <takashikkbn@gmail.com> | 2023-03-04 22:42:03 -0800 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2023-03-05 23:28:59 -0800 |
commit | 35faa33b65b14d7f3b25b48cf0ad135e8ce33807 (patch) | |
tree | 5fd74026ede33abc532da406c0ace01e34682cd2 | |
parent | 26cb5d416f9f966fb7856e936d7081d7c091558f (diff) |
Implement bmethod
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/7448
-rw-r--r-- | lib/ruby_vm/mjit/insn_compiler.rb | 63 | ||||
-rw-r--r-- | mjit_c.h | 14 | ||||
-rw-r--r-- | mjit_c.rb | 90 | ||||
-rwxr-xr-x | tool/mjit/bindgen.rb | 11 |
4 files changed, 153 insertions, 25 deletions
diff --git a/lib/ruby_vm/mjit/insn_compiler.rb b/lib/ruby_vm/mjit/insn_compiler.rb index 0c53430dfe..ceafe65351 100644 --- a/lib/ruby_vm/mjit/insn_compiler.rb +++ b/lib/ruby_vm/mjit/insn_compiler.rb @@ -3131,7 +3131,8 @@ module RubyVM::MJIT def jit_call_method_each_type(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd, block_handler, known_recv_class, send_shift:) case cme.def.type when C.VM_METHOD_TYPE_ISEQ - jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc, block_handler, send_shift:) + iseq = def_iseq_ptr(cme.def) + jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc, iseq, block_handler, send_shift:) when C.VM_METHOD_TYPE_NOTIMPLEMENTED asm.incr_counter(:send_notimplemented) return CantCompile @@ -3146,8 +3147,7 @@ module RubyVM::MJIT asm.incr_counter(:send_missing) return CantCompile when C.VM_METHOD_TYPE_BMETHOD - asm.incr_counter(:send_bmethod) - return CantCompile + jit_call_bmethod(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd, block_handler, known_recv_class, send_shift:) when C.VM_METHOD_TYPE_ALIAS jit_call_alias(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd, block_handler, known_recv_class, send_shift:) when C.VM_METHOD_TYPE_OPTIMIZED @@ -3171,8 +3171,7 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] # @param asm [RubyVM::MJIT::Assembler] - def jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc, block_handler, send_shift:) - iseq = def_iseq_ptr(cme.def) + def jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc, iseq, block_handler, send_shift:, frame_type: nil, prev_ep: nil) opt_pc = jit_callee_setup_arg(jit, ctx, asm, flags, argc, iseq) if opt_pc == CantCompile return CantCompile @@ -3183,14 +3182,14 @@ module RubyVM::MJIT asm.incr_counter(:send_tailcall) return CantCompile end - jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq, block_handler, opt_pc, send_shift:) + jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq, block_handler, opt_pc, send_shift:, frame_type:, prev_ep:) end # vm_call_iseq_setup_normal (vm_call_iseq_setup_2 -> vm_call_iseq_setup_normal) # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] # @param asm [RubyVM::MJIT::Assembler] - def jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq, block_handler, opt_pc, send_shift:) + def jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq, block_handler, opt_pc, send_shift:, frame_type:, prev_ep:) # We will not have side exits from here. Adjust the stack. if flags & C.VM_CALL_OPT_SEND != 0 jit_call_opt_send_shift_stack(ctx, asm, argc, send_shift:) @@ -3204,12 +3203,13 @@ module RubyVM::MJIT asm.mov([CFP, C.rb_control_frame_t.offsetof(:sp)], :rax) jit_save_pc(jit, asm, comment: 'save PC to caller CFP') - frame_type = C.VM_FRAME_MAGIC_METHOD | C.VM_ENV_FLAG_LOCAL + frame_type ||= C.VM_FRAME_MAGIC_METHOD | C.VM_ENV_FLAG_LOCAL jit_push_frame( jit, ctx, asm, cme, flags, argc, frame_type, block_handler, iseq: iseq, local_size: iseq.body.local_table_size - iseq.body.param.size, stack_max: iseq.body.stack_max, + prev_ep:, ) # Jump to a stub for the callee ISEQ @@ -3361,7 +3361,47 @@ module RubyVM::MJIT jit_getivar(jit, ctx, asm, comptime_recv, ivar_id, recv_opnd) end + # vm_call_bmethod + # @param jit [RubyVM::MJIT::JITState] + # @param ctx [RubyVM::MJIT::Context] + # @param asm [RubyVM::MJIT::Assembler] + def jit_call_bmethod(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd, block_handler, known_recv_class, send_shift:) + proc_addr = cme.def.body.bmethod.proc + + proc_t = C.rb_yjit_get_proc_ptr(proc_addr) + proc_block = proc_t.block + + if proc_block.type != C.block_type_iseq + asm.incr_counter(:send_optimized_bmethod_not_iseq) + return CantCompile + end + + capture = proc_block.as.captured + iseq = capture.code.iseq + + # TODO: implement this + # Optimize for single ractor mode and avoid runtime check for + # "defined with an un-shareable Proc in a different Ractor" + # if !assume_single_ractor_mode(jit, ocb) + # return CantCompile; + # end + + # Passing a block to a block needs logic different from passing + # a block to a method and sometimes requires allocation. Bail for now. + if block_handler != C.VM_BLOCK_HANDLER_NONE + asm.incr_counter(:send_optimized_bmethod_blockarg) + return CantCompile + end + + frame_type = C.VM_FRAME_MAGIC_BLOCK | C.VM_FRAME_FLAG_BMETHOD | C.VM_FRAME_FLAG_LAMBDA + prev_ep = capture.ep + jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc, iseq, block_handler, send_shift:, frame_type:, prev_ep:) + end + # vm_call_alias + # @param jit [RubyVM::MJIT::JITState] + # @param ctx [RubyVM::MJIT::Context] + # @param asm [RubyVM::MJIT::Assembler] def jit_call_alias(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd, block_handler, known_recv_class, send_shift:) cme = C.rb_aliased_callable_method_entry(cme) jit_call_method_each_type(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd, block_handler, known_recv_class, send_shift:) @@ -3550,7 +3590,7 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] # @param asm [RubyVM::MJIT::Assembler] - def jit_push_frame(jit, ctx, asm, cme, flags, argc, frame_type, block_handler, iseq: nil, local_size: 0, stack_max: 0) + def jit_push_frame(jit, ctx, asm, cme, flags, argc, frame_type, block_handler, iseq: nil, local_size: 0, stack_max: 0, prev_ep: nil) # CHECK_VM_STACK_OVERFLOW0: next_cfp <= sp + (local_size + stack_max) asm.comment('stack overflow check') asm.lea(:rax, ctx.sp_opnd(C.rb_control_frame_t.size + C.VALUE.size * (local_size + stack_max))) @@ -3580,7 +3620,10 @@ module RubyVM::MJIT asm.mov(:rax, cme.to_i) asm.mov([SP, C.VALUE.size * (ep_offset - 2)], :rax) # ep[-1]: block handler or prev env ptr - if block_handler == C.VM_BLOCK_HANDLER_NONE + 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 == 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 # vm_caller_setup_arg_block: @@ -152,12 +152,6 @@ MJIT_RUNTIME_COUNTERS( send_ivar_opt_send, send_ivar_blockarg, - send_optimized_call, - send_optimized_block_call, - send_optimized_struct_aref, - send_optimized_struct_aset, - send_optimized_unknown_type, - send_optimized_send_no_args, send_optimized_send_not_sym_or_str, send_optimized_send_mid_class_changed, @@ -169,6 +163,14 @@ MJIT_RUNTIME_COUNTERS( send_optimized_call_splat, send_optimized_blockarg, + send_optimized_block_call, + send_optimized_struct_aref, + send_optimized_struct_aset, + send_optimized_unknown_type, + + send_optimized_bmethod_not_iseq, + send_optimized_bmethod_blockarg, + invokesuper_me_changed, invokesuper_same_me, @@ -412,6 +412,14 @@ module RubyVM::MJIT # :nodoc: all rb_callable_method_entry_t.new(cme_addr) end + def rb_yjit_get_proc_ptr(proc_addr) + proc_t_addr = Primitive.cstmt! %{ + extern rb_proc_t * rb_yjit_get_proc_ptr(VALUE procv); + return SIZET2NUM((size_t)rb_yjit_get_proc_ptr((VALUE)NUM2SIZET(proc_addr))); + } + rb_proc_t.new(proc_t_addr) + end + #======================================================================================== # # Old stuff @@ -781,6 +789,10 @@ module RubyVM::MJIT # :nodoc: all Primitive.cexpr! %q{ UINT2NUM(VM_ENV_FLAG_WB_REQUIRED) } end + def C.VM_FRAME_FLAG_BMETHOD + Primitive.cexpr! %q{ UINT2NUM(VM_FRAME_FLAG_BMETHOD) } + end + def C.VM_FRAME_FLAG_CFRAME Primitive.cexpr! %q{ UINT2NUM(VM_FRAME_FLAG_CFRAME) } end @@ -789,10 +801,18 @@ module RubyVM::MJIT # :nodoc: all Primitive.cexpr! %q{ UINT2NUM(VM_FRAME_FLAG_CFRAME_KW) } end + def C.VM_FRAME_FLAG_LAMBDA + Primitive.cexpr! %q{ UINT2NUM(VM_FRAME_FLAG_LAMBDA) } + end + def C.VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM Primitive.cexpr! %q{ UINT2NUM(VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM) } end + def C.VM_FRAME_MAGIC_BLOCK + Primitive.cexpr! %q{ UINT2NUM(VM_FRAME_MAGIC_BLOCK) } + end + def C.VM_FRAME_MAGIC_CFUNC Primitive.cexpr! %q{ UINT2NUM(VM_FRAME_MAGIC_CFUNC) } end @@ -849,6 +869,10 @@ module RubyVM::MJIT # :nodoc: all Primitive.cexpr! %q{ UINT2NUM(VM_METHOD_TYPE_ZSUPER) } end + def C.block_type_iseq + Primitive.cexpr! %q{ UINT2NUM(block_type_iseq) } + end + def C.imemo_iseq Primitive.cexpr! %q{ UINT2NUM(imemo_iseq) } end @@ -1118,6 +1142,23 @@ module RubyVM::MJIT # :nodoc: all ) end + def C.rb_block + @rb_block ||= CType::Struct.new( + "rb_block", Primitive.cexpr!("SIZEOF(struct rb_block)"), + as: [CType::Union.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_block *)NULL)->as)"), + captured: self.rb_captured_block, + symbol: self.VALUE, + proc: self.VALUE, + ), Primitive.cexpr!("OFFSETOF((*((struct rb_block *)NULL)), as)")], + type: [self.rb_block_type, Primitive.cexpr!("OFFSETOF((*((struct rb_block *)NULL)), type)")], + ) + end + + def C.rb_block_type + @rb_block_type ||= CType::Immediate.parse("int") + end + def C.rb_builtin_function @rb_builtin_function ||= CType::Struct.new( "rb_builtin_function", Primitive.cexpr!("SIZEOF(struct rb_builtin_function)"), @@ -1189,6 +1230,20 @@ module RubyVM::MJIT # :nodoc: all ) end + def C.rb_captured_block + @rb_captured_block ||= CType::Struct.new( + "rb_captured_block", Primitive.cexpr!("SIZEOF(struct rb_captured_block)"), + self: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_captured_block *)NULL)), self)")], + ep: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_captured_block *)NULL)), ep)")], + code: [CType::Union.new( + "", Primitive.cexpr!("SIZEOF(((struct rb_captured_block *)NULL)->code)"), + iseq: CType::Pointer.new { self.rb_iseq_t }, + ifunc: CType::Pointer.new { self.vm_ifunc }, + val: self.VALUE, + ), Primitive.cexpr!("OFFSETOF((*((struct rb_captured_block *)NULL)), code)")], + ) + end + def C.rb_control_frame_t @rb_control_frame_t ||= CType::Struct.new( "rb_control_frame_struct", Primitive.cexpr!("SIZEOF(struct rb_control_frame_struct)"), @@ -1366,6 +1421,15 @@ module RubyVM::MJIT # :nodoc: all ) end + def C.rb_method_bmethod_t + @rb_method_bmethod_t ||= CType::Struct.new( + "rb_method_bmethod_struct", Primitive.cexpr!("SIZEOF(struct rb_method_bmethod_struct)"), + proc: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_method_bmethod_struct *)NULL)), proc)")], + hooks: [CType::Pointer.new { self.rb_hook_list_struct }, Primitive.cexpr!("OFFSETOF((*((struct rb_method_bmethod_struct *)NULL)), hooks)")], + defined_ractor: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_method_bmethod_struct *)NULL)), defined_ractor)")], + ) + end + def C.rb_method_cfunc_t @rb_method_cfunc_t ||= CType::Struct.new( "rb_method_cfunc_struct", Primitive.cexpr!("SIZEOF(struct rb_method_cfunc_struct)"), @@ -1481,11 +1545,6 @@ module RubyVM::MJIT # :nodoc: all send_ivar_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_splat)")], send_ivar_opt_send: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_opt_send)")], send_ivar_blockarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_blockarg)")], - send_optimized_call: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_call)")], - send_optimized_block_call: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_block_call)")], - send_optimized_struct_aref: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_struct_aref)")], - send_optimized_struct_aset: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_struct_aset)")], - send_optimized_unknown_type: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_unknown_type)")], send_optimized_send_no_args: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send_no_args)")], send_optimized_send_not_sym_or_str: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send_not_sym_or_str)")], send_optimized_send_mid_class_changed: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send_mid_class_changed)")], @@ -1496,6 +1555,12 @@ module RubyVM::MJIT # :nodoc: all send_optimized_call_kwarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_call_kwarg)")], send_optimized_call_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_call_splat)")], send_optimized_blockarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_blockarg)")], + send_optimized_block_call: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_block_call)")], + send_optimized_struct_aref: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_struct_aref)")], + send_optimized_struct_aset: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_struct_aset)")], + send_optimized_unknown_type: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_unknown_type)")], + send_optimized_bmethod_not_iseq: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_bmethod_not_iseq)")], + send_optimized_bmethod_blockarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_bmethod_blockarg)")], invokesuper_me_changed: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), invokesuper_me_changed)")], invokesuper_same_me: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), invokesuper_same_me)")], getivar_megamorphic: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), getivar_megamorphic)")], @@ -1542,6 +1607,13 @@ module RubyVM::MJIT # :nodoc: all ) end + def C.rb_proc_t + @rb_proc_t ||= CType::Struct.new( + "", Primitive.cexpr!("SIZEOF(rb_proc_t)"), + block: [self.rb_block, Primitive.cexpr!("OFFSETOF((*((rb_proc_t *)NULL)), block)")], + ) + end + def C.rb_serial_t @rb_serial_t ||= CType::Immediate.parse("unsigned long long") end @@ -1642,6 +1714,10 @@ module RubyVM::MJIT # :nodoc: all CType::Stub.new(:rb_callinfo_kwarg) end + def C.vm_ifunc + CType::Stub.new(:vm_ifunc) + end + def C.rb_cref_struct CType::Stub.new(:rb_cref_struct) end @@ -1718,10 +1794,6 @@ module RubyVM::MJIT # :nodoc: all CType::Stub.new(:rb_method_refined_t) end - def C.rb_method_bmethod_t - CType::Stub.new(:rb_method_bmethod_t) - end - def C.ccan_list_node CType::Stub.new(:ccan_list_node) end diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index a06e16e139..3f39ddfa48 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -214,6 +214,7 @@ class BindingGenerator to_ruby = @ruby_fields.fetch(node.spelling, []).include?(field) if child.bitwidth > 0 if bit_fields_end <= i # give up offsetof calculation for non-leading bit fields + binding.irb raise "non-leading bit fields are not supported. consider including '#{field}' in skip_fields." end offsetof = node.offsetof.fetch(field) @@ -407,6 +408,9 @@ generator = BindingGenerator.new( VM_ENV_FLAG_WB_REQUIRED VM_FRAME_MAGIC_METHOD VM_FRAME_MAGIC_CFUNC + VM_FRAME_MAGIC_BLOCK + VM_FRAME_FLAG_BMETHOD + VM_FRAME_FLAG_LAMBDA VM_FRAME_FLAG_CFRAME VM_FRAME_FLAG_CFRAME_KW VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM @@ -423,6 +427,7 @@ generator = BindingGenerator.new( VM_METHOD_TYPE_ZSUPER VM_METHOD_TYPE_REFINED imemo_iseq + block_type_iseq ], ULONG: %w[ INVALID_SHAPE_ID @@ -490,6 +495,7 @@ generator = BindingGenerator.new( rb_method_definition_struct rb_method_iseq_t rb_method_type_t + rb_method_bmethod_t rb_mjit_compile_info rb_mjit_runtime_counters rb_mjit_unit @@ -501,6 +507,10 @@ generator = BindingGenerator.new( rb_method_optimized_t method_optimized_type rb_thread_struct + rb_proc_t + rb_block + rb_block_type + rb_captured_block ], dynamic_types: %w[ VALUE @@ -511,6 +521,7 @@ generator = BindingGenerator.new( rb_execution_context_struct: %w[method_missing_reason], # non-leading bit fields not supported rb_iseq_constant_body: %w[yjit_payload], # conditionally defined rb_thread_struct: %w[status locking_native_thread to_kill abort_on_exception report_on_exception pending_interrupt_queue_checked], + :'' => %w[is_from_method is_lambda is_isolated], # rb_proc_t }, ruby_fields: { rb_iseq_constant_body: %w[ |