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