#[cfg(feature = "fuzzing")]
pub mod analysis_main;
#[cfg(not(feature = "fuzzing"))]
mod analysis_main;
mod analysis_control_flow;
mod analysis_data_flow;
mod analysis_reftypes;
mod avl_tree;
mod bt_coalescing_analysis;
mod bt_commitment_map;
mod bt_main;
mod bt_spillslot_allocator;
mod bt_vlr_priority_queue;
mod checker;
mod data_structures;
mod inst_stream;
mod linear_scan;
mod pretty_print;
mod reg_maps;
mod snapshot;
mod sparse_set;
mod union_find;
use log::{info, log_enabled, Level};
use std::default;
use std::{borrow::Cow, fmt};
pub use crate::pretty_print::*;
pub use crate::data_structures::Map;
pub use crate::data_structures::Set;
pub use crate::data_structures::RegClass;
pub use crate::data_structures::Reg;
pub use crate::data_structures::RealReg;
pub use crate::data_structures::VirtualReg;
pub use crate::data_structures::Writable;
pub use crate::data_structures::NUM_REG_CLASSES;
pub use crate::data_structures::SpillSlot;
pub use crate::data_structures::RealRegUniverse;
pub use crate::data_structures::RegClassInfo;
pub use crate::data_structures::RegUsageCollector;
pub trait RegUsageMapper: fmt::Debug {
fn get_use(&self, vreg: VirtualReg) -> Option<RealReg>;
fn get_def(&self, vreg: VirtualReg) -> Option<RealReg>;
fn get_mod(&self, vreg: VirtualReg) -> Option<RealReg>;
}
pub use crate::data_structures::TypedIxVec;
pub use crate::data_structures::{BlockIx, InstIx, Range};
pub trait Function {
type Inst: Clone + fmt::Debug;
fn insns(&self) -> &[Self::Inst];
fn insn_indices(&self) -> Range<InstIx> {
Range::new(InstIx::new(0), self.insns().len())
}
fn insns_mut(&mut self) -> &mut [Self::Inst];
fn get_insn(&self, insn: InstIx) -> &Self::Inst;
fn get_insn_mut(&mut self, insn: InstIx) -> &mut Self::Inst;
fn blocks(&self) -> Range<BlockIx>;
fn entry_block(&self) -> BlockIx;
fn block_insns(&self, block: BlockIx) -> Range<InstIx>;
fn block_succs(&self, block: BlockIx) -> Cow<[BlockIx]>;
fn is_ret(&self, insn: InstIx) -> bool;
fn is_included_in_clobbers(&self, _insn: &Self::Inst) -> bool {
true
}
fn get_regs(insn: &Self::Inst, collector: &mut RegUsageCollector);
fn map_regs<RUM: RegUsageMapper>(insn: &mut Self::Inst, maps: &RUM);
fn is_move(&self, insn: &Self::Inst) -> Option<(Writable<Reg>, Reg)>;
fn get_num_vregs(&self) -> usize;
fn get_spillslot_size(&self, regclass: RegClass, for_vreg: VirtualReg) -> u32;
fn gen_spill(
&self,
to_slot: SpillSlot,
from_reg: RealReg,
for_vreg: Option<VirtualReg>,
) -> Self::Inst;
fn gen_reload(
&self,
to_reg: Writable<RealReg>,
from_slot: SpillSlot,
for_vreg: Option<VirtualReg>,
) -> Self::Inst;
fn gen_move(
&self,
to_reg: Writable<RealReg>,
from_reg: RealReg,
for_vreg: VirtualReg,
) -> Self::Inst;
fn gen_zero_len_nop(&self) -> Self::Inst;
fn maybe_direct_reload(
&self,
insn: &Self::Inst,
reg: VirtualReg,
slot: SpillSlot,
) -> Option<Self::Inst>;
fn func_liveins(&self) -> Set<RealReg>;
fn func_liveouts(&self) -> Set<RealReg>;
}
pub struct RegAllocResult<F: Function> {
pub insns: Vec<F::Inst>,
pub target_map: TypedIxVec<BlockIx, InstIx>,
pub orig_insn_map: TypedIxVec< InstIx, InstIx>,
pub clobbered_registers: Set<RealReg>,
pub num_spill_slots: u32,
pub block_annotations: Option<TypedIxVec<BlockIx, Vec<String>>>,
pub stackmaps: Vec<Vec<SpillSlot>>,
pub new_safepoint_insns: Vec<InstIx>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AlgorithmWithDefaults {
Backtracking,
LinearScan,
}
pub use crate::analysis_main::AnalysisError;
pub use crate::checker::{CheckerError, CheckerErrors};
#[derive(Clone, Debug)]
pub enum RegAllocError {
OutOfRegisters(RegClass),
MissingSuggestedScratchReg(RegClass),
Analysis(AnalysisError),
RegChecker(CheckerErrors),
Other(String),
}
impl fmt::Display for RegAllocError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
pub use crate::bt_main::BacktrackingOptions;
pub use crate::linear_scan::LinearScanOptions;
#[derive(Clone)]
pub enum Algorithm {
LinearScan(LinearScanOptions),
Backtracking(BacktrackingOptions),
}
impl fmt::Debug for Algorithm {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Algorithm::LinearScan(opts) => write!(fmt, "{:?}", opts),
Algorithm::Backtracking(opts) => write!(fmt, "{:?}", opts),
}
}
}
#[derive(Clone)]
pub struct Options {
pub run_checker: bool,
pub algorithm: Algorithm,
}
impl default::Default for Options {
fn default() -> Self {
Self {
run_checker: false,
algorithm: Algorithm::Backtracking(Default::default()),
}
}
}
impl fmt::Debug for Options {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"checker: {:?}, algorithm: {:?}",
self.run_checker, self.algorithm
)
}
}
pub struct StackmapRequestInfo {
pub reftype_class: RegClass,
pub reftyped_vregs: Vec<VirtualReg>,
pub safepoint_insns: Vec<InstIx>,
}
#[inline(never)]
pub fn allocate_registers_with_opts<F: Function>(
func: &mut F,
rreg_universe: &RealRegUniverse,
stackmap_info: Option<&StackmapRequestInfo>,
opts: Options,
) -> Result<RegAllocResult<F>, RegAllocError> {
info!("");
info!("================ regalloc.rs: BEGIN function ================");
if log_enabled!(Level::Info) {
info!("with options: {:?}", opts);
let strs = rreg_universe.show();
info!("using RealRegUniverse:");
for s in strs {
info!(" {}", s);
}
}
if let Some(&StackmapRequestInfo {
reftype_class,
ref reftyped_vregs,
ref safepoint_insns,
}) = stackmap_info
{
if let Algorithm::LinearScan(_) = opts.algorithm {
return Err(RegAllocError::Other(
"stackmap request: not currently available for Linear Scan".to_string(),
));
}
if reftype_class != RegClass::I64 && reftype_class != RegClass::I32 {
return Err(RegAllocError::Other(
"stackmap request: invalid reftype_class".to_string(),
));
}
let num_avail_vregs = func.get_num_vregs();
for i in 0..reftyped_vregs.len() {
let vreg = &reftyped_vregs[i];
if vreg.get_class() != reftype_class {
return Err(RegAllocError::Other(
"stackmap request: invalid vreg class".to_string(),
));
}
if vreg.get_index() >= num_avail_vregs {
return Err(RegAllocError::Other(
"stackmap request: out of range vreg".to_string(),
));
}
if i > 0 && reftyped_vregs[i - 1].get_index() >= vreg.get_index() {
return Err(RegAllocError::Other(
"stackmap request: non-ascending vregs".to_string(),
));
}
}
let num_avail_insns = func.insns().len();
for i in 0..safepoint_insns.len() {
let safepoint_iix = safepoint_insns[i];
if safepoint_iix.get() as usize >= num_avail_insns {
return Err(RegAllocError::Other(
"stackmap request: out of range safepoint insn".to_string(),
));
}
if i > 0 && safepoint_insns[i - 1].get() >= safepoint_iix.get() {
return Err(RegAllocError::Other(
"stackmap request: non-ascending safepoint insns".to_string(),
));
}
if func.is_move(func.get_insn(safepoint_iix)).is_some() {
return Err(RegAllocError::Other(
"stackmap request: safepoint insn is a move insn".to_string(),
));
}
}
}
let run_checker = opts.run_checker;
let res = match &opts.algorithm {
Algorithm::Backtracking(opts) => {
bt_main::alloc_main(func, rreg_universe, stackmap_info, run_checker, opts)
}
Algorithm::LinearScan(opts) => linear_scan::run(func, rreg_universe, run_checker, opts),
};
info!("================ regalloc.rs: END function ================");
res
}
#[inline(never)]
pub fn allocate_registers<F: Function>(
func: &mut F,
rreg_universe: &RealRegUniverse,
stackmap_info: Option<&StackmapRequestInfo>,
algorithm: AlgorithmWithDefaults,
) -> Result<RegAllocResult<F>, RegAllocError> {
let algorithm = match algorithm {
AlgorithmWithDefaults::Backtracking => Algorithm::Backtracking(Default::default()),
AlgorithmWithDefaults::LinearScan => Algorithm::LinearScan(Default::default()),
};
let opts = Options {
algorithm,
..Default::default()
};
allocate_registers_with_opts(func, rreg_universe, stackmap_info, opts)
}
pub use crate::snapshot::IRSnapshot;