/* * The fellowing license applies to this file, which derives many * details (register and constraint definitions, for example) from the * files `BacktrackingAllocator.h`, `BacktrackingAllocator.cpp`, * `LIR.h`, and possibly definitions in other related files in * `js/src/jit/`: * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![allow(dead_code)] pub mod bitvec; pub mod cfg; pub mod domtree; pub mod ion; pub mod moves; pub mod postorder; pub mod ssa; #[macro_use] pub mod index; pub use index::{Block, Inst, InstRange, InstRangeIter}; pub mod checker; #[cfg(feature = "fuzzing")] pub mod fuzzing; /// Register classes. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum RegClass { Int = 0, Float = 1, } /// A physical register. Contains a physical register number and a class. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PReg(u8, RegClass); impl PReg { pub const MAX_BITS: usize = 5; pub const MAX: usize = (1 << Self::MAX_BITS) - 1; pub const MAX_INDEX: usize = 2 * Self::MAX; // including RegClass bit /// Create a new PReg. The `hw_enc` range is 6 bits. #[inline(always)] pub fn new(hw_enc: usize, class: RegClass) -> Self { assert!(hw_enc <= Self::MAX); PReg(hw_enc as u8, class) } /// The physical register number, as encoded by the ISA for the particular register class. #[inline(always)] pub fn hw_enc(self) -> usize { self.0 as usize } /// The register class. #[inline(always)] pub fn class(self) -> RegClass { self.1 } /// Get an index into the (not necessarily contiguous) index space of /// all physical registers. Allows one to maintain an array of data for /// all PRegs and index it efficiently. #[inline(always)] pub fn index(self) -> usize { ((self.1 as u8 as usize) << 5) | (self.0 as usize) } #[inline(always)] pub fn from_index(index: usize) -> Self { let class = (index >> 5) & 1; let class = match class { 0 => RegClass::Int, 1 => RegClass::Float, _ => unreachable!(), }; let index = index & Self::MAX; PReg::new(index, class) } #[inline(always)] pub fn invalid() -> Self { PReg::new(Self::MAX, RegClass::Int) } } impl std::fmt::Debug for PReg { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "PReg(hw = {}, class = {:?}, index = {})", self.hw_enc(), self.class(), self.index() ) } } impl std::fmt::Display for PReg { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let class = match self.class() { RegClass::Int => "i", RegClass::Float => "f", }; write!(f, "p{}{}", self.hw_enc(), class) } } /// A virtual register. Contains a virtual register number and a class. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct VReg(u32); impl VReg { pub const MAX_BITS: usize = 20; pub const MAX: usize = (1 << Self::MAX_BITS) - 1; #[inline(always)] pub fn new(virt_reg: usize, class: RegClass) -> Self { assert!(virt_reg <= Self::MAX); VReg(((virt_reg as u32) << 1) | (class as u8 as u32)) } #[inline(always)] pub fn vreg(self) -> usize { (self.0 >> 1) as usize } #[inline(always)] pub fn class(self) -> RegClass { match self.0 & 1 { 0 => RegClass::Int, 1 => RegClass::Float, _ => unreachable!(), } } #[inline(always)] pub fn invalid() -> Self { VReg::new(Self::MAX, RegClass::Int) } } impl std::fmt::Debug for VReg { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "VReg(vreg = {}, class = {:?})", self.vreg(), self.class() ) } } impl std::fmt::Display for VReg { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "v{}", self.vreg()) } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SpillSlot(u32); impl SpillSlot { #[inline(always)] pub fn new(slot: usize, class: RegClass) -> Self { assert!(slot < (1 << 24)); SpillSlot((slot as u32) | (class as u8 as u32) << 24) } #[inline(always)] pub fn index(self) -> usize { (self.0 & 0x00ffffff) as usize } #[inline(always)] pub fn class(self) -> RegClass { match (self.0 >> 24) as u8 { 0 => RegClass::Int, 1 => RegClass::Float, _ => unreachable!(), } } #[inline(always)] pub fn plus(self, offset: usize) -> Self { SpillSlot::new(self.index() + offset, self.class()) } } impl std::fmt::Display for SpillSlot { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "stack{}", self.index()) } } /// An `Operand` encodes everything about a mention of a register in /// an instruction: virtual register number, and any constraint/policy /// that applies to the register at this program point. /// /// An Operand may be a use or def (this corresponds to `LUse` and /// `LAllocation` in Ion). #[derive(Clone, Copy, PartialEq, Eq)] pub struct Operand { /// Bit-pack into 32 bits. Note that `policy` overlaps with `kind` /// in `Allocation` and we use mutually disjoint tag-value ranges /// so that clients, if they wish, can track just one `u32` per /// register slot and edit it in-place after allocation. /// /// policy:3 kind:1 pos:2 class:1 preg:5 vreg:20 bits: u32, } impl Operand { #[inline(always)] pub fn new(vreg: VReg, policy: OperandPolicy, kind: OperandKind, pos: OperandPos) -> Self { let (preg_field, policy_field): (u32, u32) = match policy { OperandPolicy::Any => (0, 0), OperandPolicy::Reg => (0, 1), OperandPolicy::Stack => (0, 2), OperandPolicy::FixedReg(preg) => { assert_eq!(preg.class(), vreg.class()); (preg.hw_enc() as u32, 3) } OperandPolicy::Reuse(which) => { assert!(which <= PReg::MAX); (which as u32, 4) } }; let class_field = vreg.class() as u8 as u32; let pos_field = pos as u8 as u32; let kind_field = kind as u8 as u32; Operand { bits: vreg.vreg() as u32 | (preg_field << 20) | (class_field << 25) | (pos_field << 26) | (kind_field << 28) | (policy_field << 29), } } #[inline(always)] pub fn reg_use(vreg: VReg) -> Self { Operand::new( vreg, OperandPolicy::Reg, OperandKind::Use, OperandPos::Before, ) } #[inline(always)] pub fn reg_use_at_end(vreg: VReg) -> Self { Operand::new(vreg, OperandPolicy::Reg, OperandKind::Use, OperandPos::Both) } #[inline(always)] pub fn reg_def(vreg: VReg) -> Self { Operand::new( vreg, OperandPolicy::Reg, OperandKind::Def, OperandPos::After, ) } #[inline(always)] pub fn reg_def_at_start(vreg: VReg) -> Self { Operand::new(vreg, OperandPolicy::Reg, OperandKind::Def, OperandPos::Both) } #[inline(always)] pub fn reg_temp(vreg: VReg) -> Self { Operand::new(vreg, OperandPolicy::Reg, OperandKind::Def, OperandPos::Both) } #[inline(always)] pub fn reg_reuse_def(vreg: VReg, idx: usize) -> Self { Operand::new( vreg, OperandPolicy::Reuse(idx), OperandKind::Def, OperandPos::Both, ) } #[inline(always)] pub fn reg_fixed_use(vreg: VReg, preg: PReg) -> Self { Operand::new( vreg, OperandPolicy::FixedReg(preg), OperandKind::Use, OperandPos::Before, ) } #[inline(always)] pub fn reg_fixed_def(vreg: VReg, preg: PReg) -> Self { Operand::new( vreg, OperandPolicy::FixedReg(preg), OperandKind::Def, OperandPos::After, ) } #[inline(always)] pub fn vreg(self) -> VReg { let vreg_idx = ((self.bits as usize) & VReg::MAX) as usize; VReg::new(vreg_idx, self.class()) } #[inline(always)] pub fn class(self) -> RegClass { let class_field = (self.bits >> 25) & 1; match class_field { 0 => RegClass::Int, 1 => RegClass::Float, _ => unreachable!(), } } #[inline(always)] pub fn kind(self) -> OperandKind { let kind_field = (self.bits >> 28) & 1; match kind_field { 0 => OperandKind::Def, 1 => OperandKind::Use, _ => unreachable!(), } } #[inline(always)] pub fn pos(self) -> OperandPos { let pos_field = (self.bits >> 26) & 3; match pos_field { 0 => OperandPos::Before, 1 => OperandPos::After, 2 => OperandPos::Both, _ => unreachable!(), } } #[inline(always)] pub fn policy(self) -> OperandPolicy { let policy_field = (self.bits >> 29) & 7; let preg_field = ((self.bits >> 20) as usize) & PReg::MAX; match policy_field { 0 => OperandPolicy::Any, 1 => OperandPolicy::Reg, 2 => OperandPolicy::Stack, 3 => OperandPolicy::FixedReg(PReg::new(preg_field, self.class())), 4 => OperandPolicy::Reuse(preg_field), _ => unreachable!(), } } #[inline(always)] pub fn bits(self) -> u32 { self.bits } #[inline(always)] pub fn from_bits(bits: u32) -> Self { debug_assert!(bits >> 29 <= 4); Operand { bits } } } impl std::fmt::Debug for Operand { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Display::fmt(self, f) } } impl std::fmt::Display for Operand { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{:?}@{:?}: {}{} {}", self.kind(), self.pos(), self.vreg(), match self.class() { RegClass::Int => "i", RegClass::Float => "f", }, self.policy() ) } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum OperandPolicy { /// Any location is fine (register or stack slot). Any, /// Operand must be in a register. Register is read-only for Uses. Reg, /// Operand must be on the stack. Stack, /// Operand must be in a fixed register. FixedReg(PReg), /// On defs only: reuse a use's register. Which use is given by `preg` field. Reuse(usize), } impl std::fmt::Display for OperandPolicy { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Self::Any => write!(f, "any"), Self::Reg => write!(f, "reg"), Self::Stack => write!(f, "stack"), Self::FixedReg(preg) => write!(f, "fixed({})", preg), Self::Reuse(idx) => write!(f, "reuse({})", idx), } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum OperandKind { Def = 0, Use = 1, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum OperandPos { Before = 0, After = 1, Both = 2, } /// An Allocation represents the end result of regalloc for an /// Operand. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Allocation { /// Bit-pack in 32 bits. Note that `kind` overlaps with the /// `policy` field in `Operand`, and we are careful to use /// disjoint ranges of values in this field for each type. We also /// leave the def-or-use bit (`kind` for `Operand`) unused here so /// that we can use it below in `OperandOrAllocation` to record /// whether `Allocation`s are defs or uses (which is often useful /// to know). /// /// kind:3 unused:1 index:28 bits: u32, } impl std::fmt::Debug for Allocation { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Display::fmt(self, f) } } impl std::fmt::Display for Allocation { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self.kind() { AllocationKind::None => write!(f, "none"), AllocationKind::Reg => write!(f, "{}", self.as_reg().unwrap()), AllocationKind::Stack => write!(f, "{}", self.as_stack().unwrap()), } } } impl Allocation { #[inline(always)] pub(crate) fn new(kind: AllocationKind, index: usize) -> Self { assert!(index < (1 << 28)); Self { bits: ((kind as u8 as u32) << 29) | (index as u32), } } #[inline(always)] pub fn none() -> Allocation { Allocation::new(AllocationKind::None, 0) } #[inline(always)] pub fn reg(preg: PReg) -> Allocation { Allocation::new(AllocationKind::Reg, preg.index()) } #[inline(always)] pub fn stack(slot: SpillSlot) -> Allocation { Allocation::new(AllocationKind::Stack, slot.0 as usize) } #[inline(always)] pub fn kind(self) -> AllocationKind { match (self.bits >> 29) & 7 { 5 => AllocationKind::None, 6 => AllocationKind::Reg, 7 => AllocationKind::Stack, _ => unreachable!(), } } #[inline(always)] pub fn is_none(self) -> bool { self.kind() == AllocationKind::None } #[inline(always)] pub fn is_reg(self) -> bool { self.kind() == AllocationKind::Reg } #[inline(always)] pub fn is_stack(self) -> bool { self.kind() == AllocationKind::Stack } #[inline(always)] pub fn index(self) -> usize { (self.bits & ((1 << 28) - 1)) as usize } #[inline(always)] pub fn as_reg(self) -> Option { if self.kind() == AllocationKind::Reg { Some(PReg::from_index(self.index())) } else { None } } #[inline(always)] pub fn as_stack(self) -> Option { if self.kind() == AllocationKind::Stack { Some(SpillSlot(self.index() as u32)) } else { None } } #[inline(always)] pub fn bits(self) -> u32 { self.bits } #[inline(always)] pub fn from_bits(bits: u32) -> Self { debug_assert!(bits >> 29 >= 5); Self { bits } } } // N.B.: These values must be *disjoint* with the values used to // encode `OperandPolicy`, because they share a 3-bit field. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum AllocationKind { None = 5, Reg = 6, Stack = 7, } impl Allocation { #[inline(always)] pub fn class(self) -> RegClass { match self.kind() { AllocationKind::None => panic!("Allocation::None has no class"), AllocationKind::Reg => self.as_reg().unwrap().class(), AllocationKind::Stack => self.as_stack().unwrap().class(), } } } /// A helper that wraps either an `Operand` or an `Allocation` and is /// able to tell which it is based on the tag bits. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct OperandOrAllocation { bits: u32, } impl OperandOrAllocation { pub fn from_operand(operand: Operand) -> Self { debug_assert!(operand.bits() >> 29 <= 4); Self { bits: operand.bits(), } } pub fn from_alloc(alloc: Allocation) -> Self { debug_assert!(alloc.bits() >> 29 >= 5); Self { bits: alloc.bits() } } pub fn from_alloc_and_kind(alloc: Allocation, kind: OperandKind) -> Self { debug_assert!(alloc.bits() >> 29 >= 5); let bits = alloc.bits() | match kind { OperandKind::Def => 0, OperandKind::Use => 1 << 28, }; Self { bits } } pub fn is_operand(&self) -> bool { (self.bits >> 29) <= 4 } pub fn is_allocation(&self) -> bool { (self.bits >> 29) >= 5 } pub fn as_operand(&self) -> Option { if self.is_operand() { Some(Operand::from_bits(self.bits)) } else { None } } pub fn as_allocation(&self) -> Option { if self.is_allocation() { // Remove the def/use bit -- the canonical `Allocation` // does not have this, and we want allocs to continue to // be comparable whether they are used for reads or // writes. Some(Allocation::from_bits(self.bits & !(1 << 28))) } else { None } } pub fn kind(&self) -> OperandKind { let kind_field = (self.bits >> 28) & 1; match kind_field { 0 => OperandKind::Def, 1 => OperandKind::Use, _ => unreachable!(), } } /// Replaces the Operand with an Allocation, keeping the def/use bit. pub fn replace_with_alloc(&mut self, alloc: Allocation) { self.bits &= 1 << 28; self.bits |= alloc.bits; } } /// A trait defined by the regalloc client to provide access to its /// machine-instruction / CFG representation. /// /// (This trait's design is inspired by, and derives heavily from, the /// trait of the same name in regalloc.rs.) pub trait Function { // ------------- // CFG traversal // ------------- /// How many instructions are there? fn insts(&self) -> usize; /// How many blocks are there? fn blocks(&self) -> usize; /// Get the index of the entry block. fn entry_block(&self) -> Block; /// Provide the range of instruction indices contained in each block. fn block_insns(&self, block: Block) -> InstRange; /// Get CFG successors for a given block. fn block_succs(&self, block: Block) -> &[Block]; /// Get the CFG predecessors for a given block. fn block_preds(&self, block: Block) -> &[Block]; /// Get the block parameters for a given block. fn block_params(&self, block: Block) -> &[VReg]; /// Determine whether an instruction is a call instruction. This is used /// only for splitting heuristics. fn is_call(&self, insn: Inst) -> bool; /// Determine whether an instruction is a return instruction. fn is_ret(&self, insn: Inst) -> bool; /// Determine whether an instruction is the end-of-block /// branch. If so, its operands at the indices given by /// `branch_blockparam_arg_offset()` below *must* be the block /// parameters for each of its block's `block_succs` successor /// blocks, in order. fn is_branch(&self, insn: Inst) -> bool; /// If `insn` is a branch at the end of `block`, returns the /// operand index at which outgoing blockparam arguments are /// found. Starting at this index, blockparam arguments for each /// successor block's blockparams, in order, must be found. /// /// It is an error if `self.inst_operands(insn).len() - /// self.branch_blockparam_arg_offset(insn)` is not exactly equal /// to the sum of blockparam counts for all successor blocks. fn branch_blockparam_arg_offset(&self, block: Block, insn: Inst) -> usize; /// Determine whether an instruction is a safepoint and requires a stackmap. fn is_safepoint(&self, _: Inst) -> bool { false } /// Determine whether an instruction is a move; if so, return the /// vregs for (src, dst). fn is_move(&self, insn: Inst) -> Option<(VReg, VReg)>; // -------------------------- // Instruction register slots // -------------------------- /// Get the Operands for an instruction. fn inst_operands(&self, insn: Inst) -> &[Operand]; /// Get the clobbers for an instruction. fn inst_clobbers(&self, insn: Inst) -> &[PReg]; /// Get the number of `VReg` in use in this function. fn num_vregs(&self) -> usize; /// Get the VRegs that are pointer/reference types. This has the /// following effects for each such vreg: /// /// - At all safepoint instructions, the vreg will be in a /// SpillSlot, not in a register. /// - The vreg *may not* be used as a register operand on /// safepoint instructions: this is because a vreg can only live /// in one place at a time. The client should copy the value to an /// integer-typed vreg and use this to pass a pointer as an input /// to a safepoint instruction (such as a function call). /// - At all safepoint instructions, all live vregs' locations /// will be included in a list in the `Output` below, so that /// pointer-inspecting/updating functionality (such as a moving /// garbage collector) may observe and edit their values. fn reftype_vregs(&self) -> &[VReg] { &[] } /// Get the VRegs for which we should generate value-location /// metadata for debugging purposes. This can be used to generate /// e.g. DWARF with valid prgram-point ranges for each value /// expression in a way that is more efficient than a post-hoc /// analysis of the allocator's output. /// /// Each tuple is (vreg, inclusive_start, exclusive_end, /// label). In the `Output` there will be (label, inclusive_start, /// exclusive_end, alloc)` tuples. The ranges may not exactly /// match -- specifically, the returned metadata may cover only a /// subset of the requested ranges -- if the value is not live for /// the entire requested ranges. fn debug_value_labels(&self) -> &[(Inst, Inst, VReg, u32)] { &[] } // -------------- // Spills/reloads // -------------- /// How many logical spill slots does the given regclass require? E.g., on /// a 64-bit machine, spill slots may nominally be 64-bit words, but a /// 128-bit vector value will require two slots. The regalloc will always /// align on this size. /// /// This passes the associated virtual register to the client as well, /// because the way in which we spill a real register may depend on the /// value that we are using it for. E.g., if a machine has V128 registers /// but we also use them for F32 and F64 values, we may use a different /// store-slot size and smaller-operand store/load instructions for an F64 /// than for a true V128. /// /// (This trait method's design and doc text derives from /// regalloc.rs' trait of the same name.) fn spillslot_size(&self, regclass: RegClass, for_vreg: VReg) -> usize; /// When providing a spillslot number for a multi-slot spillslot, /// do we provide the first or the last? This is usually related /// to which direction the stack grows and different clients may /// have different preferences. fn multi_spillslot_named_by_last_slot(&self) -> bool { false } } /// A position before or after an instruction. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum InstPosition { Before = 0, After = 1, } /// A program point: a single point before or after a given instruction. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ProgPoint { pub inst: Inst, pub pos: InstPosition, } impl ProgPoint { pub fn before(inst: Inst) -> Self { Self { inst, pos: InstPosition::Before, } } pub fn after(inst: Inst) -> Self { Self { inst, pos: InstPosition::After, } } pub fn next(self) -> ProgPoint { match self.pos { InstPosition::Before => ProgPoint { inst: self.inst, pos: InstPosition::After, }, InstPosition::After => ProgPoint { inst: self.inst.next(), pos: InstPosition::Before, }, } } pub fn prev(self) -> ProgPoint { match self.pos { InstPosition::Before => ProgPoint { inst: self.inst.prev(), pos: InstPosition::After, }, InstPosition::After => ProgPoint { inst: self.inst, pos: InstPosition::Before, }, } } pub fn to_index(self) -> u32 { debug_assert!(self.inst.index() <= ((1 << 31) - 1)); ((self.inst.index() as u32) << 1) | (self.pos as u8 as u32) } pub fn from_index(index: u32) -> Self { let inst = Inst::new((index >> 1) as usize); let pos = match index & 1 { 0 => InstPosition::Before, 1 => InstPosition::After, _ => unreachable!(), }; Self { inst, pos } } } /// An instruction to insert into the program to perform some data movement. #[derive(Clone, Debug)] pub enum Edit { /// Move one allocation to another. Each allocation may be a /// register or a stack slot (spillslot). Move { from: Allocation, to: Allocation }, /// Define blockparams' locations. Note that this is not typically /// turned into machine code, but can be useful metadata (e.g. for /// the checker). BlockParams { vregs: Vec, allocs: Vec, }, } /// A machine envrionment tells the register allocator which registers /// are available to allocate and what register may be used as a /// scratch register for each class, and some other miscellaneous info /// as well. #[derive(Clone, Debug)] pub struct MachineEnv { regs: Vec, preferred_regs_by_class: Vec>, non_preferred_regs_by_class: Vec>, scratch_by_class: Vec, } /// The output of the register allocator. #[derive(Clone, Debug)] pub struct Output { /// How many spillslots are needed in the frame? pub num_spillslots: usize, /// Edits (insertions or removals). Guaranteed to be sorted by /// program point. pub edits: Vec<(ProgPoint, Edit)>, /// Allocations for each operand. Mapping from instruction to /// allocations provided by `inst_alloc_offsets` below. pub allocs: Vec, /// Allocation offset in `allocs` for each instruction. pub inst_alloc_offsets: Vec, /// Safepoint records: at a given program point, a reference-typed value lives in the given SpillSlot. pub safepoint_slots: Vec<(ProgPoint, SpillSlot)>, /// Debug info: a labeled value (as applied to vregs by /// `Function::debug_value_labels()` on the input side) is located /// in the given allocation from the first program point /// (inclusive) to the second (exclusive). Guaranteed to be sorted /// by label and program point, and the ranges are guaranteed to /// be disjoint. pub debug_locations: Vec<(u32, ProgPoint, ProgPoint, Allocation)>, /// Internal stats from the allocator. pub stats: ion::Stats, } impl Output { pub fn inst_allocs(&self, inst: Inst) -> &[Allocation] { let start = self.inst_alloc_offsets[inst.index()] as usize; let end = if inst.index() + 1 == self.inst_alloc_offsets.len() { self.allocs.len() } else { self.inst_alloc_offsets[inst.index() + 1] as usize }; &self.allocs[start..end] } } /// An error that prevents allocation. #[derive(Clone, Debug)] pub enum RegAllocError { /// Invalid SSA for given vreg at given inst: multiple defs or /// illegal use. `inst` may be `Inst::invalid()` if this concerns /// a block param. SSA(VReg, Inst), /// Invalid basic block: does not end in branch/ret, or contains a /// branch/ret in the middle. BB(Block), /// Invalid branch: operand count does not match sum of block /// params of successor blocks. Branch(Inst), } impl std::fmt::Display for RegAllocError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:?}", self) } } impl std::error::Error for RegAllocError {} pub fn run(func: &F, env: &MachineEnv) -> Result { ion::run(func, env) }