///|
fn same_physical_reg(lhs : PhysicalReg, rhs : PhysicalReg) -> Bool {
  lhs.id == rhs.id && lhs.class == rhs.class
}

///|
fn append_probe_phase(
  out : Array[PhysicalReg],
  regs : Array[PhysicalReg],
  scan_limit : Int,
  offset : Int,
  skip_reg : PhysicalReg?,
) -> Unit {
  let n = regs.length()
  if n == 0 {
    return
  }
  let phase_scan_limit = if scan_limit < n { scan_limit } else { n }
  let phase_offset = (offset % n + n) % n
  let mut scanned = 0
  for i in 0..= phase_scan_limit {
      break
    }
    let preg = regs[(phase_offset + i) % n]
    if skip_reg is Some(skip) && same_physical_reg(preg, skip) {
      continue
    }
    scanned = scanned + 1
    out.push(preg)
  }
}

///|
pub fn plan_register_probe_order(
  available_regs : Array[PhysicalReg],
  caller_saved_regs : Array[PhysicalReg],
  callee_saved_regs : Array[PhysicalReg],
  prefer_caller_saved : Bool,
  scan_limit : Int,
  offset : Int,
  skip_reg : PhysicalReg?,
) -> Array[PhysicalReg] {
  let out : Array[PhysicalReg] = []
  let limit = if scan_limit < available_regs.length() {
    scan_limit
  } else {
    available_regs.length()
  }
  if prefer_caller_saved {
    append_probe_phase(out, caller_saved_regs, limit, offset, skip_reg)
    append_probe_phase(out, callee_saved_regs, limit, offset, skip_reg)
  } else {
    append_probe_phase(out, available_regs, limit, offset, skip_reg)
  }
  out
}