summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ruby_vm/rjit/insn_compiler.rb42
-rw-r--r--rjit_c.rb16
-rwxr-xr-xtool/rjit/bindgen.rb19
3 files changed, 67 insertions, 10 deletions
diff --git a/lib/ruby_vm/rjit/insn_compiler.rb b/lib/ruby_vm/rjit/insn_compiler.rb
index 376902553e..aff61afc65 100644
--- a/lib/ruby_vm/rjit/insn_compiler.rb
+++ b/lib/ruby_vm/rjit/insn_compiler.rb
@@ -18,7 +18,7 @@ module RubyVM::RJIT
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 @@ module RubyVM::RJIT
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 @@ module RubyVM::RJIT
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 677e294ddb..32731ba4d0 100644
--- a/rjit_c.rb
+++ b/rjit_c.rb
@@ -1001,6 +1001,18 @@ module RubyVM::RJIT # :nodoc: all
)
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 @@ module RubyVM::RJIT # :nodoc: all
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 bcc4ee954a..ad2e5f9e1e 100755
--- a/tool/rjit/bindgen.rb
+++ b/tool/rjit/bindgen.rb
@@ -156,8 +156,8 @@ class BindingGenerator
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 @@ class BindingGenerator
# 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 @@ class BindingGenerator
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 @@ generator = BindingGenerator.new(
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