Introduce EntitySet, and use it to replace the HashSet in the frontend.

This commit is contained in:
Dan Gohman
2017-09-01 10:13:41 -07:00
parent fe12fe0e63
commit d4c53935b4
3 changed files with 154 additions and 6 deletions

View File

@@ -4,8 +4,8 @@
//! `usize` as usual, but by *entity references* which are integers wrapped in new-types. This has //! `usize` as usual, but by *entity references* which are integers wrapped in new-types. This has
//! a couple advantages: //! a couple advantages:
//! //!
//! - Improved type safety. The various map types accept a specific key type, so there is no //! - Improved type safety. The various map and set types accept a specific key type, so there is
//! confusion about the meaning of an array index, as there is with plain arrays. //! 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 //! - 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 //! purposes. The entity reference types can be smaller, allowing for more compact data
//! structures. //! structures.
@@ -22,6 +22,9 @@
//! number of entities. It tracks accurately which entities have been inserted. This is a //! 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 //! specialized data structure which can use a lot of memory, so read the documentation before
//! using it. //! 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 //! - [`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 //! references allocated from an associated memory pool. It has a much smaller footprint than
//! `Vec`. //! `Vec`.
@@ -31,11 +34,13 @@ mod list;
mod map; mod map;
mod primary; mod primary;
mod sparse; mod sparse;
mod set;
pub use self::keys::Keys; pub use self::keys::Keys;
pub use self::list::{EntityList, ListPool}; pub use self::list::{EntityList, ListPool};
pub use self::map::EntityMap; pub use self::map::EntityMap;
pub use self::primary::PrimaryMap; pub use self::primary::PrimaryMap;
pub use self::set::EntitySet;
pub use self::sparse::{SparseSet, SparseMap, SparseMapValue}; 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 /// A type wrapping a small integer index should implement `EntityRef` so it can be used as the key

View File

@@ -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<K>
where
K: EntityRef,
{
elems: Vec<u8>,
len: usize,
unused: PhantomData<K>,
}
/// Shared `EntitySet` implementation for all value types.
impl<K> EntitySet<K>
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<K> {
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<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.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)));
m.clear();
assert!(m.is_empty());
}
}

View File

@@ -6,8 +6,7 @@ use cretonne::ir::instructions::BranchInfo;
use cretonne::ir::function::DisplayFunction; use cretonne::ir::function::DisplayFunction;
use cretonne::isa::TargetIsa; use cretonne::isa::TargetIsa;
use ssa::{SSABuilder, SideEffects, Block}; use ssa::{SSABuilder, SideEffects, Block};
use cretonne::entity::{EntityRef, EntityMap}; use cretonne::entity::{EntityRef, EntityMap, EntitySet};
use std::collections::HashSet;
use std::hash::Hash; use std::hash::Hash;
/// Permanent structure used for translating into Cretonne IL. /// 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 { if let InstructionData::BranchTable { table, .. } = data {
// Unlike all other jumps/branches, jump tables are // Unlike all other jumps/branches, jump tables are
// capable of having the same successor appear // capable of having the same successor appear
// multiple times. Use a HashSet to deduplicate. // multiple times, so we must deduplicate.
let mut unique = HashSet::new(); let mut unique = EntitySet::<Ebb>::new();
for dest_ebb in self.builder for dest_ebb in self.builder
.func .func
.jump_tables .jump_tables