[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:
@@ -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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use cranelift_codegen_shared::condcodes::IntCC;
|
||||
use cranelift_entity::{entity_impl, PrimaryMap};
|
||||
|
||||
use std::collections::HashMap;
|
||||
@@ -6,13 +7,14 @@ use std::fmt::{Display, Error, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::cdsl::camel_case;
|
||||
use crate::cdsl::formats::{FormatField, FormatRegistry, InstructionFormat};
|
||||
use crate::cdsl::formats::{FormatField, InstructionFormat};
|
||||
use crate::cdsl::operands::Operand;
|
||||
use crate::cdsl::type_inference::Constraint;
|
||||
use crate::cdsl::types::{LaneType, ReferenceType, ValueType, VectorType};
|
||||
use crate::cdsl::typevar::TypeVar;
|
||||
|
||||
use crate::shared::formats::Formats;
|
||||
use crate::shared::types::{Bool, Float, Int, Reference};
|
||||
use cranelift_codegen_shared::condcodes::IntCC;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub(crate) struct OpcodeNumber(u32);
|
||||
@@ -20,19 +22,14 @@ entity_impl!(OpcodeNumber);
|
||||
|
||||
pub(crate) type AllInstructions = PrimaryMap<OpcodeNumber, Instruction>;
|
||||
|
||||
pub(crate) struct InstructionGroupBuilder<'format_reg, 'all_inst> {
|
||||
format_registry: &'format_reg FormatRegistry,
|
||||
pub(crate) struct InstructionGroupBuilder<'all_inst> {
|
||||
all_instructions: &'all_inst mut AllInstructions,
|
||||
own_instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
impl<'format_reg, 'all_inst> InstructionGroupBuilder<'format_reg, 'all_inst> {
|
||||
pub fn new(
|
||||
all_instructions: &'all_inst mut AllInstructions,
|
||||
format_registry: &'format_reg FormatRegistry,
|
||||
) -> Self {
|
||||
impl<'all_inst> InstructionGroupBuilder<'all_inst> {
|
||||
pub fn new(all_instructions: &'all_inst mut AllInstructions) -> Self {
|
||||
Self {
|
||||
format_registry,
|
||||
all_instructions,
|
||||
own_instructions: Vec::new(),
|
||||
}
|
||||
@@ -40,7 +37,7 @@ impl<'format_reg, 'all_inst> InstructionGroupBuilder<'format_reg, 'all_inst> {
|
||||
|
||||
pub fn push(&mut self, builder: InstructionBuilder) {
|
||||
let opcode_number = OpcodeNumber(self.all_instructions.next_key().as_u32());
|
||||
let inst = builder.build(self.format_registry, opcode_number);
|
||||
let inst = builder.build(opcode_number);
|
||||
// Note this clone is cheap, since Instruction is a Rc<> wrapper for InstructionContent.
|
||||
self.own_instructions.push(inst.clone());
|
||||
self.all_instructions.push(inst);
|
||||
@@ -201,6 +198,7 @@ impl fmt::Display for InstructionContent {
|
||||
pub(crate) struct InstructionBuilder {
|
||||
name: String,
|
||||
doc: String,
|
||||
format: Rc<InstructionFormat>,
|
||||
operands_in: Option<Vec<Operand>>,
|
||||
operands_out: Option<Vec<Operand>>,
|
||||
constraints: Option<Vec<Constraint>>,
|
||||
@@ -219,10 +217,11 @@ pub(crate) struct InstructionBuilder {
|
||||
}
|
||||
|
||||
impl InstructionBuilder {
|
||||
pub fn new<S: Into<String>>(name: S, doc: S) -> Self {
|
||||
pub fn new<S: Into<String>>(name: S, doc: S, format: &Rc<InstructionFormat>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
doc: doc.into(),
|
||||
format: format.clone(),
|
||||
operands_in: None,
|
||||
operands_out: None,
|
||||
constraints: None,
|
||||
@@ -297,7 +296,7 @@ impl InstructionBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
fn build(self, format_registry: &FormatRegistry, opcode_number: OpcodeNumber) -> Instruction {
|
||||
fn build(self, opcode_number: OpcodeNumber) -> Instruction {
|
||||
let operands_in = self.operands_in.unwrap_or_else(Vec::new);
|
||||
let operands_out = self.operands_out.unwrap_or_else(Vec::new);
|
||||
|
||||
@@ -319,9 +318,10 @@ impl InstructionBuilder {
|
||||
.filter_map(|(i, op)| if op.is_value() { Some(i) } else { None })
|
||||
.collect();
|
||||
|
||||
let format = format_registry.lookup(&operands_in).clone();
|
||||
verify_format(&self.name, &operands_in, &self.format);
|
||||
|
||||
let polymorphic_info =
|
||||
verify_polymorphic(&operands_in, &operands_out, &format, &value_opnums);
|
||||
verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
|
||||
|
||||
// Infer from output operands whether an instruciton clobbers CPU flags or not.
|
||||
let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags());
|
||||
@@ -336,7 +336,7 @@ impl InstructionBuilder {
|
||||
operands_in,
|
||||
operands_out,
|
||||
constraints: self.constraints.unwrap_or_else(Vec::new),
|
||||
format,
|
||||
format: self.format,
|
||||
polymorphic_info,
|
||||
value_opnums,
|
||||
value_results,
|
||||
@@ -533,6 +533,57 @@ impl Bindable for BoundInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the input operands actually match the given format.
|
||||
fn verify_format(inst_name: &str, operands_in: &Vec<Operand>, format: &InstructionFormat) {
|
||||
// A format is defined by:
|
||||
// - its number of input value operands,
|
||||
// - its number and names of input immediate operands,
|
||||
// - whether it has a value list or not.
|
||||
let mut num_values = 0;
|
||||
let mut imm_index = 0;
|
||||
|
||||
for operand in operands_in.iter() {
|
||||
if operand.is_varargs() {
|
||||
assert!(
|
||||
format.has_value_list,
|
||||
"instruction {} has varargs, but its format {} doesn't have a value list; you may \
|
||||
need to use a different format.",
|
||||
inst_name, format.name
|
||||
);
|
||||
}
|
||||
if operand.is_value() {
|
||||
num_values += 1;
|
||||
}
|
||||
if let Some(imm_name) = operand.kind.imm_name() {
|
||||
if let Some(format_field) = format.imm_fields.get(imm_index) {
|
||||
assert_eq!(
|
||||
format_field.kind.name, imm_name,
|
||||
"{}th operand of {} should be {} (according to format), not {} (according to \
|
||||
inst definition). You may need to use a different format.",
|
||||
imm_index, inst_name, format_field.kind.name, imm_name
|
||||
);
|
||||
imm_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
num_values, format.num_value_operands,
|
||||
"inst {} doesnt' have as many value input operand as its format {} declares; you may need \
|
||||
to use a different format.",
|
||||
inst_name, format.name
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
imm_index,
|
||||
format.imm_fields.len(),
|
||||
"inst {} doesn't have as many immediate input \
|
||||
operands as its format {} declares; you may need to use a different format.",
|
||||
inst_name,
|
||||
format.name
|
||||
);
|
||||
}
|
||||
|
||||
/// Check if this instruction is polymorphic, and verify its use of type variables.
|
||||
fn verify_polymorphic(
|
||||
operands_in: &Vec<Operand>,
|
||||
@@ -1089,8 +1140,8 @@ impl InstructionPredicate {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_is_colocated_data(format_registry: &FormatRegistry) -> InstructionPredicateNode {
|
||||
let format = format_registry.by_name("UnaryGlobalValue");
|
||||
pub fn new_is_colocated_data(formats: &Formats) -> InstructionPredicateNode {
|
||||
let format = &formats.unary_global_value;
|
||||
InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new(
|
||||
&*format,
|
||||
"global_value",
|
||||
@@ -1271,7 +1322,6 @@ mod test {
|
||||
outputs: Vec<OperandKindFields>,
|
||||
) -> Instruction {
|
||||
// setup a format from the input operands
|
||||
let mut formats = FormatRegistry::new();
|
||||
let mut format = InstructionFormatBuilder::new("fake");
|
||||
for (i, f) in inputs.iter().enumerate() {
|
||||
match f {
|
||||
@@ -1282,13 +1332,13 @@ mod test {
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
formats.insert(format);
|
||||
let format = format.build();
|
||||
|
||||
// create the fake instruction
|
||||
InstructionBuilder::new("fake", "A fake instruction for testing.")
|
||||
InstructionBuilder::new("fake", "A fake instruction for testing.", &format)
|
||||
.operands_in(field_to_operands(inputs).iter().collect())
|
||||
.operands_out(field_to_operands(outputs).iter().collect())
|
||||
.build(&formats, OpcodeNumber(42))
|
||||
.build(OpcodeNumber(42))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -138,11 +138,11 @@ pub struct OperandKind {
|
||||
}
|
||||
|
||||
impl OperandKind {
|
||||
pub fn imm_key(&self) -> Option<String> {
|
||||
pub fn imm_name(&self) -> Option<&str> {
|
||||
match self.fields {
|
||||
OperandKindFields::ImmEnum(_)
|
||||
| OperandKindFields::ImmValue
|
||||
| OperandKindFields::EntityRef => Some(self.name.to_string()),
|
||||
| OperandKindFields::EntityRef => Some(&self.name),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,14 +465,15 @@ impl TransformGroups {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_double_custom_legalization() {
|
||||
use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder};
|
||||
use crate::cdsl::formats::InstructionFormatBuilder;
|
||||
use crate::cdsl::instructions::{AllInstructions, InstructionBuilder, InstructionGroupBuilder};
|
||||
|
||||
let nullary = InstructionFormatBuilder::new("nullary").build();
|
||||
|
||||
let mut dummy_all = AllInstructions::new();
|
||||
let mut format = FormatRegistry::new();
|
||||
format.insert(InstructionFormatBuilder::new("nullary"));
|
||||
let mut inst_group = InstructionGroupBuilder::new(&mut dummy_all, &format);
|
||||
inst_group.push(InstructionBuilder::new("dummy", "doc"));
|
||||
let mut inst_group = InstructionGroupBuilder::new(&mut dummy_all);
|
||||
inst_group.push(InstructionBuilder::new("dummy", "doc", &nullary));
|
||||
|
||||
let inst_group = inst_group.build();
|
||||
let dummy_inst = inst_group.by_name("dummy");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user