diff --git a/lib/cretonne/src/entity/mod.rs b/lib/cretonne/src/entity/mod.rs index 6702c43dab..4fc40ea56e 100644 --- a/lib/cretonne/src/entity/mod.rs +++ b/lib/cretonne/src/entity/mod.rs @@ -4,8 +4,8 @@ //! `usize` as usual, but by *entity references* which are integers wrapped in new-types. This has //! a couple advantages: //! -//! - Improved type safety. The various map types accept a specific key type, so there is no -//! confusion about the meaning of an array index, as there is with plain arrays. +//! - Improved type safety. The various map and set types accept a specific key type, so there is +//! no confusion about the meaning of an array index, as there is with plain arrays. //! - Smaller indexes. The normal `usize` index is often 64 bits which is way too large for most //! purposes. The entity reference types can be smaller, allowing for more compact data //! structures. @@ -22,6 +22,9 @@ //! number of entities. It tracks accurately which entities have been inserted. This is a //! specialized data structure which can use a lot of memory, so read the documentation before //! using it. +//! - [`EntitySet`](struct.EntitySet.html) is used to represent a secondary set of entities. +//! The set is implemented as a simple vector, so it does not keep track of which entities have +//! been inserted into the primary map. Instead, any unknown entities are not in the set. //! - [`EntityList`](struct.EntityList.html) is a compact representation of lists of entity //! references allocated from an associated memory pool. It has a much smaller footprint than //! `Vec`. @@ -31,11 +34,13 @@ mod list; mod map; mod primary; mod sparse; +mod set; pub use self::keys::Keys; pub use self::list::{EntityList, ListPool}; pub use self::map::EntityMap; pub use self::primary::PrimaryMap; +pub use self::set::EntitySet; pub use self::sparse::{SparseSet, SparseMap, SparseMapValue}; /// A type wrapping a small integer index should implement `EntityRef` so it can be used as the key diff --git a/lib/cretonne/src/entity/set.rs b/lib/cretonne/src/entity/set.rs new file mode 100644 index 0000000000..be9d76bb4e --- /dev/null +++ b/lib/cretonne/src/entity/set.rs @@ -0,0 +1,144 @@ +//! Densely numbered entity references as set keys. + +use entity::{EntityRef, Keys}; +use std::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 `EntityMap`, an `EntitySet` is used to associate secondary information with entities. +#[derive(Debug, Clone)] +pub struct EntitySet +where + K: EntityRef, +{ + elems: Vec, + len: usize, + unused: PhantomData, +} + +/// Shared `EntitySet` implementation for all value types. +impl EntitySet +where + K: EntityRef, +{ + /// Create a new empty set. + pub fn new() -> Self { + EntitySet { + elems: Vec::new(), + len: 0, + unused: PhantomData, + } + } + + /// Get the element at `k` if it exists. + pub fn contains(&self, k: K) -> bool { + let index = k.index(); + debug_assert!(index < self.len); + (self.elems[index / 8] & (1 << (index % 8))) != 0 + } + + /// Is this set completely empty? + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// 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 { + Keys::new(self.len) + } + + /// Resize the set to have `n` entries by adding default entries as needed. + pub fn resize(&mut self, n: usize) { + if n < self.len { + self.elems.truncate((n + 7) / 8) + } else { + // TODO: Is there a better way to grow/resize/etc.? + let additional = (n - self.len + 7) / 8; + self.elems.reserve(additional); + for _ in 0..additional { + self.elems.push(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 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // `EntityRef` impl for testing. + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + 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 = 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.is_empty()); + + let v: Vec = 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))); + + m.clear(); + assert!(m.is_empty()); + } +} diff --git a/lib/frontend/src/frontend.rs b/lib/frontend/src/frontend.rs index c32a824158..beb5dfdcfe 100644 --- a/lib/frontend/src/frontend.rs +++ b/lib/frontend/src/frontend.rs @@ -6,8 +6,7 @@ use cretonne::ir::instructions::BranchInfo; use cretonne::ir::function::DisplayFunction; use cretonne::isa::TargetIsa; use ssa::{SSABuilder, SideEffects, Block}; -use cretonne::entity::{EntityRef, EntityMap}; -use std::collections::HashSet; +use cretonne::entity::{EntityRef, EntityMap, EntitySet}; use std::hash::Hash; /// Permanent structure used for translating into Cretonne IL. @@ -151,8 +150,8 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short if let InstructionData::BranchTable { table, .. } = data { // Unlike all other jumps/branches, jump tables are // capable of having the same successor appear -// multiple times. Use a HashSet to deduplicate. - let mut unique = HashSet::new(); +// multiple times, so we must deduplicate. + let mut unique = EntitySet::::new(); for dest_ebb in self.builder .func .jump_tables