Files
wasmtime/cranelift/entity/src/set.rs
Jamey Sharp 77ab99d3b0 cranelift-frontend: SSA-building cleanup (#4984)
* 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
2022-09-29 16:59:47 -07:00

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());
}
}