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:
Amanieu d'Antras
2023-03-09 20:25:59 +01:00
committed by GitHub
parent 7354cfedde
commit 2bd03256b3
23 changed files with 176 additions and 115 deletions

View File

@@ -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(),
}
}

View File

@@ -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};

View File

@@ -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)

View File

@@ -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![],
);

View File

@@ -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(),

View File

@@ -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!(

View File

@@ -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

View File

@@ -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)]

View File

@@ -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> {

View File

@@ -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

View File

@@ -12,6 +12,8 @@
//! Stackmap computation.
use alloc::vec::Vec;
use super::{Env, ProgPoint, VRegIndex};
use crate::{ion::data_structures::u64_key, Function};