///|
pub struct VCodeAllocationEnvironment {
allocatable_regs : Array[@vcode.PhysicalReg]
spill_scratch_regs : Array[@vcode.PhysicalReg]
fixed_operand_regs : Array[@vcode.PhysicalReg]
}
///|
pub fn VCodeAllocationEnvironment::new(
allocatable_regs : Array[@vcode.PhysicalReg],
spill_scratch_regs : Array[@vcode.PhysicalReg],
) -> VCodeAllocationEnvironment {
{
allocatable_regs: allocatable_regs.copy(),
spill_scratch_regs: spill_scratch_regs.copy(),
fixed_operand_regs: [],
}
}
///|
/// Declare reserved registers that selected VCode may name only through fixed
/// operand constraints.
pub fn VCodeAllocationEnvironment::with_fixed_operand_regs(
self : VCodeAllocationEnvironment,
fixed_operand_regs : Array[@vcode.PhysicalReg],
) -> VCodeAllocationEnvironment {
{ ..self, fixed_operand_regs: fixed_operand_regs.copy(), }
}
///|
pub suberror VCodeAllocationError {
InvalidSelected(cause~ : @vcode.VCodeVerifyError)
RegallocFailure(cause~ : @regalloc.VerifyError)
AllocationConstructionRejected
InvalidAllocation(cause~ : @vcode.AllocationVerifyError)
} derive(Debug)
///|
pub impl Show for VCodeAllocationError with fn output(self, logger) {
logger.write_string(Repr(self).to_string())
}
///|
fn[Inst] add_plan_transfer(
builder : @vcode.AllocationBuilder[Inst],
instruction : Int,
placement : @vcode.PointPlacement,
value : Int,
from : @vcode.AllocationLocation,
to : @vcode.AllocationLocation,
) -> Unit raise VCodeAllocationError {
if !builder.add_transfer(instruction, placement, value, from, to) {
raise AllocationConstructionRejected
}
}
///|
/// Allocates VCode that has already passed `@vcode.verify_selected`.
///
/// The caller must not mutate `function` between validation and this call.
/// Prefer `allocate_vcode` unless validation is owned by an aggregate compile
/// pipeline.
pub struct AllocationSession {
priv core : @regalloc.AllocationSession
}
///|
pub fn AllocationSession::new() -> AllocationSession {
{ core: @regalloc.AllocationSession::new(), }
}
///|
pub fn[Inst] AllocationSession::allocate_selected_vcode(
self : AllocationSession,
function : @vcode.Function[Inst],
environment : VCodeAllocationEnvironment,
on_phase? : ((String?) -> Unit)? = None,
on_statistics? : ((@vcode.RegallocLoopStatistics) -> Unit)? = None,
verify? : Bool = true,
) -> @vcode.Allocation raise VCodeAllocationError {
// Translate the allocator's phase enum to a name here so neither the
// allocator nor the event enum has to know about the other (ISS-371).
let observer : ((@regalloc.RegallocPhase?) -> Unit)? = match on_phase {
Some(notify) =>
Some(phase => {
notify(
match phase {
Some(p) => Some("\{p}")
None => None
},
)
})
None => None
}
let statistics_observer : ((@regalloc.BundleAllocationStatistics) -> Unit)? = match
on_statistics {
Some(notify) =>
Some(statistics => {
notify({
queue_pops: statistics.queue_pops(),
register_probes: statistics.register_probes(),
occupied_segments_scanned: statistics.occupied_segments_scanned(),
conflicts: statistics.conflicts(),
evictions: statistics.evictions(),
bundle_splits: statistics.bundle_splits(),
second_chance_attempts: statistics.second_chance_attempts(),
max_queue_length: statistics.max_queue_length(),
})
})
None => None
}
if on_phase is Some(notify) {
notify(Some("function_view"))
}
let function_view = VCodeFunctionView::new(function)
let plan = self.core.allocate_function(
function_view,
@regalloc.MachineEnv::new(
environment.allocatable_regs,
environment.spill_scratch_regs,
)
.with_operand_scratch_regs([])
.with_fixed_operand_regs(environment.fixed_operand_regs),
config=RegallocConfig(verify~, observer~, statistics_observer~),
) catch {
error => raise RegallocFailure(cause=error)
}
let spill_owners : Array[Int] = []
let spill_sizes : Array[Int] = []
let spill_alignments : Array[Int] = []
for slot in 0..
add_plan_transfer(
builder,
instruction_id,
Before,
edit.value.id,
edit.from,
edit.to,
)
After(instruction_id) =>
add_plan_transfer(
builder,
instruction_id,
After,
edit.value.id,
edit.from,
edit.to,
)
Edge(source_block~, successor_index~) =>
if !builder.add_edge_transfer(
source_block,
successor_index,
edit.value.id,
edit.from,
edit.to,
) {
raise AllocationConstructionRejected
}
}
}
if !builder.add_function_safepoint_roots() {
raise AllocationConstructionRejected
}
let allocation = builder.finish()
if verify {
@vcode.verify_allocation_invariants(function, allocation) catch {
error => raise InvalidAllocation(cause=error)
}
}
allocation
}
///|
/// Allocate one selected function with an ephemeral session.
pub fn[Inst] allocate_selected_vcode(
function : @vcode.Function[Inst],
environment : VCodeAllocationEnvironment,
on_phase? : ((String?) -> Unit)? = None,
on_statistics? : ((@vcode.RegallocLoopStatistics) -> Unit)? = None,
verify? : Bool = true,
) -> @vcode.Allocation raise VCodeAllocationError {
AllocationSession::new().allocate_selected_vcode(
function,
environment,
on_phase~,
on_statistics~,
verify~,
)
}
///|
pub fn[Inst] allocate_vcode(
function : @vcode.Function[Inst],
environment : VCodeAllocationEnvironment,
) -> @vcode.Allocation raise VCodeAllocationError {
@vcode.verify_selected(function) catch {
error => raise InvalidSelected(cause=error)
}
allocate_selected_vcode(function, environment)
}