///|
pub(all) struct ReloadUseInfo {
block_id : Int
inst_index : Int
spill_slot : Int
vreg : VirtualReg
} derive(Eq, Debug)
///|
pub(all) struct ReloadIntervalInfo {
block_id : Int
spill_slot : Int
vreg : VirtualReg
first_inst : Int
last_inst : Int
} derive(Eq, Debug)
///|
fn same_reload_key(a : ReloadUseInfo, b : ReloadUseInfo) -> Bool {
a.block_id == b.block_id && a.spill_slot == b.spill_slot
}
///|
fn has_conflicting_owner(
uses : Array[ReloadUseInfo],
item : ReloadUseInfo,
) -> Bool {
for other in uses {
if same_reload_key(other, item) && other.vreg.id != item.vreg.id {
return true
}
}
false
}
///|
fn interval_already_added(
intervals : Array[ReloadIntervalInfo],
item : ReloadUseInfo,
) -> Bool {
for interval in intervals {
if interval.block_id == item.block_id &&
interval.spill_slot == item.spill_slot {
return true
}
}
false
}
///|
/// Compute block-local reload intervals for spill slots used more than once.
///
/// The logic is intentionally independent of any machine IR. Callers project
/// their spilled operand uses into `ReloadUseInfo`, and target adapters decide
/// later which physical registers can hold each interval.
pub fn compute_reload_intervals(
uses : Array[ReloadUseInfo],
allow_vector? : Bool = false,
) -> Array[ReloadIntervalInfo] {
let intervals : Array[ReloadIntervalInfo] = []
for item in uses {
if item.vreg.class is Vector && !allow_vector {
continue
}
if interval_already_added(intervals, item) ||
has_conflicting_owner(uses, item) {
continue
}
let mut first = item.inst_index
let mut last = item.inst_index
for other in uses {
if same_reload_key(other, item) && other.vreg.id == item.vreg.id {
if other.inst_index < first {
first = other.inst_index
}
if other.inst_index > last {
last = other.inst_index
}
}
}
if last > first {
intervals.push({
block_id: item.block_id,
spill_slot: item.spill_slot,
vreg: item.vreg,
first_inst: first,
last_inst: last,
})
}
}
intervals
}