class SyntaxTree::YARV::ControlFlowGraph::Compiler

given instruction sequence.
This class is responsible for creating a control flow graph from the

def build_basic_blocks

block. They are keyed by the index of their first instruction.
Builds up a set of basic blocks by iterating over the starts of each
def build_basic_blocks
  block_starts = find_basic_block_starts
  length = 0
  blocks =
    iseq
      .insns
      .grep(Instruction)
      .slice_after do |insn|
        length += insn.length
        block_starts.include?(length)
      end
  block_starts
    .zip(blocks)
    .to_h do |block_start, insns|
      # It's possible that we have not detected a block start but still
      # have branching instructions inside of a basic block. This can
      # happen if you have an unconditional jump which is followed by
      # instructions that are unreachable. As of Ruby 3.2, this is
      # possible with something as simple as "1 => a". In this case we
      # can discard all instructions that follow branching instructions.
      block_insns =
        insns.slice_after { |insn| insn.branch_targets.any? }.first
      [block_start, BasicBlock.new(block_start, block_insns)]
    end
end

def compile

flow graph. It returns an instance of ControlFlowGraph.
This method is used to compile the instruction sequence into a control
def compile
  blocks = build_basic_blocks
  connect_basic_blocks(blocks)
  prune_basic_blocks(blocks)
  ControlFlowGraph.new(iseq, insns, blocks.values).tap(&:verify)
end

def connect_basic_blocks(blocks)

outgoing from each block.
Connect the blocks by letting them know which blocks are incoming and
def connect_basic_blocks(blocks)
  blocks.each do |block_start, block|
    insn = block.insns.last
    insn.branch_targets.each do |branch_target|
      block.outgoing_blocks << blocks.fetch(labels[branch_target])
    end
    if (insn.branch_targets.empty? && !insn.leaves?) ||
         insn.falls_through?
      fall_through_start = block_start + block.insns.sum(&:length)
      block.outgoing_blocks << blocks.fetch(fall_through_start)
    end
    block.outgoing_blocks.each do |outgoing_block|
      outgoing_block.incoming_blocks << block
    end
  end
end

def find_basic_block_starts


* fallen through to from a branch
* the target of a branch
* the start of an instruction sequence

they're either:
Finds the indices of the instructions that start a basic block because
def find_basic_block_starts
  block_starts = Set.new([0])
  insns.each do |index, insn|
    branch_targets = insn.branch_targets
    if branch_targets.any?
      branch_targets.each do |branch_target|
        block_starts.add(labels[branch_target])
      end
      block_starts.add(index + insn.length) if insn.falls_through?
    end
  end
  block_starts.to_a.sort
end

def initialize(iseq)

def initialize(iseq)
  @iseq = iseq
  @insns = {}
  @labels = {}
  length = 0
  iseq.insns.each do |insn|
    case insn
    when Instruction
      @insns[length] = insn
      length += insn.length
    when InstructionSequence::Label
      @labels[insn] = length
    end
  end
end

def prune_basic_blocks(blocks)

graph entirely at this point.
If there are blocks that are unreachable, we can remove them from the
def prune_basic_blocks(blocks)
  visited = Set.new
  queue = [blocks.fetch(0)]
  until queue.empty?
    current_block = queue.shift
    next if visited.include?(current_block)
    visited << current_block
    queue.concat(current_block.outgoing_blocks)
  end
  blocks.select! { |_, block| visited.include?(block) }
end