///|
fn operand_program_point(
  block : Int32,
  instruction : Int32,
  timing : OperandTiming,
) -> ProgramPoint {
  let phase = match timing {
    Early => 0
    Late => 1
  }
  ProgramPoint(block, instruction * 2 + phase)
}

///|
fn[F : FunctionView] collect_function_liveness(
  function : F,
  ranges : LiveRangeSet,
) -> Array[Array[Int32]] {
  let word_count = (function.value_count() + 63) / 64
  let block_count = function.block_count()
  let use_dense = block_count == 0 || word_count <= 262144 / block_count
  let block_defs : Array[SparseVRegSet] = []
  let live_in : Array[SparseVRegSet] = []
  let successors : Array[Array[Int32]] = []
  let block_ids : Array[Int32] = []
  for block in 0.. {
        function.block_successor_at(block, successor)
      }),
    )
  }
  propagate_live_sets(
    function.block_count(),
    build_dense_successor_edges(block_ids, successors),
    block_defs,
    live_in,
  )
}

///|
fn[F : FunctionView] build_function_range_fragments(
  ranges : LiveRangeSet,
  function : F,
  live_out : Array[Array[Int32]],
) -> Unit {
  let block_ends = Array::make(function.block_count(), 0)
  let open_ends = Array::make(function.value_count(), no_program_point())
  let open_values : Array[Int32] = []
  for block in 0.. 0 {
      let edge_point = ProgramPoint(block, (instruction_count - 1) * 2)
      for successor in 0.. 0 {
      instruction_index = instruction_index - 1
      let instruction = function.block_instruction_at(block, instruction_index)
      let operands = function.instruction_operands(instruction)
      for timing in [OperandTiming::Late, Early] {
        for operand in operands {
          if operand.timing == timing &&
            (operand.role is Def || operand.role is UseDef) {
            let point = operand_program_point(block, instruction_index, timing)
            let end = open_ends[operand.vreg.id]
            add_live_range_fragment(
              ranges,
              operand.vreg,
              point,
              if end.present {
                end.point
              } else {
                point
              },
            )
            open_ends[operand.vreg.id] = no_program_point()
          }
        }
        for operand in operands {
          if operand.timing == timing &&
            (operand.role is Use || operand.role is UseDef) {
            let point = operand_program_point(block, instruction_index, timing)
            if !open_ends[operand.vreg.id].present {
              open_values.push(operand.vreg.id)
              open_ends[operand.vreg.id] = some_program_point(point)
            }
          }
        }
      }
    }
    for parameter_index in 0.. LiveRangeSet {
  let block_order = Array::make(function.block_count(), 0)
  for block in 0..