diff --git a/cranelift/src/libcretonne/constant_hash.rs b/cranelift/src/libcretonne/constant_hash.rs new file mode 100644 index 0000000000..fd8115c7fa --- /dev/null +++ b/cranelift/src/libcretonne/constant_hash.rs @@ -0,0 +1,74 @@ +//! Runtime support for precomputed constant hash tables. +//! +//! The `meta/constant_hash.py` Python module can generate constant hash tables using open +//! addressing and quadratic probing. The hash tables are arrays that are guaranteed to: +//! +//! - Have a power-of-two size. +//! - Contain at least one empty slot. +//! +//! This module provides runtime support for lookups in these tables. + +/// Trait that must be implemented by the entries in a constant hash table. +pub trait Table { + /// Get the number of entries in this table which must be a power of two. + fn len(&self) -> usize; + + /// Get the key corresponding to the entry at `idx`, or `None` if the entry is empty. + /// The `idx` must be in range. + fn key(&self, idx: usize) -> Option; +} + + +/// Look for `key` in `table`. +/// +/// The provided `hash` value must have been computed from `key` using the same hash function that +/// was used to construct the table. +/// +/// Returns the table index containing the found entry, or `None` if no entry could be found. +pub fn probe + ?Sized>(table: &T, key: K, hash: usize) -> Option { + debug_assert!(table.len().is_power_of_two()); + let mask = table.len() - 1; + + let mut idx = hash; + let mut step = 0; + + loop { + idx &= mask; + + match table.key(idx) { + None => return None, + Some(k) if k == key => return Some(idx), + _ => {} + } + + // Quadratic probing. + step += 1; + // When `table.len()` is a power of two, it can be proven that `idx` will visit all + // entries. This means that this loop will always terminate if the hash table has even + // one unused entry. + debug_assert!(step < table.len()); + idx += step; + } +} + +/// A primitive hash function for matching opcodes. +/// Must match `meta/constant_hash.py`. +pub fn simple_hash(s: &str) -> usize { + let mut h: u32 = 5381; + for c in s.chars() { + h = (h ^ c as u32).wrapping_add(h.rotate_right(6)); + } + h as usize +} + +#[cfg(test)] +mod tests { + use super::simple_hash; + + #[test] + fn basic() { + // c.f. meta/constant_hash.py tests. + assert_eq!(simple_hash("Hello"), 0x2fa70c01); + assert_eq!(simple_hash("world"), 0x5b0c31d5); + } +} diff --git a/cranelift/src/libcretonne/ir/instructions.rs b/cranelift/src/libcretonne/ir/instructions.rs index eee47b8d66..ca43a3dc31 100644 --- a/cranelift/src/libcretonne/ir/instructions.rs +++ b/cranelift/src/libcretonne/ir/instructions.rs @@ -64,30 +64,25 @@ impl FromStr for Opcode { /// Parse an Opcode name from a string. fn from_str(s: &str) -> Result { - use simple_hash::simple_hash; - let tlen = OPCODE_HASH_TABLE.len(); - assert!(tlen.is_power_of_two()); - let mut idx = simple_hash(s) as usize; - let mut step: usize = 0; - loop { - idx = idx % tlen; - let entry = OPCODE_HASH_TABLE[idx]; + use constant_hash::{Table, simple_hash, probe}; - if entry == Opcode::NotAnOpcode { - return Err("Unknown opcode"); + impl<'a> Table<&'a str> for [Opcode] { + fn len(&self) -> usize { + self.len() } - if *opcode_name(entry) == *s { - return Ok(entry); + fn key(&self, idx: usize) -> Option<&'a str> { + if self[idx] == Opcode::NotAnOpcode { + None + } else { + Some(opcode_name(self[idx])) + } } + } - // Quadratic probing. - step += 1; - // When `tlen` is a power of two, it can be proven that idx will visit all entries. - // This means that this loop will always terminate if the hash table has even one - // unused entry. - assert!(step < tlen); - idx += step; + match probe::<&str, [Opcode]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) { + None => Err("Unknown opcode"), + Some(i) => Ok(OPCODE_HASH_TABLE[i]), } } } diff --git a/cranelift/src/libcretonne/lib.rs b/cranelift/src/libcretonne/lib.rs index a8cd09ab8d..a4ed7dfecb 100644 --- a/cranelift/src/libcretonne/lib.rs +++ b/cranelift/src/libcretonne/lib.rs @@ -15,6 +15,6 @@ pub mod dominator_tree; pub mod entity_map; pub mod settings; -mod simple_hash; +mod constant_hash; #[cfg(test)]pub mod test_utils; diff --git a/cranelift/src/libcretonne/settings.rs b/cranelift/src/libcretonne/settings.rs index c65c5eeb68..72e8f06a58 100644 --- a/cranelift/src/libcretonne/settings.rs +++ b/cranelift/src/libcretonne/settings.rs @@ -23,7 +23,7 @@ use std::fmt; use std::result; -use simple_hash::simple_hash; +use constant_hash::simple_hash; /// A string-based configurator for settings groups. /// diff --git a/cranelift/src/libcretonne/simple_hash.rs b/cranelift/src/libcretonne/simple_hash.rs deleted file mode 100644 index 99cd80015c..0000000000 --- a/cranelift/src/libcretonne/simple_hash.rs +++ /dev/null @@ -1,21 +0,0 @@ -/// A primitive hash function for matching opcodes. -/// Must match `meta/constant_hash.py`. -pub fn simple_hash(s: &str) -> u32 { - let mut h: u32 = 5381; - for c in s.chars() { - h = (h ^ c as u32).wrapping_add(h.rotate_right(6)); - } - h -} - -#[cfg(test)] -mod tests { - use super::simple_hash; - - #[test] - fn basic() { - // c.f. meta/constant_hash.py tests. - assert_eq!(simple_hash("Hello"), 0x2fa70c01); - assert_eq!(simple_hash("world"), 0x5b0c31d5); - } -}