class SyntaxTree::YARV::SeaOfNodes

A sea of nodes is an intermediate representation used by a compiler to represent both control and data flow in the same graph. The way we use it allows us to have the vertices of the graph represent either an instruction in the instruction sequence or a synthesized node that we add to the graph. The edges of the graph represent either control flow or data flow.

Attributes

dfg[R]
local_graphs[R]
nodes[R]

Public Class Methods

compile(dfg) click to toggle source
# File lib/syntax_tree/yarv/sea_of_nodes.rb, line 529
def self.compile(dfg)
  Compiler.new(dfg).compile
end
new(dfg, nodes, local_graphs) click to toggle source
# File lib/syntax_tree/yarv/sea_of_nodes.rb, line 462
def initialize(dfg, nodes, local_graphs)
  @dfg = dfg
  @nodes = nodes
  @local_graphs = local_graphs
end

Public Instance Methods

to_mermaid() click to toggle source
# File lib/syntax_tree/yarv/sea_of_nodes.rb, line 468
def to_mermaid
  Mermaid.flowchart do |flowchart|
    nodes.each do |node|
      flowchart.node("node_#{node.id}", node.label, shape: :rounded)
    end

    nodes.each do |producer|
      producer.outputs.each do |consumer_edge|
        label =
          if !consumer_edge.label
            # No label.
          elsif consumer_edge.to.is_a?(PhiNode)
            # Edges into phi nodes are labelled by the offset of the
            # instruction going into the merge.
            "%04d" % consumer_edge.label
          else
            consumer_edge.label.to_s
          end

        flowchart.link(
          flowchart.fetch("node_#{producer.id}"),
          flowchart.fetch("node_#{consumer_edge.to.id}"),
          label,
          type: consumer_edge.type == :info ? :dotted : :directed,
          color: { data: :green, control: :red }[consumer_edge.type]
        )
      end
    end
  end
end
verify() click to toggle source
# File lib/syntax_tree/yarv/sea_of_nodes.rb, line 499
def verify
  # Verify edge labels.
  nodes.each do |node|
    # Not talking about phi nodes right now.
    next if node.is_a?(PhiNode)

    if node.is_a?(InsnNode) && node.insn.branch_targets.any? &&
         !node.insn.is_a?(Leave)
      # A branching node must have at least one branch edge and
      # potentially a fallthrough edge coming out.

      labels = node.outputs.map(&:label).sort
      raise if labels[0] != :branch0
      raise if labels[1] != :fallthrough && labels.size > 2
    else
      labels = node.inputs.filter { |e| e.type == :data }.map(&:label)
      next if labels.empty?

      # No nil labels
      raise if labels.any?(&:nil?)

      # Labels should start at zero.
      raise unless labels.min.zero?

      # Labels should be contiguous.
      raise unless labels.sort == (labels.min..labels.max).to_a
    end
  end
end