Make regalloc2 #![no_std] (#119)
* Make regalloc2 `#![no_std]` This crate doesn't require any features from the standard library, so it can be made `no_std` to allow it to be used in environments that can't use the Rust standard library. This PR mainly performs the following mechanical changes: - `std::collections` is replaced with `alloc::collections`. - `std::*` is replaced with `core::*`. - `Vec`, `vec!`, `format!` and `ToString` are imported when needed since they are no longer in the prelude. - `HashSet` and `HashMap` are taken from the `hashbrown` crate, which is the same implementation that the standard library uses. - `FxHashSet` and `FxHashMap` are typedefs in `lib.rs` that are based on the `hashbrown` types. The only functional change is that `RegAllocError` no longer implements the `Error` trait since that is not available in `core`. Dependencies were adjusted to not require `std` and this is tested in CI by building against the `thumbv6m-none-eabi` target that doesn't have `std`. * Add the Error trait impl back under a "std" feature
This commit is contained in:
10
.github/workflows/rust.yml
vendored
10
.github/workflows/rust.yml
vendored
@@ -37,6 +37,16 @@ jobs:
|
||||
- name: Check with all features
|
||||
run: cargo check --all-features
|
||||
|
||||
# Make sure the code and its dependencies compile without std.
|
||||
no_std:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install thumbv6m-none-eabi target
|
||||
run: rustup target add thumbv6m-none-eabi
|
||||
- name: Check no_std build
|
||||
run: cargo check --target thumbv6m-none-eabi --no-default-features --features trace-log,checker,enable-serde
|
||||
|
||||
# Lint dependency graph for security advisories, duplicate versions, and
|
||||
# incompatible licences.
|
||||
cargo_deny:
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -13,11 +13,15 @@ repository = "https://github.com/bytecodealliance/regalloc2"
|
||||
[dependencies]
|
||||
log = { version = "0.4.8", default-features = false }
|
||||
smallvec = { version = "1.6.1", features = ["union"] }
|
||||
fxhash = "0.2.1"
|
||||
slice-group-by = "0.3.0"
|
||||
rustc-hash = { version = "1.1.0", default-features = false }
|
||||
slice-group-by = { version = "0.3.0", default-features = false }
|
||||
hashbrown = "0.13.2"
|
||||
|
||||
# Optional serde support, enabled by feature below.
|
||||
serde = { version = "1.0.136", features = ["derive"], optional = true }
|
||||
serde = { version = "1.0.136", features = [
|
||||
"derive",
|
||||
"alloc",
|
||||
], default-features = false, optional = true }
|
||||
|
||||
# The below are only needed for fuzzing.
|
||||
libfuzzer-sys = { version = "0.4.2", optional = true }
|
||||
@@ -29,7 +33,10 @@ debug-assertions = true
|
||||
overflow-checks = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["std"]
|
||||
|
||||
# Enables std-specific features such as the Error trait for RegAllocError.
|
||||
std = []
|
||||
|
||||
# Enables generation of DefAlloc edits for the checker.
|
||||
checker = []
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
//! Lightweight CFG analyses.
|
||||
|
||||
use crate::{domtree, postorder, Block, Function, Inst, ProgPoint, RegAllocError};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -96,14 +96,16 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::{
|
||||
Allocation, AllocationKind, Block, Edit, Function, Inst, InstOrEdit, InstPosition, MachineEnv,
|
||||
Operand, OperandConstraint, OperandKind, OperandPos, Output, PReg, PRegSet, VReg,
|
||||
Allocation, AllocationKind, Block, Edit, Function, FxHashMap, FxHashSet, Inst, InstOrEdit,
|
||||
InstPosition, MachineEnv, Operand, OperandConstraint, OperandKind, OperandPos, Output, PReg,
|
||||
PRegSet, VReg,
|
||||
};
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
use core::default::Default;
|
||||
use core::hash::Hash;
|
||||
use core::result::Result;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::default::Default;
|
||||
use std::hash::Hash;
|
||||
use std::result::Result;
|
||||
|
||||
/// A set of errors detected by the regalloc checker.
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -230,7 +232,7 @@ impl CheckerValue {
|
||||
}
|
||||
|
||||
fn from_reg(reg: VReg) -> CheckerValue {
|
||||
CheckerValue::VRegs(std::iter::once(reg).collect())
|
||||
CheckerValue::VRegs(core::iter::once(reg).collect())
|
||||
}
|
||||
|
||||
fn remove_vreg(&mut self, reg: VReg) {
|
||||
@@ -373,8 +375,8 @@ impl Default for CheckerState {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CheckerValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Display for CheckerValue {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self {
|
||||
CheckerValue::Universe => {
|
||||
write!(f, "top")
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
// TR-06-33870
|
||||
// https://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::Block;
|
||||
|
||||
// Helper
|
||||
|
||||
@@ -8,6 +8,9 @@ use crate::{
|
||||
OperandConstraint, OperandKind, OperandPos, PReg, PRegSet, RegClass, VReg,
|
||||
};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
|
||||
use super::arbitrary::Result as ArbitraryResult;
|
||||
use super::arbitrary::{Arbitrary, Unstructured};
|
||||
|
||||
@@ -275,7 +278,7 @@ pub struct Options {
|
||||
pub reftypes: bool,
|
||||
}
|
||||
|
||||
impl std::default::Default for Options {
|
||||
impl core::default::Default for Options {
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
reused_inputs: false,
|
||||
@@ -404,7 +407,7 @@ impl Func {
|
||||
}
|
||||
vregs_by_block.push(vregs.clone());
|
||||
vregs_by_block_to_be_defined.push(vec![]);
|
||||
let mut max_block_params = u.int_in_range(0..=std::cmp::min(3, vregs.len() / 3))?;
|
||||
let mut max_block_params = u.int_in_range(0..=core::cmp::min(3, vregs.len() / 3))?;
|
||||
for &vreg in &vregs {
|
||||
if block > 0 && opts.block_params && bool::arbitrary(u)? && max_block_params > 0 {
|
||||
block_params[block].push(vreg);
|
||||
@@ -591,8 +594,8 @@ impl Func {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Func {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Debug for Func {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{{\n")?;
|
||||
for vreg in self.reftype_vregs() {
|
||||
write!(f, " REF: {}\n", vreg)?;
|
||||
@@ -653,7 +656,7 @@ impl std::fmt::Debug for Func {
|
||||
}
|
||||
|
||||
pub fn machine_env() -> MachineEnv {
|
||||
fn regs(r: std::ops::Range<usize>) -> Vec<PReg> {
|
||||
fn regs(r: core::ops::Range<usize>) -> Vec<PReg> {
|
||||
r.map(|i| PReg::new(i, RegClass::Int)).collect()
|
||||
}
|
||||
let preferred_regs_by_class: [Vec<PReg>; 2] = [regs(0..24), vec![]];
|
||||
|
||||
@@ -50,11 +50,11 @@ macro_rules! define_index {
|
||||
};
|
||||
}
|
||||
|
||||
pub trait ContainerIndex: Clone + Copy + std::fmt::Debug + PartialEq + Eq {}
|
||||
pub trait ContainerIndex: Clone + Copy + core::fmt::Debug + PartialEq + Eq {}
|
||||
|
||||
pub trait ContainerComparator {
|
||||
type Ix: ContainerIndex;
|
||||
fn compare(&self, a: Self::Ix, b: Self::Ix) -> std::cmp::Ordering;
|
||||
fn compare(&self, a: Self::Ix, b: Self::Ix) -> core::cmp::Ordering;
|
||||
}
|
||||
|
||||
define_index!(Inst);
|
||||
@@ -146,6 +146,9 @@ impl Iterator for InstRangeIter {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
|
||||
//! Index sets: sets of integers that represent indices into a space.
|
||||
|
||||
use fxhash::FxHashMap;
|
||||
use std::cell::Cell;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::Cell;
|
||||
|
||||
use crate::FxHashMap;
|
||||
|
||||
const SMALL_ELEMS: usize = 12;
|
||||
|
||||
@@ -151,10 +153,10 @@ impl AdaptiveMap {
|
||||
|
||||
enum AdaptiveMapIter<'a> {
|
||||
Small(&'a [u32], &'a [u64]),
|
||||
Large(std::collections::hash_map::Iter<'a, u32, u64>),
|
||||
Large(hashbrown::hash_map::Iter<'a, u32, u64>),
|
||||
}
|
||||
|
||||
impl<'a> std::iter::Iterator for AdaptiveMapIter<'a> {
|
||||
impl<'a> core::iter::Iterator for AdaptiveMapIter<'a> {
|
||||
type Item = (u32, u64);
|
||||
|
||||
#[inline]
|
||||
@@ -292,7 +294,7 @@ impl Iterator for SetBitsIter {
|
||||
// Build an `Option<NonZeroU64>` so that on the nonzero path,
|
||||
// the compiler can optimize the trailing-zeroes operator
|
||||
// using that knowledge.
|
||||
std::num::NonZeroU64::new(self.0).map(|nz| {
|
||||
core::num::NonZeroU64::new(self.0).map(|nz| {
|
||||
let bitidx = nz.trailing_zeros();
|
||||
self.0 &= self.0 - 1; // clear highest set bit
|
||||
bitidx as usize
|
||||
@@ -300,8 +302,8 @@ impl Iterator for SetBitsIter {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for IndexSet {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Debug for IndexSet {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let vals = self.iter().collect::<Vec<_>>();
|
||||
write!(f, "{:?}", vals)
|
||||
}
|
||||
|
||||
@@ -17,14 +17,16 @@ use crate::cfg::CFGInfo;
|
||||
use crate::index::ContainerComparator;
|
||||
use crate::indexset::IndexSet;
|
||||
use crate::{
|
||||
define_index, Allocation, Block, Edit, Function, Inst, MachineEnv, Operand, PReg, ProgPoint,
|
||||
RegClass, VReg,
|
||||
define_index, Allocation, Block, Edit, Function, FxHashSet, Inst, MachineEnv, Operand, PReg,
|
||||
ProgPoint, RegClass, VReg,
|
||||
};
|
||||
use fxhash::FxHashSet;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::Debug;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// A range from `from` (inclusive) to `to` (exclusive).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -64,13 +66,13 @@ impl CodeRange {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::PartialOrd for CodeRange {
|
||||
impl core::cmp::PartialOrd for CodeRange {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl std::cmp::Ord for CodeRange {
|
||||
impl core::cmp::Ord for CodeRange {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.to <= other.from {
|
||||
@@ -278,7 +280,7 @@ const fn no_bloat_capacity<T>() -> usize {
|
||||
//
|
||||
// So if `size_of([T; N]) == size_of(pointer) + size_of(capacity)` then we
|
||||
// get the maximum inline capacity without bloat.
|
||||
std::mem::size_of::<usize>() * 2 / std::mem::size_of::<T>()
|
||||
core::mem::size_of::<usize>() * 2 / core::mem::size_of::<T>()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -431,7 +433,7 @@ pub struct Env<'a, F: Function> {
|
||||
|
||||
// For debug output only: a list of textual annotations at every
|
||||
// ProgPoint to insert into the final allocated program listing.
|
||||
pub debug_annotations: std::collections::HashMap<ProgPoint, Vec<String>>,
|
||||
pub debug_annotations: hashbrown::HashMap<ProgPoint, Vec<String>>,
|
||||
pub annotations_enabled: bool,
|
||||
|
||||
// Cached allocation for `try_to_allocate_bundle_to_reg` to avoid allocating
|
||||
@@ -492,7 +494,7 @@ impl SpillSlotList {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PrioQueue {
|
||||
pub heap: std::collections::BinaryHeap<PrioQueueEntry>,
|
||||
pub heap: alloc::collections::BinaryHeap<PrioQueueEntry>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@@ -531,28 +533,28 @@ impl LiveRangeKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::PartialEq for LiveRangeKey {
|
||||
impl core::cmp::PartialEq for LiveRangeKey {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.to > other.from && self.from < other.to
|
||||
}
|
||||
}
|
||||
impl std::cmp::Eq for LiveRangeKey {}
|
||||
impl std::cmp::PartialOrd for LiveRangeKey {
|
||||
impl core::cmp::Eq for LiveRangeKey {}
|
||||
impl core::cmp::PartialOrd for LiveRangeKey {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl std::cmp::Ord for LiveRangeKey {
|
||||
impl core::cmp::Ord for LiveRangeKey {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||
if self.to <= other.from {
|
||||
std::cmp::Ordering::Less
|
||||
core::cmp::Ordering::Less
|
||||
} else if self.from >= other.to {
|
||||
std::cmp::Ordering::Greater
|
||||
core::cmp::Ordering::Greater
|
||||
} else {
|
||||
std::cmp::Ordering::Equal
|
||||
core::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -562,7 +564,7 @@ pub struct PrioQueueComparator<'a> {
|
||||
}
|
||||
impl<'a> ContainerComparator for PrioQueueComparator<'a> {
|
||||
type Ix = LiveBundleIndex;
|
||||
fn compare(&self, a: Self::Ix, b: Self::Ix) -> std::cmp::Ordering {
|
||||
fn compare(&self, a: Self::Ix, b: Self::Ix) -> core::cmp::Ordering {
|
||||
self.prios[a.index()].cmp(&self.prios[b.index()])
|
||||
}
|
||||
}
|
||||
@@ -570,7 +572,7 @@ impl<'a> ContainerComparator for PrioQueueComparator<'a> {
|
||||
impl PrioQueue {
|
||||
pub fn new() -> Self {
|
||||
PrioQueue {
|
||||
heap: std::collections::BinaryHeap::new(),
|
||||
heap: alloc::collections::BinaryHeap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
//! Debugging output.
|
||||
|
||||
use alloc::string::ToString;
|
||||
use alloc::{format, vec};
|
||||
use alloc::{string::String, vec::Vec};
|
||||
|
||||
use super::Env;
|
||||
use crate::{Block, Function, ProgPoint};
|
||||
|
||||
|
||||
@@ -22,13 +22,15 @@ use crate::ion::data_structures::{
|
||||
BlockparamIn, BlockparamOut, FixedRegFixupLevel, MultiFixedRegFixup,
|
||||
};
|
||||
use crate::{
|
||||
Allocation, Block, Function, Inst, InstPosition, Operand, OperandConstraint, OperandKind,
|
||||
OperandPos, PReg, ProgPoint, RegAllocError, VReg,
|
||||
Allocation, Block, Function, FxHashMap, FxHashSet, Inst, InstPosition, Operand,
|
||||
OperandConstraint, OperandKind, OperandPos, PReg, ProgPoint, RegAllocError, VReg,
|
||||
};
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use alloc::collections::VecDeque;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use hashbrown::HashSet;
|
||||
use slice_group_by::GroupByMut;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
|
||||
/// A spill weight computed for a certain Use.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -42,7 +44,7 @@ pub fn spill_weight_from_constraint(
|
||||
) -> SpillWeight {
|
||||
// A bonus of 1000 for one loop level, 4000 for two loop levels,
|
||||
// 16000 for three loop levels, etc. Avoids exponentiation.
|
||||
let loop_depth = std::cmp::min(10, loop_depth);
|
||||
let loop_depth = core::cmp::min(10, loop_depth);
|
||||
let hot_bonus: f32 = (0..loop_depth).fold(1000.0, |a, _| a * 4.0);
|
||||
let def_bonus: f32 = if is_def { 2000.0 } else { 0.0 };
|
||||
let constraint_bonus: f32 = match constraint {
|
||||
@@ -91,7 +93,7 @@ impl SpillWeight {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<SpillWeight> for SpillWeight {
|
||||
impl core::ops::Add<SpillWeight> for SpillWeight {
|
||||
type Output = SpillWeight;
|
||||
fn add(self, other: SpillWeight) -> Self {
|
||||
SpillWeight(self.0 + other.0)
|
||||
|
||||
@@ -18,6 +18,7 @@ use super::{
|
||||
use crate::{
|
||||
ion::data_structures::BlockparamOut, Function, Inst, OperandConstraint, OperandKind, PReg,
|
||||
};
|
||||
use alloc::format;
|
||||
use smallvec::smallvec;
|
||||
|
||||
impl<'a, F: Function> Env<'a, F> {
|
||||
@@ -132,7 +133,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
// `to` bundle is empty -- just move the list over from
|
||||
// `from` and set `bundle` up-link on all ranges.
|
||||
trace!(" -> to bundle{} is empty; trivial merge", to.index());
|
||||
let list = std::mem::replace(&mut self.bundles[from.index()].ranges, smallvec![]);
|
||||
let list = core::mem::replace(&mut self.bundles[from.index()].ranges, smallvec![]);
|
||||
for entry in &list {
|
||||
self.ranges[entry.index.index()].bundle = to;
|
||||
|
||||
@@ -170,7 +171,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
// Two non-empty lists of LiveRanges: concatenate and
|
||||
// sort. This is faster than a mergesort-like merge into a new
|
||||
// list, empirically.
|
||||
let from_list = std::mem::replace(&mut self.bundles[from.index()].ranges, smallvec![]);
|
||||
let from_list = core::mem::replace(&mut self.bundles[from.index()].ranges, smallvec![]);
|
||||
for entry in &from_list {
|
||||
self.ranges[entry.index.index()].bundle = to;
|
||||
}
|
||||
@@ -213,7 +214,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
}
|
||||
|
||||
if self.bundles[from.index()].spillset != self.bundles[to.index()].spillset {
|
||||
let from_vregs = std::mem::replace(
|
||||
let from_vregs = core::mem::replace(
|
||||
&mut self.spillsets[self.bundles[from.index()].spillset.index()].vregs,
|
||||
smallvec![],
|
||||
);
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
use crate::cfg::CFGInfo;
|
||||
use crate::ssa::validate_ssa;
|
||||
use crate::{Function, MachineEnv, Output, PReg, ProgPoint, RegAllocError, RegClass};
|
||||
use std::collections::HashMap;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
pub(crate) mod data_structures;
|
||||
pub use data_structures::Stats;
|
||||
@@ -82,7 +84,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
|
||||
stats: Stats::default(),
|
||||
|
||||
debug_annotations: std::collections::HashMap::new(),
|
||||
debug_annotations: hashbrown::HashMap::new(),
|
||||
annotations_enabled,
|
||||
|
||||
conflict_set: Default::default(),
|
||||
|
||||
@@ -22,12 +22,13 @@ use crate::ion::data_structures::{
|
||||
use crate::ion::reg_traversal::RegTraversalIter;
|
||||
use crate::moves::{MoveAndScratchResolver, ParallelMoves};
|
||||
use crate::{
|
||||
Allocation, Block, Edit, Function, Inst, InstPosition, OperandConstraint, OperandKind,
|
||||
OperandPos, PReg, ProgPoint, RegClass, SpillSlot, VReg,
|
||||
Allocation, Block, Edit, Function, FxHashMap, Inst, InstPosition, OperandConstraint,
|
||||
OperandKind, OperandPos, PReg, ProgPoint, RegClass, SpillSlot, VReg,
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
use core::fmt::Debug;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::fmt::Debug;
|
||||
|
||||
impl<'a, F: Function> Env<'a, F> {
|
||||
pub fn is_start_of_block(&self, pos: ProgPoint) -> bool {
|
||||
@@ -494,9 +495,9 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
// this case returns the index of the first
|
||||
// entry that is greater as an `Err`.
|
||||
if label_vreg.vreg() < vreg.index() {
|
||||
std::cmp::Ordering::Less
|
||||
core::cmp::Ordering::Less
|
||||
} else {
|
||||
std::cmp::Ordering::Greater
|
||||
core::cmp::Ordering::Greater
|
||||
}
|
||||
})
|
||||
.unwrap_err();
|
||||
@@ -515,8 +516,8 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let from = std::cmp::max(label_from, range.from);
|
||||
let to = std::cmp::min(label_to, range.to);
|
||||
let from = core::cmp::max(label_from, range.from);
|
||||
let to = core::cmp::min(label_to, range.to);
|
||||
|
||||
self.debug_locations.push((label, from, to, alloc));
|
||||
}
|
||||
@@ -629,7 +630,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
}
|
||||
|
||||
// Handle multi-fixed-reg constraints by copying.
|
||||
for fixup in std::mem::replace(&mut self.multi_fixed_reg_fixups, vec![]) {
|
||||
for fixup in core::mem::replace(&mut self.multi_fixed_reg_fixups, vec![]) {
|
||||
let from_alloc = self.get_alloc(fixup.pos.inst(), fixup.from_slot as usize);
|
||||
let to_alloc = Allocation::reg(PReg::from_index(fixup.to_preg.index()));
|
||||
trace!(
|
||||
|
||||
@@ -22,12 +22,11 @@ use crate::{
|
||||
CodeRange, BUNDLE_MAX_NORMAL_SPILL_WEIGHT, MAX_SPLITS_PER_SPILLSET,
|
||||
MINIMAL_BUNDLE_SPILL_WEIGHT, MINIMAL_FIXED_BUNDLE_SPILL_WEIGHT,
|
||||
},
|
||||
Allocation, Function, Inst, InstPosition, OperandConstraint, OperandKind, PReg, ProgPoint,
|
||||
RegAllocError,
|
||||
Allocation, Function, FxHashSet, Inst, InstPosition, OperandConstraint, OperandKind, PReg,
|
||||
ProgPoint, RegAllocError,
|
||||
};
|
||||
use fxhash::FxHashSet;
|
||||
use core::fmt::Debug;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum AllocRegResult {
|
||||
@@ -159,7 +158,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
trace!(" -> conflict bundle {:?}", conflict_bundle);
|
||||
if self.conflict_set.insert(conflict_bundle) {
|
||||
conflicts.push(conflict_bundle);
|
||||
max_conflict_weight = std::cmp::max(
|
||||
max_conflict_weight = core::cmp::max(
|
||||
max_conflict_weight,
|
||||
self.bundles[conflict_bundle.index()].cached_spill_weight(),
|
||||
);
|
||||
@@ -172,7 +171,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
}
|
||||
|
||||
if first_conflict.is_none() {
|
||||
first_conflict = Some(ProgPoint::from_index(std::cmp::max(
|
||||
first_conflict = Some(ProgPoint::from_index(core::cmp::max(
|
||||
preg_key.from,
|
||||
key.from,
|
||||
)));
|
||||
@@ -334,7 +333,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
self.bundles[bundle.index()].prio,
|
||||
final_weight
|
||||
);
|
||||
std::cmp::min(BUNDLE_MAX_NORMAL_SPILL_WEIGHT, final_weight)
|
||||
core::cmp::min(BUNDLE_MAX_NORMAL_SPILL_WEIGHT, final_weight)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@@ -824,7 +823,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
// (up to the Before of the next inst), *unless*
|
||||
// the original LR was only over the Before (up to
|
||||
// the After) of this inst.
|
||||
let to = std::cmp::min(ProgPoint::before(u.pos.inst().next()), lr_to);
|
||||
let to = core::cmp::min(ProgPoint::before(u.pos.inst().next()), lr_to);
|
||||
|
||||
// If the last bundle was at the same inst, add a new
|
||||
// LR to the same bundle; otherwise, create a LR and a
|
||||
@@ -863,7 +862,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
|
||||
// Otherwise, create a new LR.
|
||||
let pos = ProgPoint::before(u.pos.inst());
|
||||
let pos = std::cmp::max(lr_from, pos);
|
||||
let pos = core::cmp::max(lr_from, pos);
|
||||
let cr = CodeRange { from: pos, to };
|
||||
let lr = self.create_liverange(cr);
|
||||
new_lrs.push((vreg, lr));
|
||||
@@ -1036,7 +1035,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
self.get_or_create_spill_bundle(bundle, /* create_if_absent = */ false)
|
||||
{
|
||||
let mut list =
|
||||
std::mem::replace(&mut self.bundles[bundle.index()].ranges, smallvec![]);
|
||||
core::mem::replace(&mut self.bundles[bundle.index()].ranges, smallvec![]);
|
||||
for entry in &list {
|
||||
self.ranges[entry.index.index()].bundle = spill;
|
||||
}
|
||||
@@ -1107,7 +1106,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
lowest_cost_evict_conflict_cost,
|
||||
lowest_cost_split_conflict_cost,
|
||||
) {
|
||||
(Some(a), Some(b)) => Some(std::cmp::max(a, b)),
|
||||
(Some(a), Some(b)) => Some(core::cmp::max(a, b)),
|
||||
_ => None,
|
||||
};
|
||||
match self.try_to_allocate_bundle_to_reg(bundle, preg_idx, scan_limit_cost) {
|
||||
@@ -1291,7 +1290,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
);
|
||||
let bundle_start = self.bundles[bundle.index()].ranges[0].range.from;
|
||||
let mut split_at_point =
|
||||
std::cmp::max(lowest_cost_split_conflict_point, bundle_start);
|
||||
core::cmp::max(lowest_cost_split_conflict_point, bundle_start);
|
||||
let requeue_with_reg = lowest_cost_split_conflict_reg;
|
||||
|
||||
// Adjust `split_at_point` if it is within a deeper loop
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Redundant-move elimination.
|
||||
|
||||
use crate::{Allocation, VReg};
|
||||
use fxhash::FxHashMap;
|
||||
use crate::{Allocation, FxHashMap, VReg};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
||||
@@ -78,7 +78,7 @@ impl<'a> RegTraversalIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::iter::Iterator for RegTraversalIter<'a> {
|
||||
impl<'a> core::iter::Iterator for RegTraversalIter<'a> {
|
||||
type Item = PReg;
|
||||
|
||||
fn next(&mut self) -> Option<PReg> {
|
||||
|
||||
@@ -138,7 +138,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
let mut success = false;
|
||||
// Never probe the same element more than once: limit the
|
||||
// attempt count to the number of slots in existence.
|
||||
for _attempt in 0..std::cmp::min(self.slots_by_size[size].slots.len(), MAX_ATTEMPTS) {
|
||||
for _attempt in 0..core::cmp::min(self.slots_by_size[size].slots.len(), MAX_ATTEMPTS) {
|
||||
// Note: this indexing of `slots` is always valid
|
||||
// because either the `slots` list is empty and the
|
||||
// iteration limit above consequently means we don't
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
//! Stackmap computation.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use super::{Env, ProgPoint, VRegIndex};
|
||||
use crate::{ion::data_structures::u64_key, Function};
|
||||
|
||||
|
||||
70
src/lib.rs
70
src/lib.rs
@@ -11,6 +11,12 @@
|
||||
*/
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
// Even when trace logging is disabled, the trace macro has a significant
|
||||
// performance cost so we disable it in release builds.
|
||||
@@ -28,6 +34,11 @@ macro_rules! trace_enabled {
|
||||
};
|
||||
}
|
||||
|
||||
use core::hash::BuildHasherDefault;
|
||||
use rustc_hash::FxHasher;
|
||||
type FxHashMap<K, V> = hashbrown::HashMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||
type FxHashSet<V> = hashbrown::HashSet<V, BuildHasherDefault<FxHasher>>;
|
||||
|
||||
pub(crate) mod cfg;
|
||||
pub(crate) mod domtree;
|
||||
pub mod indexset;
|
||||
@@ -38,6 +49,8 @@ pub mod ssa;
|
||||
|
||||
#[macro_use]
|
||||
mod index;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
pub use index::{Block, Inst, InstRange, InstRangeIter};
|
||||
|
||||
pub mod checker;
|
||||
@@ -142,8 +155,8 @@ impl PReg {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PReg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Debug for PReg {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"PReg(hw = {}, class = {:?}, index = {})",
|
||||
@@ -154,8 +167,8 @@ impl std::fmt::Debug for PReg {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PReg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Display for PReg {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let class = match self.class() {
|
||||
RegClass::Int => "i",
|
||||
RegClass::Float => "f",
|
||||
@@ -311,8 +324,8 @@ impl VReg {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for VReg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Debug for VReg {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"VReg(vreg = {}, class = {:?})",
|
||||
@@ -322,8 +335,8 @@ impl std::fmt::Debug for VReg {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VReg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Display for VReg {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "v{}", self.vreg())
|
||||
}
|
||||
}
|
||||
@@ -382,8 +395,8 @@ impl SpillSlot {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SpillSlot {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Display for SpillSlot {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "stack{}", self.index())
|
||||
}
|
||||
}
|
||||
@@ -413,8 +426,8 @@ pub enum OperandConstraint {
|
||||
Reuse(usize),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for OperandConstraint {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Display for OperandConstraint {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::Any => write!(f, "any"),
|
||||
Self::Reg => write!(f, "reg"),
|
||||
@@ -796,14 +809,14 @@ impl Operand {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Operand {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
impl core::fmt::Debug for Operand {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
core::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Operand {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Display for Operand {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match (self.kind(), self.pos()) {
|
||||
(OperandKind::Def, OperandPos::Late) | (OperandKind::Use, OperandPos::Early) => {
|
||||
write!(f, "{:?}", self.kind())?;
|
||||
@@ -836,14 +849,14 @@ pub struct Allocation {
|
||||
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 core::fmt::Debug for Allocation {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
core::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Allocation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Display for Allocation {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self.kind() {
|
||||
AllocationKind::None => write!(f, "none"),
|
||||
AllocationKind::Reg => write!(f, "{}", self.as_reg().unwrap()),
|
||||
@@ -1173,8 +1186,8 @@ pub struct ProgPoint {
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ProgPoint {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Debug for ProgPoint {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"progpoint{}{}",
|
||||
@@ -1422,9 +1435,9 @@ impl Output {
|
||||
// binary_search_by returns the index of where it would have
|
||||
// been inserted in Err.
|
||||
if pos < ProgPoint::before(inst_range.first()) {
|
||||
std::cmp::Ordering::Less
|
||||
core::cmp::Ordering::Less
|
||||
} else {
|
||||
std::cmp::Ordering::Greater
|
||||
core::cmp::Ordering::Greater
|
||||
}
|
||||
})
|
||||
.unwrap_err();
|
||||
@@ -1463,12 +1476,13 @@ pub enum RegAllocError {
|
||||
TooManyLiveRegs,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RegAllocError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
impl core::fmt::Display for RegAllocError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for RegAllocError {}
|
||||
|
||||
/// Run the allocator.
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
use crate::{ion::data_structures::u64_key, Allocation, PReg};
|
||||
use core::fmt::Debug;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// A list of moves to be performed in sequence, with auxiliary data
|
||||
/// attached to each.
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
//! Fast postorder computation.
|
||||
|
||||
use crate::Block;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
pub fn calculate<'a, SuccFn: Fn(Block) -> &'a [Block]>(
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
//! SSA-related utilities.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use alloc::vec;
|
||||
use hashbrown::HashSet;
|
||||
|
||||
use crate::cfg::CFGInfo;
|
||||
use crate::{Block, Function, Inst, OperandKind, RegAllocError, VReg};
|
||||
|
||||
Reference in New Issue
Block a user