class SyntaxTree::YARV::SeaOfNodes::Compiler
def create_local_graph(block)
Create a sub-graph for a single basic block - block block argument
def create_local_graph(block) block_flow = dfg.block_flows.fetch(block.id) # A map of instructions to nodes. insn_nodes = {} # Create a node for each instruction in the block. block.each_with_length do |insn, offset| node = InsnNode.new(insn, offset) insn_nodes[offset] = node nodes << node end # The first and last node in the sub-graph, and the last fixed node. previous_fixed = nil first_fixed = nil last_fixed = nil # The merge node for the phi nodes to attach to. merge_node = nil # If there is more than one predecessor and we have basic block # arguments coming in, then we need a merge node for the phi nodes to # attach to. if block.incoming_blocks.size > 1 && !block_flow.in.empty? merge_node = MergeNode.new(id_counter) nodes << merge_node previous_fixed = merge_node first_fixed = merge_node last_fixed = merge_node end # Connect local control flow (only nodes with side effects.) block.each_with_length do |insn, length| if insn.side_effects? insn_node = insn_nodes[length] connect previous_fixed, insn_node, :control if previous_fixed previous_fixed = insn_node first_fixed ||= insn_node last_fixed = insn_node end end # Connect basic block arguments. inputs = {} outputs = {} block_flow.in.each do |arg| # Each basic block argument gets a phi node. Even if there's only # one predecessor! We'll tidy this up later. phi = PhiNode.new(id_counter) connect(phi, merge_node, :info) if merge_node nodes << phi inputs[arg] = phi block.each_with_length do |_, consumer_offset| consumer_flow = dfg.insn_flows[consumer_offset] consumer_flow.in.each_with_index do |producer, input_index| if producer == arg connect(phi, insn_nodes[consumer_offset], :data, input_index) end end end block_flow.out.each { |out| outputs[out] = phi if out == arg } end # Connect local dataflow from consumers back to producers. block.each_with_length do |_, consumer_offset| consumer_flow = dfg.insn_flows.fetch(consumer_offset) consumer_flow.in.each_with_index do |producer, input_index| if producer.local? connect( insn_nodes[producer.length], insn_nodes[consumer_offset], :data, input_index ) end end end # Connect dataflow from producers that leaves the block. block.each_with_length do |_, producer_pc| dfg .insn_flows .fetch(producer_pc) .out .each do |consumer| unless consumer.local? # This is an argument to the successor block - not to an # instruction here. outputs[consumer.name] = insn_nodes[producer_pc] end end end # A graph with only side-effect free instructions will currently have # no fixed nodes! In that case just use the first instruction's node # for both first and last. But it's a bug that it'll appear in the # control flow path! SubGraph.new( first_fixed || insn_nodes[block.block_start], last_fixed || insn_nodes[block.block_start], inputs, outputs ) end