///|
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] compute_function_live_sets(function : F) -> DenseLiveSets {
  let use_defs : Array[LiveSetUseDef] = []
  let definitions : Array[ProgramPoint?] = Array::make(
    function.value_count(),
    None,
  )
  let uses : Array[Array[ProgramPoint]] = []
  for _ in 0.. Unit {
  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.get(operand.vreg.id) {
              Some(end) => {
                add_live_range_fragment(ranges, operand.vreg, point, end)
                open_ends.remove(operand.vreg.id)
              }
              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.get(operand.vreg.id) is None {
              open_ends[operand.vreg.id] = point
            }
          }
        }
      }
    }
    for parameter in function.block_parameters(block) {
      match open_ends.get(parameter.id) {
        Some(end) => {
          add_live_range_fragment(ranges, parameter, block_start, end)
          open_ends.remove(parameter.id)
        }
        None =>
          add_live_range_fragment(ranges, parameter, block_start, block_start)
      }
    }
    if block == 0 {
      for parameter in function.entry_values() {
        match open_ends.get(parameter.id) {
          Some(end) => {
            add_live_range_fragment(ranges, parameter, block_start, end)
            open_ends.remove(parameter.id)
          }
          None =>
            add_live_range_fragment(ranges, parameter, block_start, block_start)
        }
      }
    }
    for value_id, end in open_ends {
      add_live_range_fragment(
        ranges,
        { id: value_id, class: function.value_class(value_id) },
        block_start,
        end,
      )
    }
  }
  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
}