diff --git a/lib/ruby_vm/rjit/insn_compiler.rb b/lib/ruby_vm/rjit/insn_compiler.rb index 376902553e88b0..aff61afc65faac 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}") - # 77/102 + # 78/102 case insn.name when :nop then nop(jit, ctx, asm) when :getlocal then getlocal(jit, ctx, asm) @@ -66,7 +66,7 @@ def compile(jit, ctx, asm, insn) when :defined then defined(jit, ctx, asm) when :definedivar then definedivar(jit, ctx, asm) # checkmatch - # checkkeyword + when :checkkeyword then checkkeyword(jit, ctx, asm) # checktype # defineclass # definemethod @@ -1154,7 +1154,43 @@ def definedivar(jit, ctx, asm) end # checkmatch - # checkkeyword + + def checkkeyword(jit, ctx, asm) + # When a keyword is unspecified past index 32, a hash will be used + # instead. This can only happen in iseqs taking more than 32 keywords. + if jit.iseq.body.param.keyword.num >= 32 + return CantCompile + end + + # The EP offset to the undefined bits local + bits_offset = jit.operand(0) + + # The index of the keyword we want to check + index = jit.operand(1, signed: true) + + # Load environment pointer EP + ep_reg = :rax + jit_get_ep(asm, 0, reg: ep_reg) + + # VALUE kw_bits = *(ep - bits) + bits_opnd = [ep_reg, C.VALUE.size * -bits_offset] + + # unsigned int b = (unsigned int)FIX2ULONG(kw_bits); + # if ((b & (0x01 << idx))) { + # + # We can skip the FIX2ULONG conversion by shifting the bit we test + bit_test = 0x01 << (index + 1) + asm.test(bits_opnd, bit_test) + asm.mov(:rax, Qfalse) + asm.mov(:rcx, Qtrue) + asm.cmovz(:rax, :rcx) + + stack_ret = ctx.stack_push + asm.mov(stack_ret, :rax) + + KeepCompiling + end + # checktype # defineclass # definemethod diff --git a/rjit_c.rb b/rjit_c.rb index 677e294ddb9bb9..32731ba4d0db06 100644 --- a/rjit_c.rb +++ b/rjit_c.rb @@ -1001,6 +1001,18 @@ def C.rb_iseq_location_t ) end + def C.rb_iseq_param_keyword + @rb_iseq_param_keyword ||= CType::Struct.new( + "rb_iseq_param_keyword", Primitive.cexpr!("SIZEOF(struct rb_iseq_param_keyword)"), + num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_param_keyword *)NULL)), num)")], + required_num: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_param_keyword *)NULL)), required_num)")], + bits_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_param_keyword *)NULL)), bits_start)")], + rest_start: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_param_keyword *)NULL)), rest_start)")], + table: [CType::Pointer.new { self.ID }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_param_keyword *)NULL)), table)")], + default_values: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_param_keyword *)NULL)), default_values)")], + ) + end + def C.rb_iseq_struct @rb_iseq_struct ||= CType::Struct.new( "rb_iseq_struct", Primitive.cexpr!("SIZEOF(struct rb_iseq_struct)"), @@ -1354,10 +1366,6 @@ def C.rb_iseq_type CType::Stub.new(:rb_iseq_type) end - def C.rb_iseq_param_keyword - CType::Stub.new(:rb_iseq_param_keyword) - end - def C.iseq_insn_info CType::Stub.new(:iseq_insn_info) end diff --git a/tool/rjit/bindgen.rb b/tool/rjit/bindgen.rb index bcc4ee954a7d90..ad2e5f9e1e3252 100755 --- a/tool/rjit/bindgen.rb +++ b/tool/rjit/bindgen.rb @@ -156,8 +156,8 @@ def generate(nodes) println end - # TODO: Support nested declarations - nodes_index = nodes.group_by(&:spelling).transform_values do |values| + # Build a hash table for type lookup by name + nodes_index = flatten_nodes(nodes).group_by(&:spelling).transform_values do |values| # Try to search a declaration with definitions node_with_children = values.find { |v| !v.children.empty? } next node_with_children if node_with_children @@ -169,7 +169,7 @@ def generate(nodes) # Define types @types.each do |type| unless definition = generate_node(nodes_index[type]) - raise "Failed to generate type: #{type}" + raise "Failed to find or generate type: #{type}" end println " def C.#{type}" println "@#{type} ||= #{definition}".gsub(/^/, " ").chomp @@ -201,6 +201,18 @@ def generate(nodes) private + # Make an array that includes all top-level and nested nodes + def flatten_nodes(nodes) + result = [] + nodes.each do |node| + unless node.children.empty? + result.concat(flatten_nodes(node.children)) + end + end + result.concat(nodes) # prioritize top-level nodes + result + end + # Return code before BINDGEN_BEG and code after BINDGEN_END def split_ambles(src_path) lines = File.read(src_path).lines @@ -573,6 +585,7 @@ def push_target(target) rb_shape_t rb_thread_struct rb_jit_func_t + rb_iseq_param_keyword rjit_options ], # #ifdef-dependent immediate types, which need Primitive.cexpr! for type detection