Generate a constant hash table for recognizing opcodes.
Use a simple quadratically probed, open addressed hash table. We could use a parfect hash function, but it would take longer to compute in Python, and this is not in the critical path performancewise.
This commit is contained in:
@@ -8,8 +8,12 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::mem;
|
||||
|
||||
// The `Opcode` enum and the `opcode_name` function are generated from the meta instruction
|
||||
// descriptions.
|
||||
// Include code generated by `meta/gen_instr.py`. This file contains:
|
||||
//
|
||||
// - The `pub enum Opcode` definition with all known opcodes,
|
||||
// - The private `fn opcode_name(Opcode) -> &'static str` function, and
|
||||
// - The hash table `const OPCODE_HASH_TABLE: [Opcode; N]`.
|
||||
//
|
||||
include!(concat!(env!("OUT_DIR"), "/opcodes.rs"));
|
||||
|
||||
impl Display for Opcode {
|
||||
@@ -18,6 +22,46 @@ impl Display for Opcode {
|
||||
}
|
||||
}
|
||||
|
||||
// A primitive hash function for matching opcodes.
|
||||
// Must match `meta/constant_hash.py`.
|
||||
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
|
||||
}
|
||||
|
||||
impl Opcode {
|
||||
/// Parse an Opcode name from a string.
|
||||
pub fn from_str(s: &str) -> Option<Opcode> {
|
||||
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 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if *opcode_name(entry) == *s {
|
||||
return Some(entry);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 64-bit immediate integer operand.
|
||||
///
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
@@ -178,6 +222,13 @@ mod tests {
|
||||
|
||||
assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm");
|
||||
assert_eq!(format!("{}", Opcode::IaddImm), "iadd_imm");
|
||||
|
||||
// Check the matcher.
|
||||
assert_eq!(Opcode::from_str("iadd"), Some(Opcode::Iadd));
|
||||
assert_eq!(Opcode::from_str("iadd_imm"), Some(Opcode::IaddImm));
|
||||
assert_eq!(Opcode::from_str("iadd\0"), None);
|
||||
assert_eq!(Opcode::from_str(""), None);
|
||||
assert_eq!(Opcode::from_str("\0"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user