///|
pub fn allocation_iteration_limit(bundle_count : Int) -> Int {
  bundle_count * 10
}

///|
pub fn should_force_spill_after_retry(
  attempts : Int,
  retry_limit : Int,
  is_pinned : Bool,
  has_fixed_constraint : Bool,
) -> Bool {
  attempts > retry_limit && !is_pinned && !has_fixed_constraint
}

///|
pub fn should_force_spill_for_class_and_call(
  class : RegClass,
  crosses_call : Bool,
) -> Bool {
  class == Vector && crosses_call
}

///|
pub fn select_available_regs_for_bundle(
  class : RegClass,
  crosses_call : Bool,
  available_regs : Array[PhysicalReg],
  callee_saved_regs : Array[PhysicalReg],
) -> Array[PhysicalReg] {
  if crosses_call {
    match class {
      Int | Float => callee_saved_regs
      Vector => []
    }
  } else {
    available_regs
  }
}

///|
pub fn max_physical_reg_id(regs : Array[PhysicalReg]) -> Int {
  let mut max_id = -1
  for preg in regs {
    if preg.id > max_id {
      max_id = preg.id
    }
  }
  max_id
}

///|
pub fn select_caller_saved_regs(
  allocatable_regs : Array[PhysicalReg],
  callee_saved_regs : Array[PhysicalReg],
) -> Array[PhysicalReg] {
  let mut max_id = max_physical_reg_id(allocatable_regs)
  let callee_max_id = max_physical_reg_id(callee_saved_regs)
  if callee_max_id > max_id {
    max_id = callee_max_id
  }
  let is_callee_saved = Array::make(max_id + 1, false)
  for preg in callee_saved_regs {
    if preg.id >= 0 && preg.id < is_callee_saved.length() {
      is_callee_saved[preg.id] = true
    }
  }
  let caller_saved : Array[PhysicalReg] = []
  for preg in allocatable_regs {
    if preg.id >= 0 &&
      preg.id < is_callee_saved.length() &&
      !is_callee_saved[preg.id] {
      caller_saved.push(preg)
    }
  }
  caller_saved
}

///|
pub(all) enum ParamPreassignDecision {
  PreassignIncoming(Bool)
  HintIncoming
  Defer
} derive(Eq, Debug)

///|
pub fn plan_param_preassignment(
  strict_precolor : Bool,
  is_pinned_special_param : Bool,
  crosses_call : Bool,
) -> ParamPreassignDecision {
  if crosses_call && !is_pinned_special_param {
    Defer
  } else if strict_precolor || is_pinned_special_param {
    PreassignIncoming(
      is_pinned_special_param || (strict_precolor && !crosses_call),
    )
  } else {
    HintIncoming
  }
}