vcode

Target-independent native lowering, VCode, and code-object infrastructure

compiler
vcode
register-allocation
codegen
Download zip
Author
Version
0.14.0
License
Apache-2.0
Last updated
16 hours ago
Downloads
24

#vcode

Milky2018/vcode provides the target-independent infrastructure shared by Wasmoon's native backends. It defines the semantic vocabulary at the native compiler boundary, a streaming instruction-selection protocol, dense storage for target-owned VCode, allocation side tables and verifiers, parallel-move planning, and verified unlinked code objects.

The module is intended for compiler backends and embedding runtimes. It is not a source-language IR, an optimizer, a register allocator, or an executable-code loader. MilkIR owns target-independent program structure and optimization; Milky2018/regalloc owns allocation policy; each target owns its instructions, ABI, frame layout, and encoding; the embedding product owns symbol resolution, executable memory, and runtime registration.

#Packages

PackageResponsibility
Milky2018/vcode/native_typesCanonical native value types, signatures, calls, symbols, operation effects, traps, safepoints, source locations, and stack-map metadata.
Milky2018/vcode/native_loweringStreaming producer-to-target protocol, target-neutral operations, transient value and block handles, and call-ABI elaboration.
Milky2018/vcode/allocation_typesMinimal register-allocation vocabulary shared by VCode, the allocator, and their adapter: register classes, physical and virtual registers, operand roles and timing, constraints, and compact locations.
Milky2018/vcodeDense target VCode, checked construction, allocation and frame side tables, staged verification, call-transfer planning, and parallel-move resolution.
Milky2018/vcode/code_objectVerified machine-code bytes with typed relocations, source and trap sites, safepoints and roots, and target-neutral unwind directives.

These packages are separate ownership seams inside one module, not successive copies of the same IR. In particular, native_lowering does not depend on the VCode package and does not retain a function graph. A target implements a TargetSink that translates each streamed operation directly into its own instruction type stored by vcode.

#Compiler flow

MilkIR and dialect adapters | | native_lowering.Operation, one operation at a time v target TargetSink --> target-owned Inst in vcode.Function[Inst] | | verify_selected v regalloc + vcode allocation side tables | | verify_allocated v target frame layout | | verify_framed / verify_emission_input v target machine-code emitter | v code_object.UnlinkedCodeObject | v embedding-owned linker and code loader

native_types supplies the common vocabulary on both sides of the lowering seam. allocation_types supplies identities that must be shared exactly by VCode and Milky2018/regalloc; targets should not introduce parallel register class, operand-role, or allocation-location types.

#Constructing target VCode

The instruction payload is generic. A target defines an instruction type, then uses CheckedBuilder[Inst] to attach operands, constraints, clobbers, CFG edges, and metadata. Handles are function-owned, so values, blocks, and instructions from different functions cannot be mixed accidentally.

///|
priv enum ExampleInst {
AddOne
Return
} derive(Debug)

///|
test "construct and verify target VCode" {
let builder : CheckedBuilder[ExampleInst] = CheckedBuilder::new_with_results(
"add_one",
[I64],
[I64],
)
let entry = builder.entry_block()
let input = builder.parameter(0)
let (_, results) = builder.append_body(
entry,
AddOne,
[Input::any(input)],
[Output::any(I64)],
[],
InstructionMetadata::empty(),
)
builder.set_terminator(
entry,
Return,
[Input::any(results[0])],
[],
[],
InstructionMetadata::empty(),
)
|> ignore

let function = builder.finish()
verify_selected(function)
inspect(function.parameter_count(), content="1")
inspect(function.instruction_count(), content="2")
inspect(function.summary().contains("AddOne"), content="true")
}

CheckedBuilder is the normal production construction API: it rejects invalid operands and edges before mutation and checks local completeness when it is sealed. It does not replace verify_selected, which checks whole-function CFG and SSA properties. The lower-level Builder is useful for negative tests and tooling that deliberately needs to construct an invalid intermediate state.

An Input or Output describes a correctness constraint independently from a placement preference. Fixed and TiedTo are hard constraints. A preferred physical register is only a hint and must not make an otherwise legal allocation fail. Early and Late operand timing lets the allocator model when an instruction stops using an input and starts defining an output.

#Streaming native lowering

Milky2018/vcode/native_lowering is the boundary between a legalized MilkIR producer and a native target. DirectBuilder exposes typed, transient Value and Block handles to the producer and forwards operations to a TargetSink. It retains only the bookkeeping needed to track types, map transient handles to target ids, delay one terminator, and elaborate configured call ABI details. It does not retain instructions, uses, SSA definitions, or CFG edges.

The protocol defines operation semantics, while the producer owns source-level legalization. The target owns instruction selection, target immediates, calling-convention decisions, physical-register policy, and target VCode verification. Call-ABI elaboration may add hidden stack-map arguments or caller root scopes, but only when an embedding explicitly supplies the corresponding contract.

Use Milky2018/milkir/native to stream core MilkIR and Milky2018/wasm_milkir/native for the WebAssembly dialect. Target users normally create an AArch64 or x64 lowering session instead of constructing a TargetSink directly.

#Allocation and move planning

Function[Inst] is the authoritative selected machine graph. Allocation is stored separately in Allocation, so register assignment, spills, reloads, edge transfers, safepoint roots, and frame placement do not rewrite or clone the instruction graph. Milky2018/vcode_regalloc exposes the function through the allocator's read-only view and materializes its returned plan into these side tables.

Parallel assignments are planned with a dedicated transfer scratch for each register bank. When a cycle and a stack-to-stack transfer cannot be resolved safely with that scratch, the planner emits explicit emergency save and restore steps. The target owns the physical emergency area and must verify that its frame reserves it whenever the resolved plan requires it.

The verification functions represent lifecycle boundaries:

  • verify_selected checks CFG shape, SSA dominance, operands, clobbers, metadata, and layout before allocation.
  • verify_allocated checks homes, operand locations, edits, interference, clobbers, and safepoint roots after allocation.
  • verify_framed adds spill-slot placement, alignment, overlap, and frame-size checks.
  • verify_emission_input is the final target-independent gate immediately before encoding.

Verification validates the current snapshot; it does not permanently mark a mutable function or side table as verified. Run the appropriate verifier again after any later mutation.

#Unlinked code objects

Milky2018/vcode/code_object.build is the final reusable boundary between a target emitter and an embedding runtime. It copies the machine-code bytes and metadata, validates them, and returns an UnlinkedCodeObject only when all architecture, alignment, bounds, relocation, instruction-encoding, stack-map, root-location, and unwind-state contracts hold.

///|
let object = @code_object.build(@code_object.X64, [b'\xc3'])

Relocations remain symbolic. The package does not resolve runtime symbols, apply relocations, allocate executable memory, encode platform unwind formats, or register unwind data with the host. Those responsibilities belong to the embedding runtime. An unwind directive's offset is the code offset immediately after the prologue instruction that establishes the described state; saved register locations are relative to the canonical frame address.

#Integration guidance

  • Import the narrowest package that owns the contract you need. A frontend usually needs native_types and native_lowering; a target also needs the root package and code_object; a loader normally needs only native_types and code_object.
  • Keep target-specific instruction variants and ABI policy in the target module. The generic packages should never depend on AArch64, x64, or Wasmoon runtime layouts.
  • Keep runtime symbol resolution and executable-code installation outside this module. Code objects are ordinary verified data until an embedding installs them.
  • Treat verification errors as compiler contract failures with structured diagnostics. Do not bypass a failed stage to continue emission.

AllocationConstraint

A normalized register-allocation constraint.

Operand ties are represented separately by a shared nonnegative tie label, so this type contains only location constraints.

AllocationLocation

A packed allocation result location within one allocation session.

Spill indices are session-local and must be translated at ownership boundaries before they are exposed as product-facing stack-slot handles.

AllocationOperand

The canonical allocation facts for one instruction operand.

AllocationVirtualReg

using @Milky2018/vcode/allocation_types { type VirtualReg as AllocationVirtualReg }

A dense virtual-register identity within one allocation session.

OperandRole

Whether an allocation operand reads, writes, or both reads and writes its virtual register.

SSA VCode producers use Use and Def. UseDef remains available to standalone register-allocation clients that model in-place updates.

OperandTiming

The point within an instruction where an allocation operand is read or written.

PhysicalReg

A target physical register identified within one register class.

RegClass

Target-neutral register-class identity shared by VCode and register allocation.

FpVector preserves targets whose scalar floating-point and vector values occupy one physical register bank. Float and Vector remain available to standalone allocator embeddings that model distinct banks.

ValueType

Exact target-neutral value types shared by native lowering and target VCode.

Ptr64 is an untraced address or opaque handle. GcRef64 is a nullable managed reference and must be reported as a root at GC safepoints.

AllocationVerifyError

pub suberror AllocationVerifyError {
SelectedFailure(cause~ : VCodeVerifyError)
SourceMismatch
MissingValueLocation(value~ : Value)
ForeignLocation(value~ : Value)
LocationClassMismatch(value~ : Value)
MissingOperandLocation(instruction~ : Instruction, operand~ : Int)
OperandClassMismatch(instruction~ : Instruction, operand~ : Int)
RegisterConstraintViolation(instruction~ : Instruction, operand~ : Int)
FixedConstraintViolation(instruction~ : Instruction, operand~ : Int)
PreservedHomeConstraintViolation(instruction~ : Instruction, operand~ : Int)
TiedConstraintViolation(instruction~ : Instruction, operand~ : Int)
Interference(left~ : Value, right~ : Value, location~ : Location)
ClobberViolation(instruction~ : Instruction, value~ : Value)
InvalidStackSlotLayout(slot~ : StackSlot)
InvalidEdit(index~ : Int)
MissingReload(instruction~ : Instruction, operand~ : Int)
InvalidEdgeMove(index~ : Int)
MissingSafepointRoot(instruction~ : Instruction, value~ : Value)
UnexpectedSafepointRoot(instruction~ : Instruction, value~ : Value)
} derive(Eq,
Debug
)

AllocationVerifyError::equal

AllocationVerifyError::not_equal

AllocationVerifyError::output

fn AllocationVerifyError::output(self : AllocationVerifyError, logger : &Logger) -> Unit

AllocationVerifyError::to_string

fn AllocationVerifyError::to_string(self : AllocationVerifyError) -> String

CallTransferError

pub suberror CallTransferError {
InvalidSourceClass(index~ : Int)
ForeignSourceStack(index~ : Int)
InvalidSourceStackType(index~ : Int, expected~ :
ValueType
, actual~ :
ValueType
)
InvalidSourceStackLayout(index~ : Int)
InvalidDestinationClass(index~ : Int)
InvalidStackOffset(index~ : Int, offset~ : Int)
InvalidOutgoingStackRange(start~ : Int, size~ : Int)
OutgoingStackDestinationOutOfRange(index~ : Int, offset~ : Int)
DuplicateRegisterDestination(index~ : Int, register~ :
PhysicalReg
)
OverlappingStackDestination(first~ : Int, second~ : Int)
ProtectedLocationOverwrite(index~ : Int, location~ : Location)
MoveResolutionFailed(cause~ : MoveResolveError)
} derive(Eq,
Debug
)

CallTransferError::equal

CallTransferError::not_equal

fn CallTransferError::not_equal(x : CallTransferError, y : CallTransferError) -> Bool

EmissionVerifyError

pub suberror EmissionVerifyError {
FrameFailure(cause~ : FrameVerifyError)
} derive(Eq,
Debug
)

EmissionVerifyError::equal

EmissionVerifyError::not_equal

EmissionVerifyError::output

fn EmissionVerifyError::output(self : EmissionVerifyError, logger : &Logger) -> Unit

EmissionVerifyError::to_string

fn EmissionVerifyError::to_string(self : EmissionVerifyError) -> String

FrameVerifyError

pub suberror FrameVerifyError {
AllocationFailure(cause~ : AllocationVerifyError)
SourceMismatch
InvalidFrameSize(size~ : Int)
InvalidFrameAlignment(alignment~ : Int)
MissingStackSlot(slot~ : StackSlot)
MisalignedStackSlot(slot~ : StackSlot)
OverlappingStackSlots(left~ : StackSlot, right~ : StackSlot)
StackSlotOutOfFrame(slot~ : StackSlot)
} derive(Eq,
Debug
)

FrameVerifyError::equal

FrameVerifyError::not_equal

fn FrameVerifyError::not_equal(x : FrameVerifyError, y : FrameVerifyError) -> Bool

FrameVerifyError::output

fn FrameVerifyError::output(self : FrameVerifyError, logger : &Logger) -> Unit

FrameVerifyError::to_string

fn FrameVerifyError::to_string(self : FrameVerifyError) -> String

MoveResolveError

MoveResolveError::equal

MoveResolveError::not_equal

fn MoveResolveError::not_equal(x : MoveResolveError, y : MoveResolveError) -> Bool

MoveResolveError::output

fn MoveResolveError::output(self : MoveResolveError, logger : &Logger) -> Unit

MoveResolveError::to_string

fn MoveResolveError::to_string(self : MoveResolveError) -> String

VCodeBuildError

pub suberror VCodeBuildError {
InvalidParameter(index~ : Int)
InvalidBlockParameter(block~ : Block, index~ : Int)
ForeignBlock(block~ : Block)
ForeignValue(value~ : Value)
InvalidOperandConstraint
DuplicateTerminator(block~ : Block)
BlockAlreadyTerminated(block~ : Block)
InvalidEdgeArity(block~ : Block)
EdgeClassMismatch(block~ : Block, index~ : Int)
DuplicateClobber(reg~ :
PhysicalReg
)
InvalidSafepointRoot(value~ : Value)
InvalidStackMap
MissingTerminator(block~ : Block)
InvalidLayout
} derive(Eq,
Debug
)

VCodeBuildError::equal

VCodeBuildError::not_equal

fn VCodeBuildError::not_equal(x : VCodeBuildError, y : VCodeBuildError) -> Bool

VCodeBuildError::output

fn VCodeBuildError::output(self : VCodeBuildError, logger : &Logger) -> Unit

VCodeBuildError::to_string

fn VCodeBuildError::to_string(self : VCodeBuildError) -> String

VCodeVerifyError

pub suberror VCodeVerifyError {
EmptyFunction
MissingTerminator(block~ : Block)
ForeignValue(instruction~ : Instruction, value~ : Value)
ForeignBlock(instruction~ : Instruction, block~ : Block)
InvalidEdgeArity(instruction~ : Instruction, block~ : Block)
EdgeClassMismatch(instruction~ : Instruction, block~ : Block, index~ : Int)
InvalidTie(instruction~ : Instruction, operand~ : Int, tied_to~ : Int)
FixedRegisterClassMismatch(instruction~ : Instruction, operand~ : Int)
InvalidOperandPreference(instruction~ : Instruction, operand~ : Int)
DuplicateClobber(instruction~ : Instruction, reg~ :
PhysicalReg
)
InvalidSafepointRoot(instruction~ : Instruction, value~ : Value)
InvalidStackMap(instruction~ : Instruction)
UnreachableBlock(block~ : Block)
UseBeforeDefinition(instruction~ : Instruction, value~ : Value)
DefinitionDoesNotDominate(instruction~ : Instruction, value~ : Value)
InvalidLayout
} derive(Eq,
Debug
)

VCodeVerifyError::equal

VCodeVerifyError::not_equal

fn VCodeVerifyError::not_equal(x : VCodeVerifyError, y : VCodeVerifyError) -> Bool

VCodeVerifyError::output

fn VCodeVerifyError::output(self : VCodeVerifyError, logger : &Logger) -> Unit

VCodeVerifyError::to_string

fn VCodeVerifyError::to_string(self : VCodeVerifyError) -> String

Allocation

pub struct Allocation {
// private fields
}

Allocation::add_edit

fn Allocation::add_edit(self : Allocation, edit : Edit) -> Bool

Allocation::add_safepoint_root

fn Allocation::add_safepoint_root(self : Allocation, instruction : Instruction, value : Value, location : Location) -> Bool

Allocation::allocated_operand_location_at

fn Allocation::allocated_operand_location_at(self : Allocation, instruction : Int, operand : Int) ->
AllocationLocation
?

Return a canonical allocation location by dense instruction and operand indices. Spill indices belong to this allocation.

Allocation::allocated_value_location_at

Return a canonical allocation location by dense value index.

Spill indices belong to this allocation and must not be used as public stack-slot handles. Compiler infrastructure that needs such a handle must call stack_slot_at on the same allocation.

Allocation::assign_operand

fn Allocation::assign_operand(self : Allocation, instruction : Instruction, operand_index : Int, reg :
PhysicalReg
) -> Bool

Allocation::assign_operand_location

fn Allocation::assign_operand_location(self : Allocation, instruction : Instruction, operand_index : Int, location : Location) -> Bool

Allocation::assign_value

fn Allocation::assign_value(self : Allocation, value : Value, location : Location) -> Bool

Allocation::create_stack_slot

fn Allocation::create_stack_slot(self : Allocation, ty :
ValueType
, size : Int, alignment : Int) -> StackSlot

Allocation::edge_edits_at

fn Allocation::edge_edits_at(self : Allocation, source : Block, successor_index : Int) -> ArrayView[Edit]

Returns the allocation edits scheduled for one outgoing CFG edge.

The returned view is read-only and remains valid while this allocation is not mutated. Target emitters should query this index instead of scanning edits() for every edge.

Allocation::edit_count

fn Allocation::edit_count(self : Allocation) -> Int

Allocation::edits

fn Allocation::edits(self : Allocation) -> Array[Edit]

Allocation::edits_at

fn Allocation::edits_at(self : Allocation, instruction : Instruction, placement : PointPlacement) -> ArrayView[Edit]

Returns the allocation edits scheduled at one instruction point.

The returned view is read-only and remains valid while this allocation is not mutated. Target emitters should query this index instead of scanning edits() for every instruction.

Allocation::for_function

fn[Inst] Allocation::for_function(function : Function[Inst]) -> Allocation

Allocation::operand_location

fn Allocation::operand_location(self : Allocation, instruction : Instruction, operand_index : Int) -> Location?

Allocation::safepoint_roots

fn Allocation::safepoint_roots(self : Allocation, instruction : Instruction) -> Array[(Value, Location)]

Allocation::source_instruction_count

fn Allocation::source_instruction_count(self : Allocation) -> Int

Allocation::stack_slot_alignment

fn Allocation::stack_slot_alignment(self : Allocation, slot : StackSlot) -> Int?

Allocation::stack_slot_at

fn Allocation::stack_slot_at(self : Allocation, index : Int) -> StackSlot?

Allocation::stack_slot_count

fn Allocation::stack_slot_count(self : Allocation) -> Int

Allocation::stack_slot_size

fn Allocation::stack_slot_size(self : Allocation, slot : StackSlot) -> Int?

Allocation::stack_slot_type

Allocation::statistics

fn Allocation::statistics(self : Allocation) -> AllocationStatistics

Allocation::summary

fn Allocation::summary(self : Allocation) -> String

Allocation::value_location

fn Allocation::value_location(self : Allocation, value : Value) -> Location?

Return the value's default transfer home.

A verified segmented allocation may keep the newest value in an instruction- or edge-specific location until an edit returns it here.

AllocationBuilder

pub struct AllocationBuilder[Inst] {
// private fields
}

Dense-index writer used after a verified register-allocation plan has been produced for this exact function.

The writer avoids reconstructing and revalidating owner-tagged handles for every value, operand, and edit. Indices must come from the matching function and stay within its published counts. Final allocation invariants remain the authoritative validation boundary.

AllocationBuilder::add_edge_transfer

fn[Inst] AllocationBuilder::add_edge_transfer(self : AllocationBuilder[Inst], source_block : Int, successor : Int, value : Int, from :
AllocationLocation
, to :
AllocationLocation
) -> Bool

AllocationBuilder::add_function_safepoint_roots

fn[Inst] AllocationBuilder::add_function_safepoint_roots(self : AllocationBuilder[Inst]) -> Bool

Adds every selected VCode safepoint root through dense internal ids.

Selected-function validation already established metadata ownership and root types; this method retains allocation-location checks without copying layouts, instruction metadata, or owner-tagged root snapshots.

AllocationBuilder::add_safepoint_root

fn[Inst] AllocationBuilder::add_safepoint_root(self : AllocationBuilder[Inst], instruction : Int, value : Int) -> Bool

AllocationBuilder::add_transfer

AllocationBuilder::assign_operand

fn[Inst] AllocationBuilder::assign_operand(self : AllocationBuilder[Inst], instruction : Int, operand : Int, location :
AllocationLocation
) -> Bool

AllocationBuilder::assign_value

fn[Inst] AllocationBuilder::assign_value(self : AllocationBuilder[Inst], value : Int, location :
AllocationLocation
) -> Bool

AllocationBuilder::create_stack_slot

fn[Inst] AllocationBuilder::create_stack_slot(self : AllocationBuilder[Inst], value : Int, size : Int, alignment : Int) -> Int

AllocationBuilder::finish

fn[Inst] AllocationBuilder::finish(self : AllocationBuilder[Inst]) -> Allocation

AllocationBuilder::from_plan_storage

fn[Inst] AllocationBuilder::from_plan_storage(function : Function[Inst], value_locations : Array[
AllocationLocation
?], operand_locations : Array[Array[
AllocationLocation
?]], spill_owners : Array[Int], spill_sizes : Array[Int], spill_alignments : Array[Int]) -> AllocationBuilder[Inst]?

Adopts canonical location tables produced for this exact selected function.

The caller must stop mutating the source tables after adoption. Shape, location class, spill ownership, size, and alignment are checked here so callers that disable the final allocation verifier retain the same local construction guarantees as incremental builder writes.

AllocationBuilder::new

fn[Inst] AllocationBuilder::new(function : Function[Inst]) -> AllocationBuilder[Inst]

AllocationBuilder::operand_value_at

fn[Inst] AllocationBuilder::operand_value_at(self : AllocationBuilder[Inst], instruction : Int, operand : Int) -> Int

AllocationBuilder::value_location

AllocationBuilder::value_type_at

fn[Inst] AllocationBuilder::value_type_at(self : AllocationBuilder[Inst], value : Int) ->
ValueType

AllocationStatistics

pub struct AllocationStatistics {
spill_slots : Int
spills : Int
reloads : Int
reg_moves : Int
spill_to_spill : Int
} derive(Eq,
Debug
)

Read-only summary of the transfers introduced by register allocation.

AllocationStatistics::equal

AllocationStatistics::not_equal

Block

pub struct Block {
// private fields
}

impl Eq for Block
impl Show for Block

Block::equal

fn Block::equal(self : Block, other : Block) -> Bool

Block::not_equal

fn Block::not_equal(x : Block, y : Block) -> Bool

Block::output

fn Block::output(self : Block, logger : &Logger) -> Unit

Block::to_repr

Block::to_string

fn Block::to_string(self : Block) -> String

Builder

pub struct Builder[Inst] {
// private fields
}

Builder::append_body

fn[Inst] Builder::append_body(self : Builder[Inst], block : Block, inst : Inst, inputs : Array[Input], outputs : Array[Output], clobbers : Array[
PhysicalReg
], metadata : InstructionMetadata) -> (Instruction, Array[Value]) raise VCodeBuildError

Builder::block_parameter

fn[Inst] Builder::block_parameter(self : Builder[Inst], block : Block, index : Int) -> Value raise VCodeBuildError

Builder::create_block

fn[Inst] Builder::create_block(self : Builder[Inst], parameter_types : Array[
ValueType
]) -> Block

Builder::entry_block

fn[Inst] Builder::entry_block(self : Builder[Inst]) -> Block

Builder::finish

fn[Inst] Builder::finish(self : Builder[Inst]) -> Function[Inst]

Builder::new

fn[Inst] Builder::new(name : String, parameter_types : Array[
ValueType
]) -> Builder[Inst]

Builder::new_with_protocol

Builder::new_with_results

fn[Inst] Builder::new_with_results(name : String, parameter_types : Array[
ValueType
], result_types : Array[
ValueType
]) -> Builder[Inst]

Builder::parameter

fn[Inst] Builder::parameter(self : Builder[Inst], index : Int) -> Value raise VCodeBuildError

Builder::set_terminator

fn[Inst] Builder::set_terminator(self : Builder[Inst], block : Block, inst : Inst, inputs : Array[Input], successors : Array[Edge], clobbers : Array[
PhysicalReg
], metadata : InstructionMetadata) -> Instruction raise VCodeBuildError

CallTransfer

pub struct CallTransfer {
// private fields
}

One allocated source and its physical destination in a target call layout.

CallTransfer::to_stack

CallTransferPlan

pub struct CallTransferPlan {
stack_transfers : Array[StackArgumentTransfer]
register_moves : Array[ParallelMove]
} derive(Eq,
Debug
)

CallTransferPlan::equal

CallTransferPlan::not_equal

fn CallTransferPlan::not_equal(x : CallTransferPlan, y : CallTransferPlan) -> Bool

CheckedBuilder

pub struct CheckedBuilder[Inst] {
// private fields
}

A production builder that establishes local VCode invariants before every mutation. The permissive Builder remains available for external tools and verifier negative tests; target selectors use this type so sealing does not need to rediscover local facts by scanning the completed instruction list.

CheckedBuilder::append_body

fn[Inst] CheckedBuilder::append_body(self : CheckedBuilder[Inst], block : Block, inst : Inst, inputs : Array[Input], outputs : Array[Output], clobbers : Array[
PhysicalReg
], metadata : InstructionMetadata) -> (Instruction, Array[Value]) raise VCodeBuildError

CheckedBuilder::block_parameter

fn[Inst] CheckedBuilder::block_parameter(self : CheckedBuilder[Inst], block : Block, index : Int) -> Value raise VCodeBuildError

CheckedBuilder::create_block

fn[Inst] CheckedBuilder::create_block(self : CheckedBuilder[Inst], parameter_types : Array[
ValueType
]) -> Block

CheckedBuilder::entry_block

fn[Inst] CheckedBuilder::entry_block(self : CheckedBuilder[Inst]) -> Block

CheckedBuilder::finish

fn[Inst] CheckedBuilder::finish(self : CheckedBuilder[Inst]) -> Function[Inst] raise VCodeBuildError

CheckedBuilder::new

fn[Inst] CheckedBuilder::new(name : String, parameter_types : Array[
ValueType
]) -> CheckedBuilder[Inst]

CheckedBuilder::new_with_protocol

CheckedBuilder::new_with_results

fn[Inst] CheckedBuilder::new_with_results(name : String, parameter_types : Array[
ValueType
], result_types : Array[
ValueType
]) -> CheckedBuilder[Inst]

CheckedBuilder::parameter

fn[Inst] CheckedBuilder::parameter(self : CheckedBuilder[Inst], index : Int) -> Value raise VCodeBuildError

CheckedBuilder::set_terminator

fn[Inst] CheckedBuilder::set_terminator(self : CheckedBuilder[Inst], block : Block, inst : Inst, inputs : Array[Input], successors : Array[Edge], clobbers : Array[
PhysicalReg
], metadata : InstructionMetadata) -> Instruction raise VCodeBuildError

Edge

pub struct Edge {
target : Block
arguments : Array[Value]
}

Edge::new

fn Edge::new(target : Block, arguments : Array[Value]) -> Edge

Edit

pub struct Edit {
point : ProgramPoint?
kind : EditKind
}

Edit::edge_move

fn Edit::edge_move(source : Block, successor_index : Int, value : Value, from : Location, to : Location) -> Edit

Edit::kind

fn Edit::kind(self : Edit) -> EditKind

Edit::point

fn Edit::point(self : Edit) -> ProgramPoint?

Edit::reload

EditKind

pub(all) enum EditKind {
Spill(value~ : Value, reg~ :
PhysicalReg
, slot~ : StackSlot)
Reload(value~ : Value, slot~ : StackSlot, reg~ :
PhysicalReg
)
Move(value~ : Value, from~ :
PhysicalReg
, to~ :
PhysicalReg
)
EdgeMove(source~ : Block, successor_index~ : Int, value~ : Value, from~ : Location, to~ : Location)
} derive(Eq,
Debug
)

EditKind::equal

fn EditKind::equal(EditKind, EditKind) -> Bool

EditKind::not_equal

fn EditKind::not_equal(x : EditKind, y : EditKind) -> Bool

EditKind::to_repr

FrameLayout

pub struct FrameLayout {
// private fields
}

FrameLayout::alignment

fn FrameLayout::alignment(self : FrameLayout) -> Int

FrameLayout::frame_size

fn FrameLayout::frame_size(self : FrameLayout) -> Int

FrameLayout::new

fn[Inst] FrameLayout::new(function : Function[Inst], allocation : Allocation, frame_size : Int, alignment : Int) -> FrameLayout

FrameLayout::place_slot

fn FrameLayout::place_slot(self : FrameLayout, slot : StackSlot, offset : Int) -> Bool

FrameLayout::slot_offset

fn FrameLayout::slot_offset(self : FrameLayout, slot : StackSlot) -> Int?

Function

pub struct Function[Inst] {
// private fields
}

Function::after

fn[Inst] Function::after(self : Function[Inst], instruction : Instruction) -> ProgramPoint?

Function::allocation_block_instruction_at

fn[Inst] Function::allocation_block_instruction_at(self : Function[Inst], block : Int, instruction : Int) -> Int

Function::allocation_block_instruction_count

fn[Inst] Function::allocation_block_instruction_count(self : Function[Inst], block : Int) -> Int

Function::allocation_block_parameter_at

fn[Inst] Function::allocation_block_parameter_at(self : Function[Inst], block : Int, parameter : Int) ->
VirtualReg

Function::allocation_block_parameter_count

fn[Inst] Function::allocation_block_parameter_count(self : Function[Inst], block : Int) -> Int

Function::allocation_block_successor_at

fn[Inst] Function::allocation_block_successor_at(self : Function[Inst], block : Int, successor : Int) -> Int

Function::allocation_block_successor_count

fn[Inst] Function::allocation_block_successor_count(self : Function[Inst], block : Int) -> Int

Function::allocation_edge_argument_at

fn[Inst] Function::allocation_edge_argument_at(self : Function[Inst], block : Int, successor : Int, argument : Int) ->
VirtualReg

Function::allocation_edge_argument_count

fn[Inst] Function::allocation_edge_argument_count(self : Function[Inst], block : Int, successor : Int) -> Int

Function::allocation_entry_value_at

fn[Inst] Function::allocation_entry_value_at(self : Function[Inst], parameter : Int) ->
VirtualReg

Returns a function parameter as a canonical allocation virtual register.

Function::allocation_instruction_clobbers

fn[Inst] Function::allocation_instruction_clobbers(self : Function[Inst], instruction : Int) -> ArrayView[
PhysicalReg
]

Function::allocation_instruction_operands

fn[Inst] Function::allocation_instruction_operands(self : Function[Inst], instruction : Int) -> ArrayView[
AllocationOperand
]

Function::allocation_layout_block_id_at

fn[Inst] Function::allocation_layout_block_id_at(self : Function[Inst], block : Int) -> Int

Returns the stable block id at a valid dense layout index.

Function::allocation_value_type_at

fn[Inst] Function::allocation_value_type_at(self : Function[Inst], value : Int) ->
ValueType

Returns the allocation type for a valid dense value id.

Allocation consumers call these indexed accessors only after selected VCode validation, so invalid indices are programmer errors.

Function::allocation_vreg

fn[Inst] Function::allocation_vreg(self : Function[Inst], value : Value) ->
VirtualReg
?

Function::before

fn[Inst] Function::before(self : Function[Inst], instruction : Instruction) -> ProgramPoint?

Function::block_at

fn[Inst] Function::block_at(self : Function[Inst], index : Int) -> Block?

Function::block_body

fn[Inst] Function::block_body(self : Function[Inst], block : Block) -> Array[Instruction]

Function::block_count

fn[Inst] Function::block_count(self : Function[Inst]) -> Int

Function::block_index

fn[Inst] Function::block_index(self : Function[Inst], block : Block) -> Int?

Function::block_instruction_at

fn[Inst] Function::block_instruction_at(self : Function[Inst], block : Block, index : Int) -> Instruction?

Returns a body instruction or the block terminator by linear block index.

Function::block_instruction_count

fn[Inst] Function::block_instruction_count(self : Function[Inst], block : Block) -> Int

Number of instructions in a block, including its terminator when present.

Function::block_parameter_at

fn[Inst] Function::block_parameter_at(self : Function[Inst], block : Block, index : Int) -> Value?

Function::block_parameter_count

fn[Inst] Function::block_parameter_count(self : Function[Inst], block : Block) -> Int

Function::block_parameters

fn[Inst] Function::block_parameters(self : Function[Inst], block : Block) -> Array[Value]

Function::block_terminator

fn[Inst] Function::block_terminator(self : Function[Inst], block : Block) -> Instruction?

Function::entry_block

fn[Inst] Function::entry_block(self : Function[Inst]) -> Block

Function::instruction

fn[Inst] Function::instruction(self : Function[Inst], instruction : Instruction) -> Inst?

Function::instruction_allocation_operand_at

fn[Inst] Function::instruction_allocation_operand_at(self : Function[Inst], instruction : Instruction, index : Int) ->
AllocationOperand
?

Function::instruction_at

fn[Inst] Function::instruction_at(self : Function[Inst], index : Int) -> Instruction?

Function::instruction_clobber_at

fn[Inst] Function::instruction_clobber_at(self : Function[Inst], instruction : Instruction, index : Int) ->
PhysicalReg
?

Function::instruction_clobber_count

fn[Inst] Function::instruction_clobber_count(self : Function[Inst], instruction : Instruction) -> Int

Function::instruction_clobbers

fn[Inst] Function::instruction_clobbers(self : Function[Inst], instruction : Instruction) -> Array[
PhysicalReg
]

Function::instruction_count

fn[Inst] Function::instruction_count(self : Function[Inst]) -> Int

Function::instruction_index

fn[Inst] Function::instruction_index(self : Function[Inst], instruction : Instruction) -> Int?

Function::instruction_is_terminator

fn[Inst] Function::instruction_is_terminator(self : Function[Inst], instruction : Instruction) -> Bool

Function::instruction_metadata

fn[Inst] Function::instruction_metadata(self : Function[Inst], instruction : Instruction) -> InstructionMetadata?

Function::instruction_operand_at

fn[Inst] Function::instruction_operand_at(self : Function[Inst], instruction : Instruction, index : Int) -> Operand?

Function::instruction_operand_count

fn[Inst] Function::instruction_operand_count(self : Function[Inst], instruction : Instruction) -> Int

Function::instruction_operands

fn[Inst] Function::instruction_operands(self : Function[Inst], instruction : Instruction) -> Array[Operand]

Function::instruction_results

fn[Inst] Function::instruction_results(self : Function[Inst], instruction : Instruction) -> Array[Value]

Function::instruction_successor_argument_at

fn[Inst] Function::instruction_successor_argument_at(self : Function[Inst], instruction : Instruction, successor : Int, argument : Int) -> Value?

Function::instruction_successor_argument_count

fn[Inst] Function::instruction_successor_argument_count(self : Function[Inst], instruction : Instruction, successor : Int) -> Int

Function::instruction_successor_at

fn[Inst] Function::instruction_successor_at(self : Function[Inst], instruction : Instruction, index : Int) -> Edge?

Function::instruction_successor_count

fn[Inst] Function::instruction_successor_count(self : Function[Inst], instruction : Instruction) -> Int

Function::instruction_successor_target

fn[Inst] Function::instruction_successor_target(self : Function[Inst], instruction : Instruction, successor : Int) -> Block?

Function::instruction_successors

fn[Inst] Function::instruction_successors(self : Function[Inst], instruction : Instruction) -> Array[Edge]

Function::layout

fn[Inst] Function::layout(self : Function[Inst]) -> Array[Block]

Function::layout_block_at

fn[Inst] Function::layout_block_at(self : Function[Inst], index : Int) -> Block?

Function::name

fn[Inst] Function::name(self : Function[Inst]) -> String

Function::parameter_at

fn[Inst] Function::parameter_at(self : Function[Inst], index : Int) -> Value?

Function::parameter_count

fn[Inst] Function::parameter_count(self : Function[Inst]) -> Int

Function::program_point_instruction

fn[Inst] Function::program_point_instruction(self : Function[Inst], point : ProgramPoint) -> Instruction?

Function::protocol

Function::result_types

Function::set_layout

fn[Inst] Function::set_layout(self : Function[Inst], layout : Array[Block]) -> Unit raise VCodeBuildError

Function::summary

fn[Inst :
Debug
] Function::summary(self : Function[Inst]) -> String

Function::value_at

fn[Inst] Function::value_at(self : Function[Inst], index : Int) -> Value?

Function::value_count

fn[Inst] Function::value_count(self : Function[Inst]) -> Int

Function::value_index

fn[Inst] Function::value_index(self : Function[Inst], value : Value) -> Int?

Function::value_type

fn[Inst] Function::value_type(self : Function[Inst], value : Value) ->
ValueType
?

Input

Input::any

fn Input::any(value : Value) -> Input

Input::any_location

fn Input::any_location(value : Value) -> Input

Keep an input in its allocated register or spill slot. This is intended for target operations, such as ABI argument setup, whose emitter can consume a stack-resident value directly instead of requiring every input in a register at the same program point.

Input::with_preference

fn Input::with_preference(self : Input, preference :
PhysicalReg
) -> Input

Prefer a physical register without making it an allocation constraint.

Input::with_timing

Instruction

pub struct Instruction {
// private fields
}

impl Eq for Instruction
impl Show for Instruction

Instruction::equal

fn Instruction::equal(self : Instruction, other : Instruction) -> Bool

Instruction::not_equal

fn Instruction::not_equal(x : Instruction, y : Instruction) -> Bool

Instruction::output

fn Instruction::output(self : Instruction, logger : &Logger) -> Unit

Instruction::to_repr

Instruction::to_string

fn Instruction::to_string(self : Instruction) -> String

Location

pub(all) enum Location {
Register(
PhysicalReg
)
Stack(StackSlot)
} derive(Eq)

Location::equal

fn Location::equal(Location, Location) -> Bool

Location::not_equal

fn Location::not_equal(x : Location, y : Location) -> Bool

Location::to_repr

OperandConstraint

pub(all) enum OperandConstraint {
Any
AnyLocation
Fixed(
PhysicalReg
)
TiedTo(Int)
} derive(Eq,
Debug
)

OperandConstraint::equal

OperandConstraint::not_equal

fn OperandConstraint::not_equal(x : OperandConstraint, y : OperandConstraint) -> Bool

Output

Output::any_location

Materialize the result directly in its stable register or stack home. This is intended for target ABI pseudos whose emitter owns the transfer from an implicit incoming location.

Output::tied

fn Output::tied(ty :
ValueType
, operand_index : Int) -> Output

Output::with_preference

Prefer a physical register without making it an allocation constraint.

Output::with_timing

ParallelMove

ParallelMove::equal

ParallelMove::not_equal

fn ParallelMove::not_equal(x : ParallelMove, y : ParallelMove) -> Bool

PointPlacement

pub(all) enum PointPlacement {
Before
After
} derive(Eq,
Debug
)

PointPlacement::equal

PointPlacement::not_equal

fn PointPlacement::not_equal(x : PointPlacement, y : PointPlacement) -> Bool

ProgramPoint

pub struct ProgramPoint {
// private fields
}

impl Eq for ProgramPoint

ProgramPoint::equal

fn ProgramPoint::equal(self : ProgramPoint, other : ProgramPoint) -> Bool

ProgramPoint::instruction

fn ProgramPoint::instruction(self : ProgramPoint) -> Instruction

ProgramPoint::not_equal

fn ProgramPoint::not_equal(x : ProgramPoint, y : ProgramPoint) -> Bool

ProgramPoint::placement

fn ProgramPoint::placement(self : ProgramPoint) -> PointPlacement

ProgramPoint::to_repr

RegallocLoopStatistics

pub(all) struct RegallocLoopStatistics {
queue_pops : Int
register_probes : Int
occupied_segments_scanned : Int
conflicts : Int
evictions : Int
bundle_splits : Int
second_chance_attempts : Int
max_queue_length : Int
} derive(Eq,
Debug
)

Deterministic counters for the allocator's bundle-probing loop.

RegallocLoopStatistics::equal

RegallocLoopStatistics::not_equal

ResolvedCallTransferPlan

pub struct ResolvedCallTransferPlan {
stack_transfers : Array[StackArgumentTransfer]
register_moves : ResolvedMovePlan
} derive(Eq,
Debug
)

ResolvedCallTransferPlan::equal

ResolvedCallTransferPlan::not_equal

ResolvedMovePlan

pub struct ResolvedMovePlan {
steps : Array[ResolvedMoveStep]
requires_emergency : Bool
} derive(Eq,
Debug
)

ResolvedMovePlan::empty

ResolvedMovePlan::equal

ResolvedMovePlan::not_equal

fn ResolvedMovePlan::not_equal(x : ResolvedMovePlan, y : ResolvedMovePlan) -> Bool

ResolvedMoveStep

ResolvedMoveStep::equal

ResolvedMoveStep::not_equal

fn ResolvedMoveStep::not_equal(x : ResolvedMoveStep, y : ResolvedMoveStep) -> Bool

StackArgumentTransfer

StackArgumentTransfer::equal

StackArgumentTransfer::not_equal

StackSlot

pub struct StackSlot {
// private fields
}

impl Eq for StackSlot

StackSlot::equal

fn StackSlot::equal(self : StackSlot, other : StackSlot) -> Bool

StackSlot::not_equal

fn StackSlot::not_equal(x : StackSlot, y : StackSlot) -> Bool

StackSlot::to_repr

TargetCompileEvent

pub(all) enum TargetCompileEvent {
TargetAnalysisStarted
TargetConstructionStarted
TargetValidationStarted
TargetCommonValidationStarted
TargetIsaValidationStarted
TargetSealingStarted
TargetSelectionFinished
RegallocStarted
RegallocPhaseStarted(String)
RegallocPhasesFinished
RegallocLoopMeasured(RegallocLoopStatistics)
RegallocFinished(AllocationStatistics)
FramePlanningStarted
FramePlanningFinished
EmissionStarted
EmissionFinished
} derive(Eq,
Debug
)

Target-compilation boundaries exposed to an embedding-owned observer.

The observer must not mutate compiler inputs from inside the callback.

TargetCompileEvent::equal

TargetCompileEvent::not_equal

Value

pub struct Value {
// private fields
}

impl Eq for Value
impl Show for Value

Value::equal

fn Value::equal(self : Value, other : Value) -> Bool

Value::not_equal

fn Value::not_equal(x : Value, y : Value) -> Bool

Value::output

fn Value::output(self : Value, logger : &Logger) -> Unit

Value::to_repr

Value::to_string

fn Value::to_string(self : Value) -> String

plan_call_transfers

fn plan_call_transfers(allocation : Allocation, transfers : Array[CallTransfer], outgoing_stack_start : Int, outgoing_stack_size : Int, scratch_int : Array[
PhysicalReg
], scratch_fp : Array[
PhysicalReg
], protected_locations : Array[Location]) -> CallTransferPlan raise CallTransferError

plan_resolved_call_transfers

fn plan_resolved_call_transfers(allocation : Allocation, transfers : Array[CallTransfer], outgoing_stack_start : Int, outgoing_stack_size : Int, stack_scratch_int : Array[
PhysicalReg
], stack_scratch_fp : Array[
PhysicalReg
], move_scratch_int :
PhysicalReg
, move_scratch_fp :
PhysicalReg
, protected_locations : Array[Location]) -> ResolvedCallTransferPlan raise CallTransferError

verify_allocated

fn[Inst] verify_allocated(function : Function[Inst], allocation : Allocation) -> Unit raise AllocationVerifyError

verify_allocation_invariants

fn[Inst] verify_allocation_invariants(function : Function[Inst], allocation : Allocation) -> Unit raise AllocationVerifyError

Verifies allocation-specific invariants for VCode that has already passed verify_selected. The caller must not mutate function between the two checks.

verify_emission_input

fn[Inst] verify_emission_input(function : Function[Inst], allocation : Allocation, frame : FrameLayout) -> Unit raise EmissionVerifyError

verify_framed

fn[Inst] verify_framed(function : Function[Inst], allocation : Allocation, frame : FrameLayout) -> Unit raise FrameVerifyError

verify_selected

fn[Inst] verify_selected(function : Function[Inst]) -> Unit raise VCodeVerifyError