Target-independent register allocation algorithm
///|
test "allocate virtual registers to physical registers" {
let r0 : PhysicalReg = { id: 0, class: Int }
let r1 : PhysicalReg = { id: 1, class: Int }
let v0 : VirtualReg = { id: 0, class: Int }
let v1 : VirtualReg = { id: 1, class: Int }
let program = Program::Program([r0, r1])
let block = Block::Block(0)
let inst = Instruction::Instruction(0)
inst.add_operand(Operand::def(v0))
inst.add_operand(Operand::use_reg(v1))
block.append(inst)
program.add_block(block)
let allocation = allocate_linear_scan(program)
inspect(allocation.location_of(v0) == Some(Reg(r0)), content="true")
inspect(allocation.location_of(v1) == Some(Reg(r1)), content="true")
inspect(verify_allocation(program, allocation), content="()")
}///|
test "compute a live range across two instructions" {
let r0 : PhysicalReg = { id: 0, class: Int }
let v0 : VirtualReg = { id: 0, class: Int }
let program = Program::Program([r0])
let block = Block::Block(0)
let def_inst = Instruction::Instruction(0)
def_inst.add_operand(Operand::def(v0))
block.append(def_inst)
let use_inst = Instruction::Instruction(1)
use_inst.add_operand(Operand::use_reg(v0))
block.append(use_inst)
program.add_block(block)
let ranges = build_live_ranges(program)
let range = ranges.get_by_vreg(v0).unwrap()
inspect(range.start() == Some(ProgramPoint(0, 0)), content="true")
inspect(range.end() == Some(ProgramPoint(0, 1)), content="true")
inspect(range.uses.length(), content="2")
}pub suberror VerifyError {
Unassigned(vreg~ : VirtualReg)
FixedConstraintViolation(vreg~ : VirtualReg, required~ : PhysicalReg, actual~ : Location)
OverlappingAllocation(left~ : VirtualReg, right~ : VirtualReg, location~ : Location)
RegisterClassMismatch(vreg~ : VirtualReg, preg~ : PhysicalReg)
ClobberedRegister(vreg~ : VirtualReg, preg~ : PhysicalReg)
TiedConstraintViolation(left~ : VirtualReg, right~ : VirtualReg, left_location~ : Location, right_location~ : Location)
ScratchRegisterUnavailable(message~ : String)
}pub(all) struct BacktrackingAllocationAttempt[P, Q] {
reg : P?
evicted_ids : Array[Int]
split : (Q, P)?
}pub(all) enum BacktrackingAllocationLoc[P] {
BacktrackingReg(P)
BacktrackingSpill(Int)
BacktrackingUnallocated
}pub(all) struct BacktrackingConflictScanResult[P] {
conflict_ids : Array[Int]
max_conflict_weight : Int
first_conflict_point : P?
rejected : Bool
}pub(all) struct BacktrackingConstraintInstEdits[P] {
block_index : Int
inst_index : Int
before : Array[BacktrackingConstraintMove[P]]
after : Array[BacktrackingConstraintMove[P]]
}pub(all) struct BacktrackingConstraintMove[P] {
from : BacktrackingMoveLoc[P]
to : BacktrackingMoveLoc[P]
class_tag : Int
}pub(all) enum BacktrackingConstraintOperand[P] {
BacktrackingVirtual(BacktrackingAllocationLoc[P], Int)
BacktrackingPhysical(P, Int)
}pub(all) struct BacktrackingFixedConstraint[P] {
operand : BacktrackingConstraintOperand[P]
required : P
}pub(all) struct BacktrackingFixedConstraintInst[P] {
block_index : Int
inst_index : Int
uses : Array[BacktrackingFixedConstraint[P]]
defs : Array[BacktrackingFixedConstraint[P]]
}type BacktrackingHintCache[P]fn[P] BacktrackingHintCache::set(self : BacktrackingHintCache[P], bundle_id : Int, hint : P?) -> Unitpub(all) enum BacktrackingMoveLoc[P] {
BacktrackingMoveReg(P)
BacktrackingMoveSpill(Int)
}pub(all) struct BacktrackingOccupiedSpan[P] {
owner_id : Int
span : OrderedSpan[P]
owner_weight : Int
owner_pinned : Bool
}type BacktrackingQueue[P]fn[P] BacktrackingQueue::push(self : BacktrackingQueue[P], bundle_id : Int, prio : Int, hint_preg : P?) -> Unitpub(all) struct BacktrackingQueueEntry[P] {
bundle_id : Int
prio : Int
hint_preg : P?
seq : Int
}pub(all) struct BacktrackingRangeResult[P] {
vreg_id : Int
allocation : BacktrackingAllocationLoc[P]
}type BacktrackingSpillsetState[P]fn[P] BacktrackingSpillsetState::BacktrackingSpillsetState(bundle_count : Int, max_splits_per_spillset? : Int) -> BacktrackingSpillsetState[P]fn[P] BacktrackingSpillsetState::record_hint(self : BacktrackingSpillsetState[P], spillset_id : Int, hint : P) -> Unitfn[P] BacktrackingSpillsetState::record_split(self : BacktrackingSpillsetState[P], spillset_id : Int) -> Unitfn[P] BacktrackingSpillsetState::split_limit_reached(self : BacktrackingSpillsetState[P], spillset_id : Int) -> Boolpub(all) struct BacktrackingSplitRange[P] {
range_id : Int
start : P?
end : P?
}pub(all) struct Block {
id : Int
instructions : Array[Instruction]
successors : Array[Int]
} derive(Debug)pub(all) struct BundleSplitRange {
range_id : Int
start : ProgramPoint?
end : ProgramPoint?
} derive(Eq, Debug)pub(all) struct CompactSpillSet {
old_slot : Int
class : RegClass
ranges : Array[ProgramRange]
} derive(Debug)pub(all) struct ConflictScanResult {
conflict_ids : Array[Int]
max_conflict_weight : Int
first_conflict_point : ProgramPoint?
rejected : Bool
} derive(Eq, Debug)pub(all) struct FixedConstraint {
operand : FixedConstraintOperand
required : PhysicalReg
} derive(Eq, Debug)pub(all) struct FixedConstraintInst {
block_index : Int
inst_index : Int
uses : Array[FixedConstraint]
defs : Array[FixedConstraint]
} derive(Debug)pub(all) struct FixedConstraintInstEdits {
block_index : Int
inst_index : Int
before : Array[FixedConstraintMove]
after : Array[FixedConstraintMove]
} derive(Debug)pub(all) enum FixedConstraintOperand {
Virtual(Location?, Int)
Physical(PhysicalReg, Int)
} derive(Eq, Debug)pub(all) struct Instruction {
id : Int
operands : Array[Operand]
clobbers : Array[PhysicalReg]
} derive(Debug)pub(all) struct LiveRange {
id : Int
vreg : VirtualReg
ranges : Array[ProgramRange]
uses : Array[UsePosition]
bundle_id : Int
allocation : Location?
crosses_call : Bool
} derive(Debug)fn LiveRange::touch_with_order(self : LiveRange, point : ProgramPoint, block_order : Array[Int]) -> Unitpub(all) struct LiveSetUseDef {
vreg_id : Int
def_point : ProgramPoint?
is_entry_param_def : Bool
use_points : Array[ProgramPoint]
} derive(Debug)pub(all) struct MergeSpan[R] {
span : R
start_ord : Int
start_before_ord : Int
end_ord : Int
} derive(Debug)pub(all) struct OccupiedProgramSpan {
owner_id : Int
span : OrderedProgramSpan
owner_weight : Int
owner_pinned : Bool
} derive(Eq, Debug)pub(all) struct Operand {
vreg : VirtualReg
role : OperandRole
constraint : OperandConstraint
tie_id : Int
} derive(Eq, Debug)pub(all) struct OrderedProgramSpan {
start : ProgramPoint
start_ord : Int
end_ord : Int
} derive(Eq, Debug)fn ProgramPoint::compare_with_order(self : ProgramPoint, other : ProgramPoint, block_order : Array[Int]) -> Intfn ProgramRange::contains(self : ProgramRange, point : ProgramPoint, block_order : Array[Int]) -> Boolfn ProgramRange::overlaps(self : ProgramRange, other : ProgramRange, block_order : Array[Int]) -> Boolpub(all) enum RegallocAlgorithm {
Backtracking
SinglePass
}pub(all) struct ReloadIntervalInfo {
block_id : Int
spill_slot : Int
vreg : VirtualReg
first_inst : Int
last_inst : Int
} derive(Eq, Debug)pub(all) struct ReloadUseInfo {
block_id : Int
inst_index : Int
spill_slot : Int
vreg : VirtualReg
} derive(Eq, Debug)pub(all) struct RematCloneInfo {
vreg : VirtualReg
block_id : Int
inst_index : Int
} derive(Eq, Debug)pub(all) struct RematLocalCloneInfo {
vreg : VirtualReg
block_id : Int
first_inst_index : Int
} derive(Eq, Debug)pub(all) struct SpillSlotCompaction {
remaps : Array[SpillSlotRemap]
total_slots : Int
} derive(Eq, Debug)pub(all) struct StackFrame {
slots : Array[StackSlot]
frame_size : Int
frame_align : Int
next_spill_offset : Int
next_arg_offset : Int
callee_saved_regs : Array[PhysicalReg]
}impl Show for StackFramefn StackFrame::alloc_spill_slot(self : StackFrame, vreg : VirtualReg, size : Int, align : Int) -> Intpub(all) struct StackSlot {
offset : Int
size : Int
align : Int
kind : StackSlotKind
} derive(Eq, Debug)pub(all) enum StackSlotKind {
Spill(VirtualReg)
Local(Int)
OutgoingArg(Int)
CalleeSaved(PhysicalReg)
} derive(Eq, Debug)impl Show for StackSlotKindpub(all) struct TempRegisterChoice {
preg : PhysicalReg
next_cursor : Int
save_slot : Int?
next_spill_slot : Int
} derive(Eq, Debug)pub(all) struct UsePosition {
point : ProgramPoint
kind : UseKind
constraint : OperandConstraint
tie_id : Int
} derive(Eq, Debug)fn UsePosition::UsePosition(point : ProgramPoint, kind : UseKind, constraint : OperandConstraint) -> UsePositionfn[P] backtracking_queue_entry_higher(a : BacktrackingQueueEntry[P], b : BacktrackingQueueEntry[P]) -> Boolfn[P] build_backtracking_allocation_result(ranges : Array[BacktrackingRangeResult[P]], num_spill_slots : Int) -> BacktrackingAllocationResult[P]fn build_dense_successor_edges(block_ids : Array[Int], block_successor_ids : Array[Array[Int]]) -> DenseCfgEdgesfn choose_fixed_reg_hint(available_regs : Array[PhysicalReg], fixed_regs : Array[PhysicalReg]) -> PhysicalReg?fn[C, R, P] compact_backtracking_spill_slots(spillsets : Array[SpillSetInfo[C, R, P]], no_share_edges : Array[SpillSlotNoShareEdge], reserve_slot : (Int, C) -> SpillSlotReservation, ranges_overlap : (Array[R], Array[R]) -> Bool, compare_start : (P, P) -> Int) -> SpillSlotCompactionfn compact_spill_slots(spillsets : Array[CompactSpillSet], no_share_edges : Array[SpillSlotNoShareEdge], block_order : Array[Int]) -> SpillSlotCompactionfn compute_dense_live_sets(num_blocks : Int, num_vregs : Int, cfg : DenseCfgEdges, use_defs : Array[LiveSetUseDef]) -> DenseLiveSetsfn compute_reload_intervals(uses : Array[ReloadUseInfo], allow_vector? : Bool) -> Array[ReloadIntervalInfo]fn[P] has_backtracking_span_conflict(spans : Array[OrderedSpan[P]], occupied : Array[(Int, OrderedSpan[P])]) -> Boolfn has_ordered_span_conflict(spans : Array[OrderedProgramSpan], occupied : Array[OrderedProgramSpan]) -> Boolfn insert_allocated_spans(occupied : Array[AllocatedProgramSpan], owner_id : Int, spans : Array[OrderedProgramSpan]) -> Array[AllocatedProgramSpan]fn[P] lower_bound_backtracking_span_by_end(occupied : Array[(Int, OrderedSpan[P])], point_ord : Int) -> Intfn[P] plan_backtracking_bundle_split(ranges : Array[BacktrackingSplitRange[P]], split_point : P?, split_limit_reached : Bool, end_before_or_at_split : (P, P) -> Bool, start_after_or_at_split : (P, P) -> Bool) -> BacktrackingSplitPlanfn[P] plan_backtracking_fixed_constraint_edits(insts : Array[BacktrackingFixedConstraintInst[P]], same_reg : (P, P) -> Bool) -> Array[BacktrackingConstraintInstEdits[P]]fn[C, P, R] plan_bundle_merges(ranges : Array[BundleMergeRange[C, P, R]], edges : Array[BundleMergeEdge], class_compatible : (C, C) -> Bool, same_fixed_reg : (P, P) -> Bool) -> BundleMergePlan[C, R]fn plan_bundle_split(ranges : Array[BundleSplitRange], split_point : ProgramPoint?, block_order : Array[Int], spillset_split_count : Int, max_splits_per_spillset : Int) -> BundleSplitPlanfn plan_cross_block_rematerializations(defs : Array[RematDefInfo], uses : Array[RematUseInfo]) -> Array[RematLocalCloneInfo]fn plan_fixed_constraint_edits(insts : Array[FixedConstraintInst]) -> Array[FixedConstraintInstEdits]fn plan_long_distance_rematerializations(defs : Array[RematDefInfo], uses : Array[RematUseInfo], min_distance? : Int, per_vreg_clone_cap? : Int) -> Array[RematCloneInfo]fn plan_param_preassignment(strict_precolor : Bool, is_pinned_special_param : Bool, crosses_call : Bool) -> ParamPreassignDecisionfn 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]fn remove_allocated_spans(occupied : Array[AllocatedProgramSpan], owner_id : Int) -> Array[AllocatedProgramSpan]fn resolve_parallel_moves(moves : Array[ParallelMove], scratch_int : Array[PhysicalReg], scratch_float : Array[PhysicalReg]) -> Array[ParallelMove]fn resolve_threaded_jump(blocks : Array[ThreadBlock], start_target : Int, start_args : Array[ThreadArg]) -> ThreadedJumpfn[B, R, P] run_backtracking_allocation_loop(queue : BacktrackingQueue[R], bundle_attempts : Array[Int], bundle_retry_limit : Int, enable_backtracking : Bool, bundle_count : Int, get_bundle : (Int) -> B, bundle_done : (B) -> Bool, bundle_is_pinned : (B) -> Bool, bundle_has_fixed_constraint : (B) -> Bool, bundle_must_force_spill : (B) -> Bool, hint_for_bundle : (B, R?) -> R?, try_backtracking : (B, R?) -> (R?, (P, R)?), try_single_pass : (B, R?) -> R?, record_allocation : (B, R) -> Unit, split_bundle : (B, (P, R)?) -> Unit, force_spill : (B) -> Unit) -> Unitfn[P] scan_backtracking_conflicts_with_cutoff(spans : Array[OrderedSpan[P]], occupied : Array[BacktrackingOccupiedSpan[P]], current_weight : Int, max_allowable_cost : Int?) -> BacktrackingConflictScanResult[P]fn scan_conflicts_with_cutoff(spans : Array[OrderedProgramSpan], occupied : Array[OccupiedProgramSpan], current_weight : Int, max_allowable_cost : Int?) -> ConflictScanResultfn select_available_regs_for_bundle(class : RegClass, crosses_call : Bool, available_regs : Array[PhysicalReg], callee_saved_regs : Array[PhysicalReg]) -> Array[PhysicalReg]fn select_caller_saved_regs(allocatable_regs : Array[PhysicalReg], callee_saved_regs : Array[PhysicalReg]) -> Array[PhysicalReg]fn should_force_spill_after_retry(attempts : Int, retry_limit : Int, is_pinned : Bool, has_fixed_constraint : Bool) -> Boolfn should_prefer_caller_saved_for_backtracking(class : RegClass, enable_backtracking : Bool, crosses_call : Bool) -> Boolfn[P] should_take_backtracking_split_candidate(best_split_cost : Int?, best_split_point : P?, candidate_cost : Int, candidate_point : P, compare_points : (P, P) -> Int) -> Boolfn should_take_eviction_candidate(best_conflict_cost : Int?, best_preg : PhysicalReg?, candidate_cost : Int, candidate_preg : PhysicalReg, hint_preg : PhysicalReg?) -> Boolfn should_take_split_candidate(best_split_cost : Int?, best_split_point : ProgramPoint?, candidate_cost : Int, candidate_point : ProgramPoint, block_order : Array[Int]) -> Boolfn[P, Q] try_backtracking_eviction_register(available_regs : Array[P], caller_saved_regs : Array[P], callee_saved_regs : Array[P], spans : Array[OrderedSpan[Q]], current_weight : Int, hint_reg : P?, prefer_caller_saved : Bool, scan_limit : Int, offset : Int, same_reg : (P, P) -> Bool, to_policy_reg : (P) -> PhysicalReg, occupied_for_reg : (P) -> Array[BacktrackingOccupiedSpan[Q]], compare_points : (Q, Q) -> Int) -> BacktrackingAllocationAttempt[P, Q]fn[P, Q] try_backtracking_single_pass_register(available_regs : Array[P], caller_saved_regs : Array[P], callee_saved_regs : Array[P], spans : Array[OrderedSpan[Q]], hint_reg : P?, prefer_caller_saved : Bool, scan_limit : Int, offset : Int, same_reg : (P, P) -> Bool, has_conflict_on_reg : (P, Array[OrderedSpan[Q]]) -> Bool) -> P?Install
Download zipTarget-independent register allocation algorithm