Share constant_hash code between the meta and codegen crates;

This commit is contained in:
Benjamin Bouvier
2019-10-09 16:35:33 +02:00
parent c062f12d7c
commit d404368dea
8 changed files with 94 additions and 84 deletions

View File

@@ -1,55 +0,0 @@
use std::iter;
pub(crate) 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
}
/// Compute an open addressed, quadratically probed hash table containing
/// `items`. The returned table is a list containing the elements of the
/// iterable `items` and `None` in unused slots.
pub(crate) fn generate_table<'cont, T, I: iter::Iterator<Item = &'cont T>, H: Fn(&T) -> usize>(
items: I,
num_items: usize,
hash_function: H,
) -> Vec<Option<&'cont T>> {
let size = (1.20 * num_items as f64) as usize;
// TODO do we really need the multiply by two here?
let size = if size.is_power_of_two() {
size * 2
} else {
size.next_power_of_two()
};
let mut table = vec![None; size];
for i in items {
let mut h = hash_function(&i) % size;
let mut s = 0;
while table[h].is_some() {
s += 1;
h = (h + s) % size;
}
table[h] = Some(i);
}
table
}
#[test]
fn test_generate_table() {
let v = vec!["Hello".to_string(), "world".to_string()];
let table = generate_table(v.iter(), v.len(), |s| simple_hash(&s));
assert_eq!(
table,
vec![
None,
Some(&"Hello".to_string()),
Some(&"world".to_string()),
None
]
);
}

View File

@@ -49,6 +49,7 @@ use std::collections::{BTreeMap, HashMap, HashSet};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::iter::FromIterator; use std::iter::FromIterator;
use cranelift_codegen_shared::constant_hash::generate_table;
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use crate::error; use crate::error;
@@ -66,7 +67,6 @@ use crate::cdsl::xform::TransformGroupIndex;
use crate::shared::Definitions as SharedDefinitions; use crate::shared::Definitions as SharedDefinitions;
use crate::constant_hash::generate_table;
use crate::default_map::MapWithDefault; use crate::default_map::MapWithDefault;
use crate::unique_table::UniqueSeqTable; use crate::unique_table::UniqueSeqTable;

View File

@@ -1,5 +1,6 @@
use std::fmt; use std::fmt;
use cranelift_codegen_shared::constant_hash;
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use crate::cdsl::camel_case; use crate::cdsl::camel_case;
@@ -10,7 +11,6 @@ use crate::cdsl::typevar::{TypeSet, TypeVar};
use crate::shared::Definitions as SharedDefinitions; use crate::shared::Definitions as SharedDefinitions;
use crate::constant_hash;
use crate::error; use crate::error;
use crate::srcgen::{Formatter, Match}; use crate::srcgen::{Formatter, Match};
use crate::unique_table::{UniqueSeqTable, UniqueTable}; use crate::unique_table::{UniqueSeqTable, UniqueTable};

View File

@@ -1,12 +1,14 @@
use std::collections::HashMap;
use cranelift_codegen_shared::constant_hash::{generate_table, simple_hash};
use crate::cdsl::camel_case; use crate::cdsl::camel_case;
use crate::cdsl::settings::{ use crate::cdsl::settings::{
BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting, BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting,
}; };
use crate::constant_hash::{generate_table, simple_hash};
use crate::error; use crate::error;
use crate::srcgen::{Formatter, Match}; use crate::srcgen::{Formatter, Match};
use crate::unique_table::UniqueSeqTable; use crate::unique_table::UniqueSeqTable;
use std::collections::HashMap;
pub enum ParentGroup { pub enum ParentGroup {
None, None,

View File

@@ -13,7 +13,6 @@ mod gen_registers;
mod gen_settings; mod gen_settings;
mod gen_types; mod gen_types;
mod constant_hash;
mod default_map; mod default_map;
mod shared; mod shared;
mod unique_table; mod unique_table;

View File

@@ -0,0 +1,79 @@
//! Build support for precomputed constant hash tables.
//!
//! This 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 build meta support for lookups in these tables, as well as the shared hash
//! function used for probing.
use std::iter;
/// A primitive hash function for matching opcodes.
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
}
/// Compute an open addressed, quadratically probed hash table containing
/// `items`. The returned table is a list containing the elements of the
/// iterable `items` and `None` in unused slots.
pub fn generate_table<'cont, T, I: iter::Iterator<Item = &'cont T>, H: Fn(&T) -> usize>(
items: I,
num_items: usize,
hash_function: H,
) -> Vec<Option<&'cont T>> {
let size = (1.20 * num_items as f64) as usize;
// TODO do we really need the multiply by two here?
let size = if size.is_power_of_two() {
size * 2
} else {
size.next_power_of_two()
};
let mut table = vec![None; size];
for i in items {
let mut h = hash_function(&i) % size;
let mut s = 0;
while table[h].is_some() {
s += 1;
h = (h + s) % size;
}
table[h] = Some(i);
}
table
}
#[cfg(test)]
mod tests {
use super::{generate_table, simple_hash};
#[test]
fn basic() {
assert_eq!(simple_hash("Hello"), 0x2fa70c01);
assert_eq!(simple_hash("world"), 0x5b0c31d5);
}
#[test]
fn test_generate_table() {
let v = vec!["Hello".to_string(), "world".to_string()];
let table = generate_table(v.iter(), v.len(), |s| simple_hash(&s));
assert_eq!(
table,
vec![
None,
Some(&"Hello".to_string()),
Some(&"world".to_string()),
None
]
);
}
}

View File

@@ -21,6 +21,7 @@
)] )]
pub mod condcodes; pub mod condcodes;
pub mod constant_hash;
/// Version number of this crate. /// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -1,13 +1,18 @@
//! Runtime support for precomputed constant hash tables. //! Runtime support for precomputed constant hash tables.
//! //!
//! The `cranelift-codegen/meta/src/constant_hash.rs` Rust crate can generate constant hash tables //! The shared module with the same name can generate constant hash tables using open addressing
//! using open addressing and quadratic probing. The hash tables are arrays that are guaranteed to: //! and quadratic probing.
//!
//! The hash tables are arrays that are guaranteed to:
//! //!
//! - Have a power-of-two size. //! - Have a power-of-two size.
//! - Contain at least one empty slot. //! - Contain at least one empty slot.
//! //!
//! This module provides runtime support for lookups in these tables. //! This module provides runtime support for lookups in these tables.
// Re-export entities from constant_hash for simplicity of use.
pub use cranelift_codegen_shared::constant_hash::*;
/// Trait that must be implemented by the entries in a constant hash table. /// Trait that must be implemented by the entries in a constant hash table.
pub trait Table<K: Copy + Eq> { pub trait Table<K: Copy + Eq> {
/// Get the number of entries in this table which must be a power of two. /// Get the number of entries in this table which must be a power of two.
@@ -47,6 +52,7 @@ pub fn probe<K: Copy + Eq, T: Table<K> + ?Sized>(
// Quadratic probing. // Quadratic probing.
step += 1; step += 1;
// When `table.len()` is a power of two, it can be proven that `idx` will visit all // 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 // entries. This means that this loop will always terminate if the hash table has even
// one unused entry. // one unused entry.
@@ -54,25 +60,3 @@ pub fn probe<K: Copy + Eq, T: Table<K> + ?Sized>(
idx += step; idx += step;
} }
} }
/// A primitive hash function for matching opcodes.
/// Must match `cranelift-codegen/meta/src/constant_hash.rs`.
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/src/constant_hash.rs` tests.
assert_eq!(simple_hash("Hello"), 0x2fa70c01);
assert_eq!(simple_hash("world"), 0x5b0c31d5);
}
}