///|
/// The allocator's internal phases, in the order they run.
///
/// Reported through `RegallocConfig::observer` so a caller that owns a clock
/// can attribute compile time per phase. This module stays clock-free: it
/// only says which phase it is entering (ISS-371).
pub(all) enum RegallocPhase {
  InputValidation
  LiveRanges
  SegmentConstruction
  BundleFormation
  BundleAllocation
  HomeAssignment
  OperandAssignment
  EdgeTransfers
  EditResolution
  Verification
} derive(Eq, Debug)

///|
pub impl Show for RegallocPhase with fn output(self, logger) {
  logger.write_string(
    match self {
      InputValidation => "input_validation"
      LiveRanges => "live_ranges"
      SegmentConstruction => "segment_construction"
      BundleFormation => "bundle_formation"
      BundleAllocation => "bundle_allocation"
      HomeAssignment => "home_assignment"
      OperandAssignment => "operand_assignment"
      EdgeTransfers => "edge_transfers"
      EditResolution => "edit_resolution"
      Verification => "verification"
    },
  )
}

///|
/// Deterministic work counters for the backtracking bundle loop.
///
/// These count allocator decisions rather than elapsed time, so callers can
/// compare runs without making this reusable module own a clock.
pub struct BundleAllocationStatistics {
  mut queue_pops : Int
  mut register_probes : Int
  mut occupied_segments_scanned : Int
  mut conflicts : Int
  mut evictions : Int
  mut bundle_splits : Int
  mut second_chance_attempts : Int
  mut max_queue_length : Int
} derive(Eq, Debug)

///|
fn BundleAllocationStatistics::new() -> BundleAllocationStatistics {
  {
    queue_pops: 0,
    register_probes: 0,
    occupied_segments_scanned: 0,
    conflicts: 0,
    evictions: 0,
    bundle_splits: 0,
    second_chance_attempts: 0,
    max_queue_length: 0,
  }
}

///|
pub fn BundleAllocationStatistics::queue_pops(
  self : BundleAllocationStatistics,
) -> Int {
  self.queue_pops
}

///|
pub fn BundleAllocationStatistics::register_probes(
  self : BundleAllocationStatistics,
) -> Int {
  self.register_probes
}

///|
pub fn BundleAllocationStatistics::occupied_segments_scanned(
  self : BundleAllocationStatistics,
) -> Int {
  self.occupied_segments_scanned
}

///|
pub fn BundleAllocationStatistics::conflicts(
  self : BundleAllocationStatistics,
) -> Int {
  self.conflicts
}

///|
pub fn BundleAllocationStatistics::evictions(
  self : BundleAllocationStatistics,
) -> Int {
  self.evictions
}

///|
pub fn BundleAllocationStatistics::bundle_splits(
  self : BundleAllocationStatistics,
) -> Int {
  self.bundle_splits
}

///|
pub fn BundleAllocationStatistics::second_chance_attempts(
  self : BundleAllocationStatistics,
) -> Int {
  self.second_chance_attempts
}

///|
pub fn BundleAllocationStatistics::max_queue_length(
  self : BundleAllocationStatistics,
) -> Int {
  self.max_queue_length
}

///|
pub struct RegallocConfig {
  verify : Bool
  // Called as each phase begins, and once with None when the last one ends.
  // Absent by default so the allocator pays nothing when nobody is measuring.
  observer : ((RegallocPhase?) -> Unit)?
  statistics_observer : ((BundleAllocationStatistics) -> Unit)?
}

///|
/// Reusable storage for a serial sequence of register-allocation jobs.
///
/// A session is intentionally not thread-safe. Independent compilation jobs
/// must own independent sessions; callers compiling functions serially may
/// reuse one session to retain scratch capacity between functions.
pub struct AllocationSession {
  priv context_priorities : Array[Int]
  priv context_weights : Array[Int]
  priv context_fixed_constraints : Array[Bool]
  priv context_boundary_costs : Array[Int]
  priv context_minimals : Array[Bool]
  priv context_spill_sets : Array[Int]
  priv segment_owner : Array[Int]
  priv segment_spill_set : Array[Int]
  priv owner_run_count : Array[Int]
  priv occupied_block_order : Array[Int]
  priv occupied_roots : Array[Int]
  priv occupied_left : Array[Int]
  priv occupied_right : Array[Int]
  priv occupied_parent : Array[Int]
  priv occupied_previous : Array[Int]
  priv occupied_next : Array[Int]
  priv occupied_starts : Array[Int64]
  priv occupied_ends : Array[Int64]
  priv occupied_stack : Array[Int]
  priv preferred_by_value : Array[Int]
  priv conflict_marks : Array[Int]
  priv probe_conflicts : Array[Int]
  priv eviction_conflicts : Array[Int]
  priv register_order : Array[Int]
  priv register_order_marks : Array[Bool]
  priv physical_register_indexes : Array[Int]
  priv queue_items : Array[Int]
  priv queue_priorities : Array[Int]
  priv queue_weights : Array[Int]
  priv queue_in_queue : Array[Bool]
  priv queue_hints : Array[Int]
}

///|
pub fn RegallocConfig::RegallocConfig(
  verify? : Bool = true,
  observer? : ((RegallocPhase?) -> Unit)? = None,
  statistics_observer? : ((BundleAllocationStatistics) -> Unit)? = None,
) -> RegallocConfig {
  { verify, observer, statistics_observer, }
}

///|
fn RegallocConfig::report_bundle_statistics(
  self : RegallocConfig,
  statistics : BundleAllocationStatistics,
) -> Unit {
  if self.statistics_observer is Some(notify) {
    notify(statistics)
  }
}

///|
pub fn RegallocConfig::verify(self : RegallocConfig) -> Bool {
  self.verify
}

///|
/// Announce that `phase` is starting, or that the last one has finished.
pub fn RegallocConfig::enter_phase(
  self : RegallocConfig,
  phase : RegallocPhase?,
) -> Unit {
  if self.observer is Some(notify) {
    notify(phase)
  }
}