RJIT: Implement checkkeyword

This commit is contained in:
Takashi Kokubun 2023-03-18 21:14:35 -07:00
parent 67dd52d59c
commit 9f8e914943
3 changed files with 67 additions and 10 deletions

View File

@ -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

View File

@ -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

View File

@ -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