Merge branch 'master' of github.com:stoklund/cretonne
This commit is contained in:
74
cranelift/src/libcretonne/constant_hash.rs
Normal file
74
cranelift/src/libcretonne/constant_hash.rs
Normal file
@@ -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<K: Copy + Eq> {
|
||||||
|
/// 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<K>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 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<K: Copy + Eq, T: Table<K> + ?Sized>(table: &T, key: K, hash: usize) -> Option<usize> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,30 +64,25 @@ impl FromStr for Opcode {
|
|||||||
|
|
||||||
/// Parse an Opcode name from a string.
|
/// Parse an Opcode name from a string.
|
||||||
fn from_str(s: &str) -> Result<Opcode, &'static str> {
|
fn from_str(s: &str) -> Result<Opcode, &'static str> {
|
||||||
use simple_hash::simple_hash;
|
use constant_hash::{Table, simple_hash, probe};
|
||||||
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];
|
|
||||||
|
|
||||||
if entry == Opcode::NotAnOpcode {
|
impl<'a> Table<&'a str> for [Opcode] {
|
||||||
return Err("Unknown opcode");
|
fn len(&self) -> usize {
|
||||||
|
self.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
if *opcode_name(entry) == *s {
|
fn key(&self, idx: usize) -> Option<&'a str> {
|
||||||
return Ok(entry);
|
if self[idx] == Opcode::NotAnOpcode {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(opcode_name(self[idx]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Quadratic probing.
|
match probe::<&str, [Opcode]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) {
|
||||||
step += 1;
|
None => Err("Unknown opcode"),
|
||||||
// When `tlen` is a power of two, it can be proven that idx will visit all entries.
|
Some(i) => Ok(OPCODE_HASH_TABLE[i]),
|
||||||
// This means that this loop will always terminate if the hash table has even one
|
|
||||||
// unused entry.
|
|
||||||
assert!(step < tlen);
|
|
||||||
idx += step;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ pub mod dominator_tree;
|
|||||||
pub mod entity_map;
|
pub mod entity_map;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
mod simple_hash;
|
mod constant_hash;
|
||||||
|
|
||||||
#[cfg(test)]pub mod test_utils;
|
#[cfg(test)]pub mod test_utils;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
|
||||||
use simple_hash::simple_hash;
|
use constant_hash::{probe, simple_hash};
|
||||||
|
|
||||||
/// A string-based configurator for settings groups.
|
/// A string-based configurator for settings groups.
|
||||||
///
|
///
|
||||||
@@ -75,27 +75,12 @@ impl Builder {
|
|||||||
|
|
||||||
/// Look up a descriptor by name.
|
/// Look up a descriptor by name.
|
||||||
fn lookup(&self, name: &str) -> Result<(usize, detail::Detail)> {
|
fn lookup(&self, name: &str) -> Result<(usize, detail::Detail)> {
|
||||||
let table = self.template.hash_table;
|
match probe(self.template, name, simple_hash(name)) {
|
||||||
let descs = self.template.descriptors;
|
None => Err(Error::BadName),
|
||||||
let mask = table.len() - 1;
|
Some(entry) => {
|
||||||
assert!((mask + 1).is_power_of_two());
|
let d = &self.template.descriptors[self.template.hash_table[entry] as usize];
|
||||||
|
Ok((d.offset as usize, d.detail))
|
||||||
let mut idx = simple_hash(name) as usize;
|
|
||||||
let mut step: usize = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
idx = idx & mask;
|
|
||||||
let entry = table[idx] as usize;
|
|
||||||
if entry >= descs.len() {
|
|
||||||
return Err(Error::BadName);
|
|
||||||
}
|
}
|
||||||
let desc = &descs[entry];
|
|
||||||
if desc.name == name {
|
|
||||||
return Ok((desc.offset as usize, desc.detail));
|
|
||||||
}
|
|
||||||
step += 1;
|
|
||||||
assert!(step <= mask);
|
|
||||||
idx += step;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,6 +152,7 @@ pub type Result<T> = result::Result<T, Error>;
|
|||||||
/// code in other modules.
|
/// code in other modules.
|
||||||
pub mod detail {
|
pub mod detail {
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use constant_hash;
|
||||||
|
|
||||||
/// An instruction group template.
|
/// An instruction group template.
|
||||||
pub struct Template {
|
pub struct Template {
|
||||||
@@ -207,6 +193,22 @@ pub mod detail {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The template contains a hash table for by-name lookup.
|
||||||
|
impl<'a> constant_hash::Table<&'a str> for Template {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.hash_table.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key(&self, idx: usize) -> Option<&'a str> {
|
||||||
|
let e = self.hash_table[idx] as usize;
|
||||||
|
if e < self.descriptors.len() {
|
||||||
|
Some(self.descriptors[e].name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A setting descriptor holds the information needed to generically set and print a setting.
|
/// A setting descriptor holds the information needed to generically set and print a setting.
|
||||||
///
|
///
|
||||||
/// Each settings group will be represented as a constant DESCRIPTORS array.
|
/// Each settings group will be represented as a constant DESCRIPTORS array.
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user