///|
fn operand_program_point(
  block : Int,
  instruction : Int,
  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,
) -> DenseLiveSets {
  let block_defs : Array[SparseVRegSet] = []
  let live_in : Array[SparseVRegSet] = []
  let successors : Array[Array[Int]] = []
  let block_ids : Array[Int] = []
  for block in 0.. Unit {
  let open_ends : Array[ProgramPoint?] = Array::make(
    function.value_count(),
    None,
  )
  let open_values : Array[Int] = []
  for block in 0.. 0 {
      instruction_index = instruction_index - 1
      let operands = function.instruction_operands(
        instructions[instruction_index],
      )
      for timing in [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)
            match open_ends[operand.vreg.id] {
              Some(end) => {
                add_live_range_fragment(ranges, operand.vreg, point, end)
                open_ends[operand.vreg.id] = None
              }
              None =>
                add_live_range_fragment(ranges, operand.vreg, point, 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] is None {
              open_values.push(operand.vreg.id)
              open_ends[operand.vreg.id] = Some(point)
            }
          }
        }
      }
    }
    for parameter in function.block_parameters(block) {
      match open_ends[parameter.id] {
        Some(end) => {
          add_live_range_fragment(ranges, parameter, block_start, end)
          open_ends[parameter.id] = None
        }
        None =>
          add_live_range_fragment(ranges, parameter, block_start, block_start)
      }
    }
    if block == 0 {
      for parameter in function.entry_values() {
        match open_ends[parameter.id] {
          Some(end) => {
            add_live_range_fragment(ranges, parameter, block_start, end)
            open_ends[parameter.id] = None
          }
          None =>
            add_live_range_fragment(ranges, parameter, block_start, block_start)
        }
      }
    }
    for value_id in open_values {
      if open_ends[value_id] is Some(end) {
        add_live_range_fragment(
          ranges,
          { id: value_id, class: function.value_class(value_id) },
          block_start,
          end,
        )
        open_ends[value_id] = None
      }
    }
    open_values.clear()
  }
  for range in ranges.ranges {
    range.sort_ranges(ranges.block_order)
  }
}

///|
fn[F : FunctionView] build_function_live_ranges(function : F) -> LiveRangeSet {
  let block_order = Array::make(function.block_count(), 0)
  for block in 0.. {
        let by_point = lhs.compare_with_order(rhs, ranges.block_order)
        if by_point != 0 {
          by_point
        } else {
          a.vreg.id - b.vreg.id
        }
      }
      (None, Some(_)) => 1
      (Some(_), None) => -1
      (None, None) => a.vreg.id - b.vreg.id
    }
  })
  ranges
}