Merge branch 'master' of github.com:stoklund/cretonne

This commit is contained in:
Morgan Phillips
2016-08-18 14:39:02 -07:00
5 changed files with 112 additions and 62 deletions

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

View File

@@ -64,30 +64,25 @@ impl FromStr for Opcode {
/// Parse an Opcode name from a string.
fn from_str(s: &str) -> Result<Opcode, &'static str> {
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]),
}
}
}

View File

@@ -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;

View File

@@ -23,7 +23,7 @@
use std::fmt;
use std::result;
use simple_hash::simple_hash;
use constant_hash::{probe, simple_hash};
/// A string-based configurator for settings groups.
///
@@ -75,27 +75,12 @@ impl Builder {
/// Look up a descriptor by name.
fn lookup(&self, name: &str) -> Result<(usize, detail::Detail)> {
let table = self.template.hash_table;
let descs = self.template.descriptors;
let mask = table.len() - 1;
assert!((mask + 1).is_power_of_two());
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);
match probe(self.template, name, simple_hash(name)) {
None => Err(Error::BadName),
Some(entry) => {
let d = &self.template.descriptors[self.template.hash_table[entry] as usize];
Ok((d.offset as usize, d.detail))
}
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.
pub mod detail {
use std::fmt;
use constant_hash;
/// An instruction group 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.
///
/// Each settings group will be represented as a constant DESCRIPTORS array.

View File

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