///|
/// Returns the allocation type for a valid dense value id.
///
/// Allocation consumers call these indexed accessors only after selected VCode
/// validation, so invalid indices are programmer errors.
pub fn[Inst] Function::allocation_value_type_at(
  self : Function[Inst],
  value : Int,
) -> ValueType {
  self.values[value].ty
}

///|
/// Returns a function parameter as a canonical allocation virtual register.
pub fn[Inst] Function::allocation_entry_value_at(
  self : Function[Inst],
  parameter : Int,
) -> AllocationVirtualReg {
  let value = self.parameters[parameter].id
  { id: value, class: reg_class_for_value_type(self.values[value].ty), }
}

///|
/// Returns the stable block id at a valid dense layout index.
pub fn[Inst] Function::allocation_layout_block_id_at(
  self : Function[Inst],
  block : Int,
) -> Int {
  self.layout[block].id
}

///|
pub fn[Inst] Function::allocation_block_parameter_count(
  self : Function[Inst],
  block : Int,
) -> Int {
  self.blocks[self.layout[block].id].parameters.length()
}

///|
pub fn[Inst] Function::allocation_block_parameter_at(
  self : Function[Inst],
  block : Int,
  parameter : Int,
) -> AllocationVirtualReg {
  let value = self.blocks[self.layout[block].id].parameters[parameter].id
  { id: value, class: reg_class_for_value_type(self.values[value].ty), }
}

///|
pub fn[Inst] Function::allocation_block_instruction_count(
  self : Function[Inst],
  block : Int,
) -> Int {
  let data = self.blocks[self.layout[block].id]
  let terminator_count = match data.terminator {
    Some(_) => 1
    None => 0
  }
  data.body.length() + terminator_count
}

///|
pub fn[Inst] Function::allocation_block_instruction_at(
  self : Function[Inst],
  block : Int,
  instruction : Int,
) -> Int {
  let data = self.blocks[self.layout[block].id]
  if instruction < data.body.length() {
    data.body[instruction].id
  } else {
    data.terminator.unwrap().id
  }
}

///|
pub fn[Inst] Function::allocation_block_successor_count(
  self : Function[Inst],
  block : Int,
) -> Int {
  let terminator = self.blocks[self.layout[block].id].terminator.unwrap().id
  self.instructions[terminator].successors.length()
}

///|
pub fn[Inst] Function::allocation_block_successor_at(
  self : Function[Inst],
  block : Int,
  successor : Int,
) -> Int {
  let terminator = self.blocks[self.layout[block].id].terminator.unwrap().id
  let target = self.instructions[terminator].successors[successor].target.id
  self.blocks[target].layout_index
}

///|
pub fn[Inst] Function::allocation_edge_argument_count(
  self : Function[Inst],
  block : Int,
  successor : Int,
) -> Int {
  let terminator = self.blocks[self.layout[block].id].terminator.unwrap().id
  self.instructions[terminator].successors[successor].arguments.length()
}

///|
pub fn[Inst] Function::allocation_edge_argument_at(
  self : Function[Inst],
  block : Int,
  successor : Int,
  argument : Int,
) -> AllocationVirtualReg {
  let terminator = self.blocks[self.layout[block].id].terminator.unwrap().id
  let value = self.instructions[terminator].successors[successor].arguments[argument].id
  { id: value, class: reg_class_for_value_type(self.values[value].ty), }
}

///|
pub fn[Inst] Function::allocation_instruction_operands(
  self : Function[Inst],
  instruction : Int,
) -> ArrayView[AllocationOperand] {
  let data = self.instructions[instruction]
  self.operands[data.operand_start:data.operand_start +
  data.allocation_operand_count]
}

///|
pub fn[Inst] Function::allocation_instruction_clobbers(
  self : Function[Inst],
  instruction : Int,
) -> ArrayView[PhysicalReg] {
  let data = self.instructions[instruction]
  self.clobbers[data.clobber_start:data.clobber_start + data.clobber_count]
}