* Cleanups to cranelift-frontend SSA construction * Encode sealed/undef_variables relationship in type A block can't have any undef_variables if it is sealed. It's useful to make that fact explicit in the types so that any time either value is used, it's clear that we should think about the other one too. In addition, encoding this fact in an enum type lets Rust apply an optimization that reduces the size of SSABlockData by 8 bytes, making it fit in a 64-byte cache line. I haven't taken the extra step of making SSABlockData be 64-byte aligned because 1) it doesn't seem to have a performance impact and b) doing so makes other structures quite a bit bigger. * Simplify finish_predecessors_lookup Using Vec::drain is more concise than a combination of iter().rev().take() followed by Vec::truncate. And in this case it doesn't matter what order we examine the results in, because we just want to know if they're all equal, so we might as well iterate forward instead of in reverse. There's no need for the ZeroOneOrMore enum. Instead, there are only two cases: either we have a single value to use for the variable (possibly synthesized as a constant zero), or we need to add a block parameter in every predecessor. Pre-filtering the results iterator to eliminate the sentinel makes it easy to identify how many distinct definitions this variable has. iter.next() indicates if there are any definitions at all, and then iter.all() is a clear way to express that we want to know if the remaining definitions are the same as the first one. * Simplify append_jump_argument * Avoid assigning default() into SecondaryMap This eliminates some redundant reads and writes. * cranelift-frontend: Construct with default() This eliminates a bunch of boilerplate in favor of a built in `derive` macro. Also I'm deleting an import that had the comment "FIXME: Remove in edition2021", which we've been using everywhere since April. * Fix tests
253 lines
6.4 KiB
Rust
253 lines
6.4 KiB
Rust
//! Densely numbered entity references as set keys.
|
|
|
|
use crate::keys::Keys;
|
|
use crate::EntityRef;
|
|
use alloc::vec::Vec;
|
|
use core::marker::PhantomData;
|
|
|
|
/// A set of `K` for densely indexed entity references.
|
|
///
|
|
/// The `EntitySet` data structure uses the dense index space to implement a set with a bitvector.
|
|
/// Like `SecondaryMap`, an `EntitySet` is used to associate secondary information with entities.
|
|
#[derive(Debug, Clone)]
|
|
pub struct EntitySet<K>
|
|
where
|
|
K: EntityRef,
|
|
{
|
|
elems: Vec<u8>,
|
|
len: usize,
|
|
unused: PhantomData<K>,
|
|
}
|
|
|
|
impl<K: EntityRef> Default for EntitySet<K> {
|
|
fn default() -> Self {
|
|
Self {
|
|
elems: Vec::new(),
|
|
len: 0,
|
|
unused: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Shared `EntitySet` implementation for all value types.
|
|
impl<K> EntitySet<K>
|
|
where
|
|
K: EntityRef,
|
|
{
|
|
/// Create a new empty set.
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// Creates a new empty set with the specified capacity.
|
|
pub fn with_capacity(capacity: usize) -> Self {
|
|
Self {
|
|
elems: Vec::with_capacity((capacity + 7) / 8),
|
|
..Self::new()
|
|
}
|
|
}
|
|
|
|
/// Get the element at `k` if it exists.
|
|
pub fn contains(&self, k: K) -> bool {
|
|
let index = k.index();
|
|
if index < self.len {
|
|
(self.elems[index / 8] & (1 << (index % 8))) != 0
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
/// Is this set completely empty?
|
|
pub fn is_empty(&self) -> bool {
|
|
if self.len != 0 {
|
|
false
|
|
} else {
|
|
self.elems.iter().all(|&e| e == 0)
|
|
}
|
|
}
|
|
|
|
/// Returns the cardinality of the set. More precisely, it returns the number of calls to
|
|
/// `insert` with different key values, that have happened since the the set was most recently
|
|
/// `clear`ed or created with `new`.
|
|
pub fn cardinality(&self) -> usize {
|
|
let mut n: usize = 0;
|
|
for byte_ix in 0..self.len / 8 {
|
|
n += self.elems[byte_ix].count_ones() as usize;
|
|
}
|
|
for bit_ix in (self.len / 8) * 8..self.len {
|
|
if (self.elems[bit_ix / 8] & (1 << (bit_ix % 8))) != 0 {
|
|
n += 1;
|
|
}
|
|
}
|
|
n
|
|
}
|
|
|
|
/// Remove all entries from this set.
|
|
pub fn clear(&mut self) {
|
|
self.len = 0;
|
|
self.elems.clear()
|
|
}
|
|
|
|
/// Iterate over all the keys in this set.
|
|
pub fn keys(&self) -> Keys<K> {
|
|
Keys::with_len(self.len)
|
|
}
|
|
|
|
/// Resize the set to have `n` entries by adding default entries as needed.
|
|
pub fn resize(&mut self, n: usize) {
|
|
self.elems.resize((n + 7) / 8, 0);
|
|
self.len = n
|
|
}
|
|
|
|
/// Insert the element at `k`.
|
|
pub fn insert(&mut self, k: K) -> bool {
|
|
let index = k.index();
|
|
if index >= self.len {
|
|
self.resize(index + 1)
|
|
}
|
|
let result = !self.contains(k);
|
|
self.elems[index / 8] |= 1 << (index % 8);
|
|
result
|
|
}
|
|
|
|
/// Removes and returns the entity from the set if it exists.
|
|
pub fn pop(&mut self) -> Option<K> {
|
|
if self.len == 0 {
|
|
return None;
|
|
}
|
|
|
|
// Clear the last known entity in the list.
|
|
let last_index = self.len - 1;
|
|
self.elems[last_index / 8] &= !(1 << (last_index % 8));
|
|
|
|
// Set the length to the next last stored entity or zero if we pop'ed
|
|
// the last entity.
|
|
self.len = self
|
|
.elems
|
|
.iter()
|
|
.enumerate()
|
|
.rev()
|
|
.find(|(_, &byte)| byte != 0)
|
|
// Map `i` from byte index to bit level index.
|
|
// `(i + 1) * 8` = Last bit in byte.
|
|
// `last - byte.leading_zeros()` = last set bit in byte.
|
|
// `as usize` won't ever truncate as the potential range is `0..=8`.
|
|
.map_or(0, |(i, byte)| ((i + 1) * 8) - byte.leading_zeros() as usize);
|
|
|
|
Some(K::new(last_index))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use core::u32;
|
|
|
|
// `EntityRef` impl for testing.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
struct E(u32);
|
|
|
|
impl EntityRef for E {
|
|
fn new(i: usize) -> Self {
|
|
E(i as u32)
|
|
}
|
|
fn index(self) -> usize {
|
|
self.0 as usize
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn basic() {
|
|
let r0 = E(0);
|
|
let r1 = E(1);
|
|
let r2 = E(2);
|
|
let mut m = EntitySet::new();
|
|
|
|
let v: Vec<E> = m.keys().collect();
|
|
assert_eq!(v, []);
|
|
assert!(m.is_empty());
|
|
|
|
m.insert(r2);
|
|
m.insert(r1);
|
|
|
|
assert!(!m.contains(r0));
|
|
assert!(m.contains(r1));
|
|
assert!(m.contains(r2));
|
|
assert!(!m.contains(E(3)));
|
|
assert!(!m.is_empty());
|
|
|
|
let v: Vec<E> = m.keys().collect();
|
|
assert_eq!(v, [r0, r1, r2]);
|
|
|
|
m.resize(20);
|
|
assert!(!m.contains(E(3)));
|
|
assert!(!m.contains(E(4)));
|
|
assert!(!m.contains(E(8)));
|
|
assert!(!m.contains(E(15)));
|
|
assert!(!m.contains(E(19)));
|
|
|
|
m.insert(E(8));
|
|
m.insert(E(15));
|
|
assert!(!m.contains(E(3)));
|
|
assert!(!m.contains(E(4)));
|
|
assert!(m.contains(E(8)));
|
|
assert!(!m.contains(E(9)));
|
|
assert!(!m.contains(E(14)));
|
|
assert!(m.contains(E(15)));
|
|
assert!(!m.contains(E(16)));
|
|
assert!(!m.contains(E(19)));
|
|
assert!(!m.contains(E(20)));
|
|
assert!(!m.contains(E(u32::MAX)));
|
|
|
|
m.clear();
|
|
assert!(m.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn pop_ordered() {
|
|
let r0 = E(0);
|
|
let r1 = E(1);
|
|
let r2 = E(2);
|
|
let mut m = EntitySet::new();
|
|
m.insert(r0);
|
|
m.insert(r1);
|
|
m.insert(r2);
|
|
|
|
assert_eq!(r2, m.pop().unwrap());
|
|
assert_eq!(r1, m.pop().unwrap());
|
|
assert_eq!(r0, m.pop().unwrap());
|
|
assert!(m.pop().is_none());
|
|
assert!(m.pop().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn pop_unordered() {
|
|
let mut blocks = [
|
|
E(0),
|
|
E(1),
|
|
E(6),
|
|
E(7),
|
|
E(5),
|
|
E(9),
|
|
E(10),
|
|
E(2),
|
|
E(3),
|
|
E(11),
|
|
E(12),
|
|
];
|
|
|
|
let mut m = EntitySet::new();
|
|
for &block in &blocks {
|
|
m.insert(block);
|
|
}
|
|
assert_eq!(m.len, 13);
|
|
blocks.sort();
|
|
|
|
for &block in blocks.iter().rev() {
|
|
assert_eq!(block, m.pop().unwrap());
|
|
}
|
|
|
|
assert!(m.is_empty());
|
|
}
|
|
}
|