[meta] Remove name lookups in formats;

This does a lot at once, since there was no clear way to split the three
commits:

- Instruction need to be passed an explicit InstructionFormat,
- InstructionFormat deduplication is checked once all entities have been
defined;
This commit is contained in:
Benjamin Bouvier
2019-10-18 19:24:03 +02:00
parent 9e9a7626d7
commit 0243b642e3
17 changed files with 1002 additions and 730 deletions

View File

@@ -1,9 +1,6 @@
use crate::cdsl::operands::{Operand, OperandKind};
use std::collections::{HashMap, HashSet};
use crate::cdsl::operands::OperandKind;
use std::fmt;
use std::rc::Rc;
use std::slice;
/// An immediate field in an instruction format.
///
@@ -30,7 +27,7 @@ pub struct FormatField {
///
/// All instruction formats must be predefined in the meta shared/formats.rs module.
#[derive(Debug)]
pub struct InstructionFormat {
pub(crate) struct InstructionFormat {
/// Instruction format name in CamelCase. This is used as a Rust variant name in both the
/// `InstructionData` and `InstructionFormat` enums.
pub name: &'static str,
@@ -47,6 +44,14 @@ pub struct InstructionFormat {
pub typevar_operand: Option<usize>,
}
/// A tuple serving as a key to deduplicate InstructionFormat.
#[derive(Hash, PartialEq, Eq)]
pub(crate) struct FormatStructure {
pub num_value_operands: usize,
pub has_value_list: bool,
pub imm_field_names: Vec<&'static str>,
}
impl fmt::Display for InstructionFormat {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let imm_args = self
@@ -75,9 +80,22 @@ impl InstructionFormat {
)
})
}
/// Returns a tuple that uniquely identifies the structure.
pub fn structure(&self) -> FormatStructure {
FormatStructure {
num_value_operands: self.num_value_operands,
has_value_list: self.has_value_list,
imm_field_names: self
.imm_fields
.iter()
.map(|field| field.kind.name)
.collect::<Vec<_>>(),
}
}
}
pub struct InstructionFormatBuilder {
pub(crate) struct InstructionFormatBuilder {
name: &'static str,
num_value_operands: usize,
has_value_list: bool,
@@ -131,7 +149,7 @@ impl InstructionFormatBuilder {
self
}
pub fn build(self) -> InstructionFormat {
pub fn build(self) -> Rc<InstructionFormat> {
let typevar_operand = if self.typevar_operand.is_some() {
self.typevar_operand
} else if self.has_value_list || self.num_value_operands > 0 {
@@ -141,98 +159,12 @@ impl InstructionFormatBuilder {
None
};
InstructionFormat {
Rc::new(InstructionFormat {
name: self.name,
num_value_operands: self.num_value_operands,
has_value_list: self.has_value_list,
imm_fields: self.imm_fields,
typevar_operand,
}
}
}
pub struct FormatRegistry {
/// Map (immediate kinds names, number of values, has varargs) to an instruction format.
sig_to_index: HashMap<(Vec<String>, usize, bool), usize>,
formats: Vec<Rc<InstructionFormat>>,
name_set: HashSet<&'static str>,
}
impl FormatRegistry {
pub fn new() -> Self {
Self {
sig_to_index: HashMap::new(),
formats: Vec::new(),
name_set: HashSet::new(),
}
}
/// Find an existing instruction format that matches the given lists of instruction inputs and
/// outputs.
pub fn lookup(&self, operands_in: &Vec<Operand>) -> &Rc<InstructionFormat> {
let mut imm_keys = Vec::new();
let mut num_values = 0;
let mut has_varargs = false;
for operand in operands_in.iter() {
if operand.is_value() {
num_values += 1;
}
if !has_varargs {
has_varargs = operand.is_varargs();
}
if let Some(imm_key) = operand.kind.imm_key() {
imm_keys.push(imm_key);
}
}
let sig = (imm_keys, num_values, has_varargs);
let index = *self
.sig_to_index
.get(&sig)
.expect("unknown InstructionFormat; please define it in shared/formats.rs first");
&self.formats[index]
}
pub fn by_name(&self, name: &str) -> &Rc<InstructionFormat> {
&self
.formats
.iter()
.find(|format| format.name == name)
.unwrap_or_else(|| panic!("format with name '{}' doesn't exist", name))
}
pub fn insert(&mut self, inst_format: InstructionFormatBuilder) {
let name = &inst_format.name;
if !self.name_set.insert(name) {
panic!(
"Trying to add an InstructionFormat named {}, but it already exists!",
name
);
}
let format = inst_format.build();
// Compute key.
let imm_keys = format
.imm_fields
.iter()
.map(|field| field.kind.imm_key().unwrap())
.collect();
let key = (imm_keys, format.num_value_operands, format.has_value_list);
let index = self.formats.len();
self.formats.push(Rc::new(format));
if let Some(already_inserted) = self.sig_to_index.insert(key, index) {
panic!(
"duplicate InstructionFormat: trying to insert '{}' while '{}' already has the same structure.",
self.formats[index].name,
self.formats[already_inserted].name
);
}
}
pub fn iter(&self) -> slice::Iter<Rc<InstructionFormat>> {
self.formats.iter()
})
}
}