///|
/// Read-only machine-function input consumed by register allocation.
///
/// Values and instructions have stable dense ids. Blocks passed to CFG methods
/// are dense layout indices; `block_id_at` maps them to the embedding's stable
/// block id for diagnostics and edge edits. The allocator may retain derived
/// liveness data, but does not retain the view.
pub(open) trait FunctionView {
  fn value_count(Self) -> Int
  fn value_class(Self, Int) -> RegClass
  fn value_spill_size(Self, Int) -> Int
  fn value_spill_alignment(Self, Int) -> Int
  fn values_share_spill_slot(Self, Int, Int) -> Bool
  fn entry_values(Self) -> Array[VirtualReg]
  fn block_count(Self) -> Int
  fn block_id_at(Self, Int) -> Int
  fn block_parameters(Self, Int) -> Array[VirtualReg]
  fn block_instructions(Self, Int) -> Array[Int]
  fn block_successors(Self, Int) -> Array[Int]
  fn edge_arguments(Self, Int, Int) -> Array[VirtualReg]
  fn instruction_operands(Self, Int) -> Array[Operand]
  fn instruction_clobbers(Self, Int) -> Array[PhysicalReg]
}

///|
/// Physical-register policy supplied by the embedding target.
///
/// `scratch_regs` resolve allocator-inserted edits. The operand scratch subset
/// may additionally hold unconstrained instruction operands; targets may
/// restrict that subset when their emitter reserves scratch registers for local
/// expansion. Fixed-operand registers are reserved from both scratch roles and become
/// legal only when an instruction explicitly names one with `FixedReg`.
pub struct MachineEnv {
  allocatable_regs : Array[PhysicalReg]
  scratch_regs : Array[PhysicalReg]
  operand_scratch_regs : Array[PhysicalReg]
  fixed_operand_regs : Array[PhysicalReg]
}

///|
pub fn MachineEnv::new(
  allocatable_regs : Array[PhysicalReg],
  scratch_regs : Array[PhysicalReg],
) -> MachineEnv {
  {
    allocatable_regs: allocatable_regs.copy(),
    scratch_regs: scratch_regs.copy(),
    operand_scratch_regs: scratch_regs.copy(),
    fixed_operand_regs: [],
  }
}

///|
/// Select the reserved scratch registers that may hold instruction operands.
/// Edit resolution continues to use the full scratch-register set.
pub fn MachineEnv::with_operand_scratch_regs(
  self : MachineEnv,
  operand_scratch_regs : Array[PhysicalReg],
) -> MachineEnv {
  { ..self, operand_scratch_regs: operand_scratch_regs.copy() }
}

///|
/// Declare reserved registers that may be used only by matching `FixedReg`
/// instruction operands.
///
/// These registers are not value homes or allocator-chosen scratches. A fixed
/// operand may still require an explicit edit that moves its value into the
/// named register at the instruction boundary.
pub fn MachineEnv::with_fixed_operand_regs(
  self : MachineEnv,
  fixed_operand_regs : Array[PhysicalReg],
) -> MachineEnv {
  { ..self, fixed_operand_regs: fixed_operand_regs.copy() }
}

///|
pub fn MachineEnv::allocatable_regs(self : MachineEnv) -> Array[PhysicalReg] {
  self.allocatable_regs.copy()
}

///|
pub fn MachineEnv::scratch_regs(self : MachineEnv) -> Array[PhysicalReg] {
  self.scratch_regs.copy()
}

///|
pub fn MachineEnv::operand_scratch_regs(
  self : MachineEnv,
) -> Array[PhysicalReg] {
  self.operand_scratch_regs.copy()
}

///|
pub fn MachineEnv::fixed_operand_regs(self : MachineEnv) -> Array[PhysicalReg] {
  self.fixed_operand_regs.copy()
}