Skip to content
Prev Previous commit
Next Next commit
Specialize using the linked list
  • Loading branch information
kddnewton committed Nov 26, 2022
commit 80de9c9d4e1ddfc73fab479df69d77ce7367de69
29 changes: 2 additions & 27 deletions lib/syntax_tree/yarv/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -608,33 +608,6 @@ def visit_call(node)
)
end

arg_parts = argument_parts(node.arguments)
argc = arg_parts.length

# First we're going to check if we're calling a method on an array
# literal without any arguments. In that case there are some
# specializations we might be able to perform.
if argc == 0 && (node.message.is_a?(Ident) || node.message.is_a?(Op))
case node.receiver
when ArrayLiteral
parts = node.receiver.contents&.parts || []

if parts.none? { |part| part.is_a?(ArgStar) } &&
RubyVisitor.compile(node.receiver).nil?
case node.message.value
when "max"
visit(node.receiver.contents)
iseq.opt_newarray_max(parts.length)
return
when "min"
visit(node.receiver.contents)
iseq.opt_newarray_min(parts.length)
return
end
end
end
end

# Track whether or not this is a method call on a block proxy receiver.
# If it is, we can potentially do tailcall optimizations on it.
block_receiver = false
Expand Down Expand Up @@ -663,6 +636,8 @@ def visit_call(node)
iseq.branchnil(after_call_label)
end

arg_parts = argument_parts(node.arguments)
argc = arg_parts.length
flag = 0

arg_parts.each do |arg_part|
Expand Down
115 changes: 65 additions & 50 deletions lib/syntax_tree/yarv/instruction_sequence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,16 @@ def inline_storage_for(name)
end

def length
insns.each.inject(0) do |sum, insn|
case insn
when Integer, Label, Symbol
sum
else
sum + insn.length
insns
.each
.inject(0) do |sum, insn|
case insn
when Integer, Label, Symbol
sum
else
sum + insn.length
end
end
end
end

def eval
Expand All @@ -218,29 +220,32 @@ def to_a
specialize_instructions! if options.specialized_instruction?

# Next, set it up so that all of the labels get their correct name.
insns.each.inject(0) do |length, insn|
case insn
when Integer, Symbol
length
when Label
insn.patch!(:"label_#{length}")
length
else
length + insn.length
insns
.each
.inject(0) do |length, insn|
case insn
when Integer, Symbol
length
when Label
insn.patch!(:"label_#{length}")
length
else
length + insn.length
end
end
end

# Next, dump all of the instructions into a flat list.
dumped = insns.each.map do |insn|
case insn
when Integer, Symbol
insn
when Label
insn.name
else
insn.to_a(self)
dumped =
insns.each.map do |insn|
case insn
when Integer, Symbol
insn
when Label
insn.name
else
insn.to_a(self)
end
end
end

dumped_options = argument_options.dup
dumped_options[:opt].map!(&:name) if dumped_options[:opt]
Expand Down Expand Up @@ -271,29 +276,55 @@ def to_a
def specialize_instructions!
insns.each_node do |node|
case node.instruction
when NewArray
next unless node.next_node

next_node = node.next_node
next unless next_node.instruction.is_a?(Send)
next if next_node.instruction.block_iseq

calldata = next_node.instruction.calldata
next unless calldata.flags == CallData::CALL_ARGS_SIMPLE
next unless calldata.argc == 0

case calldata.method
when :max
node.instruction = OptNewArrayMax.new(node.instruction.number)
node.next_node = next_node.next_node
when :min
node.instruction = OptNewArrayMin.new(node.instruction.number)
node.next_node = next_node.next_node
end
when PutObject, PutString
next unless node.next_node
next if node.instruction.is_a?(PutObject) && !node.instruction.object.is_a?(String)
if node.instruction.is_a?(PutObject) &&
!node.instruction.object.is_a?(String)
next
end

next_node = node.next_node
next unless next_node.instruction.is_a?(Send)
next if next_node.instruction.block_iseq

calldata = next_node.instruction.calldata
next unless calldata.flags == CallData::CALL_ARGS_SIMPLE
next unless calldata.argc == 0

case calldata.method
when :freeze
node.instruction = OptStrFreeze.new(node.instruction.object, calldata)
node.instruction =
OptStrFreeze.new(node.instruction.object, calldata)
node.next_node = next_node.next_node
when :-@
node.instruction = OptStrUMinus.new(node.instruction.object, calldata)
node.instruction =
OptStrUMinus.new(node.instruction.object, calldata)
node.next_node = next_node.next_node
end
when Send
calldata = node.instruction.calldata

if !node.instruction.block_iseq && !calldata.flag?(CallData::CALL_ARGS_BLOCKARG)
if !node.instruction.block_iseq &&
!calldata.flag?(CallData::CALL_ARGS_BLOCKARG)
# Specialize the send instruction. If it doesn't have a block
# attached, then we will replace it with an opt_send_without_block
# and do further specializations based on the called method and
Expand Down Expand Up @@ -639,24 +670,6 @@ def opt_getinlinecache(label, cache)
push(Legacy::OptGetInlineCache.new(label, cache))
end

def opt_newarray_max(length)
if options.specialized_instruction?
push(OptNewArrayMax.new(length))
else
newarray(length)
send(YARV.calldata(:max))
end
end

def opt_newarray_min(length)
if options.specialized_instruction?
push(OptNewArrayMin.new(length))
else
newarray(length)
send(YARV.calldata(:min))
end
end

def opt_setinlinecache(cache)
push(Legacy::OptSetInlineCache.new(cache))
end
Expand Down Expand Up @@ -938,9 +951,11 @@ def self.from(source, options = Compiler::Options.new, parent_iseq = nil)
when :opt_getinlinecache
iseq.opt_getinlinecache(labels[opnds[0]], opnds[1])
when :opt_newarray_max
iseq.opt_newarray_max(opnds[0])
iseq.newarray(opnds[0])
iseq.send(YARV.calldata(:max))
when :opt_newarray_min
iseq.opt_newarray_min(opnds[0])
iseq.newarray(opnds[0])
iseq.send(YARV.calldata(:min))
when :opt_neq
iseq.push(
OptNEq.new(CallData.from(opnds[0]), CallData.from(opnds[1]))
Expand Down