[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 crate::cdsl::operands::OperandKind;
use std::collections::{HashMap, HashSet};
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use std::slice;
/// An immediate field in an instruction format. /// 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. /// All instruction formats must be predefined in the meta shared/formats.rs module.
#[derive(Debug)] #[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 /// Instruction format name in CamelCase. This is used as a Rust variant name in both the
/// `InstructionData` and `InstructionFormat` enums. /// `InstructionData` and `InstructionFormat` enums.
pub name: &'static str, pub name: &'static str,
@@ -47,6 +44,14 @@ pub struct InstructionFormat {
pub typevar_operand: Option<usize>, 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 { impl fmt::Display for InstructionFormat {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let imm_args = self 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, name: &'static str,
num_value_operands: usize, num_value_operands: usize,
has_value_list: bool, has_value_list: bool,
@@ -131,7 +149,7 @@ impl InstructionFormatBuilder {
self self
} }
pub fn build(self) -> InstructionFormat { pub fn build(self) -> Rc<InstructionFormat> {
let typevar_operand = if self.typevar_operand.is_some() { let typevar_operand = if self.typevar_operand.is_some() {
self.typevar_operand self.typevar_operand
} else if self.has_value_list || self.num_value_operands > 0 { } else if self.has_value_list || self.num_value_operands > 0 {
@@ -141,98 +159,12 @@ impl InstructionFormatBuilder {
None None
}; };
InstructionFormat { Rc::new(InstructionFormat {
name: self.name, name: self.name,
num_value_operands: self.num_value_operands, num_value_operands: self.num_value_operands,
has_value_list: self.has_value_list, has_value_list: self.has_value_list,
imm_fields: self.imm_fields, imm_fields: self.imm_fields,
typevar_operand, 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()
} }
} }

View File

@@ -1,3 +1,4 @@
use cranelift_codegen_shared::condcodes::IntCC;
use cranelift_entity::{entity_impl, PrimaryMap}; use cranelift_entity::{entity_impl, PrimaryMap};
use std::collections::HashMap; use std::collections::HashMap;
@@ -6,13 +7,14 @@ use std::fmt::{Display, Error, Formatter};
use std::rc::Rc; use std::rc::Rc;
use crate::cdsl::camel_case; 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::operands::Operand;
use crate::cdsl::type_inference::Constraint; use crate::cdsl::type_inference::Constraint;
use crate::cdsl::types::{LaneType, ReferenceType, ValueType, VectorType}; use crate::cdsl::types::{LaneType, ReferenceType, ValueType, VectorType};
use crate::cdsl::typevar::TypeVar; use crate::cdsl::typevar::TypeVar;
use crate::shared::formats::Formats;
use crate::shared::types::{Bool, Float, Int, Reference}; use crate::shared::types::{Bool, Float, Int, Reference};
use cranelift_codegen_shared::condcodes::IntCC;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) struct OpcodeNumber(u32); pub(crate) struct OpcodeNumber(u32);
@@ -20,19 +22,14 @@ entity_impl!(OpcodeNumber);
pub(crate) type AllInstructions = PrimaryMap<OpcodeNumber, Instruction>; pub(crate) type AllInstructions = PrimaryMap<OpcodeNumber, Instruction>;
pub(crate) struct InstructionGroupBuilder<'format_reg, 'all_inst> { pub(crate) struct InstructionGroupBuilder<'all_inst> {
format_registry: &'format_reg FormatRegistry,
all_instructions: &'all_inst mut AllInstructions, all_instructions: &'all_inst mut AllInstructions,
own_instructions: Vec<Instruction>, own_instructions: Vec<Instruction>,
} }
impl<'format_reg, 'all_inst> InstructionGroupBuilder<'format_reg, 'all_inst> { impl<'all_inst> InstructionGroupBuilder<'all_inst> {
pub fn new( pub fn new(all_instructions: &'all_inst mut AllInstructions) -> Self {
all_instructions: &'all_inst mut AllInstructions,
format_registry: &'format_reg FormatRegistry,
) -> Self {
Self { Self {
format_registry,
all_instructions, all_instructions,
own_instructions: Vec::new(), 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) { pub fn push(&mut self, builder: InstructionBuilder) {
let opcode_number = OpcodeNumber(self.all_instructions.next_key().as_u32()); 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. // Note this clone is cheap, since Instruction is a Rc<> wrapper for InstructionContent.
self.own_instructions.push(inst.clone()); self.own_instructions.push(inst.clone());
self.all_instructions.push(inst); self.all_instructions.push(inst);
@@ -201,6 +198,7 @@ impl fmt::Display for InstructionContent {
pub(crate) struct InstructionBuilder { pub(crate) struct InstructionBuilder {
name: String, name: String,
doc: String, doc: String,
format: Rc<InstructionFormat>,
operands_in: Option<Vec<Operand>>, operands_in: Option<Vec<Operand>>,
operands_out: Option<Vec<Operand>>, operands_out: Option<Vec<Operand>>,
constraints: Option<Vec<Constraint>>, constraints: Option<Vec<Constraint>>,
@@ -219,10 +217,11 @@ pub(crate) struct InstructionBuilder {
} }
impl 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 { Self {
name: name.into(), name: name.into(),
doc: doc.into(), doc: doc.into(),
format: format.clone(),
operands_in: None, operands_in: None,
operands_out: None, operands_out: None,
constraints: None, constraints: None,
@@ -297,7 +296,7 @@ impl InstructionBuilder {
self 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_in = self.operands_in.unwrap_or_else(Vec::new);
let operands_out = self.operands_out.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 }) .filter_map(|(i, op)| if op.is_value() { Some(i) } else { None })
.collect(); .collect();
let format = format_registry.lookup(&operands_in).clone(); verify_format(&self.name, &operands_in, &self.format);
let polymorphic_info = 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. // 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()); let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags());
@@ -336,7 +336,7 @@ impl InstructionBuilder {
operands_in, operands_in,
operands_out, operands_out,
constraints: self.constraints.unwrap_or_else(Vec::new), constraints: self.constraints.unwrap_or_else(Vec::new),
format, format: self.format,
polymorphic_info, polymorphic_info,
value_opnums, value_opnums,
value_results, 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. /// Check if this instruction is polymorphic, and verify its use of type variables.
fn verify_polymorphic( fn verify_polymorphic(
operands_in: &Vec<Operand>, operands_in: &Vec<Operand>,
@@ -1089,8 +1140,8 @@ impl InstructionPredicate {
)) ))
} }
pub fn new_is_colocated_data(format_registry: &FormatRegistry) -> InstructionPredicateNode { pub fn new_is_colocated_data(formats: &Formats) -> InstructionPredicateNode {
let format = format_registry.by_name("UnaryGlobalValue"); let format = &formats.unary_global_value;
InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new( InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new(
&*format, &*format,
"global_value", "global_value",
@@ -1271,7 +1322,6 @@ mod test {
outputs: Vec<OperandKindFields>, outputs: Vec<OperandKindFields>,
) -> Instruction { ) -> Instruction {
// setup a format from the input operands // setup a format from the input operands
let mut formats = FormatRegistry::new();
let mut format = InstructionFormatBuilder::new("fake"); let mut format = InstructionFormatBuilder::new("fake");
for (i, f) in inputs.iter().enumerate() { for (i, f) in inputs.iter().enumerate() {
match f { match f {
@@ -1282,13 +1332,13 @@ mod test {
_ => {} _ => {}
}; };
} }
formats.insert(format); let format = format.build();
// create the fake instruction // 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_in(field_to_operands(inputs).iter().collect())
.operands_out(field_to_operands(outputs).iter().collect()) .operands_out(field_to_operands(outputs).iter().collect())
.build(&formats, OpcodeNumber(42)) .build(OpcodeNumber(42))
} }
#[test] #[test]

View File

@@ -138,11 +138,11 @@ pub struct OperandKind {
} }
impl OperandKind { impl OperandKind {
pub fn imm_key(&self) -> Option<String> { pub fn imm_name(&self) -> Option<&str> {
match self.fields { match self.fields {
OperandKindFields::ImmEnum(_) OperandKindFields::ImmEnum(_)
| OperandKindFields::ImmValue | OperandKindFields::ImmValue
| OperandKindFields::EntityRef => Some(self.name.to_string()), | OperandKindFields::EntityRef => Some(&self.name),
_ => None, _ => None,
} }
} }

View File

@@ -465,14 +465,15 @@ impl TransformGroups {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_double_custom_legalization() { fn test_double_custom_legalization() {
use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder}; use crate::cdsl::formats::InstructionFormatBuilder;
use crate::cdsl::instructions::{AllInstructions, InstructionBuilder, InstructionGroupBuilder}; use crate::cdsl::instructions::{AllInstructions, InstructionBuilder, InstructionGroupBuilder};
let nullary = InstructionFormatBuilder::new("nullary").build();
let mut dummy_all = AllInstructions::new(); let mut dummy_all = AllInstructions::new();
let mut format = FormatRegistry::new(); let mut inst_group = InstructionGroupBuilder::new(&mut dummy_all);
format.insert(InstructionFormatBuilder::new("nullary")); inst_group.push(InstructionBuilder::new("dummy", "doc", &nullary));
let mut inst_group = InstructionGroupBuilder::new(&mut dummy_all, &format);
inst_group.push(InstructionBuilder::new("dummy", "doc"));
let inst_group = inst_group.build(); let inst_group = inst_group.build();
let dummy_inst = inst_group.by_name("dummy"); let dummy_inst = inst_group.by_name("dummy");

View File

@@ -4,13 +4,11 @@ use cranelift_codegen_shared::constant_hash;
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use crate::cdsl::camel_case; use crate::cdsl::camel_case;
use crate::cdsl::formats::{FormatRegistry, InstructionFormat}; use crate::cdsl::formats::InstructionFormat;
use crate::cdsl::instructions::{AllInstructions, Instruction}; use crate::cdsl::instructions::{AllInstructions, Instruction};
use crate::cdsl::operands::Operand; use crate::cdsl::operands::Operand;
use crate::cdsl::typevar::{TypeSet, TypeVar}; use crate::cdsl::typevar::{TypeSet, TypeVar};
use crate::shared::Definitions as SharedDefinitions;
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};
@@ -19,7 +17,7 @@ use crate::unique_table::{UniqueSeqTable, UniqueTable};
const TYPESET_LIMIT: usize = 0xff; const TYPESET_LIMIT: usize = 0xff;
/// Generate an instruction format enumeration. /// Generate an instruction format enumeration.
fn gen_formats(registry: &FormatRegistry, fmt: &mut Formatter) { fn gen_formats(formats: &Vec<&InstructionFormat>, fmt: &mut Formatter) {
fmt.doc_comment( fmt.doc_comment(
r#" r#"
An instruction format An instruction format
@@ -32,7 +30,7 @@ fn gen_formats(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug)]"); fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug)]");
fmt.line("pub enum InstructionFormat {"); fmt.line("pub enum InstructionFormat {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
for format in registry.iter() { for format in formats {
fmt.doc_comment(format.to_string()); fmt.doc_comment(format.to_string());
fmtln!(fmt, "{},", format.name); fmtln!(fmt, "{},", format.name);
} }
@@ -47,7 +45,7 @@ fn gen_formats(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.line("fn from(inst: &'a InstructionData) -> Self {"); fmt.line("fn from(inst: &'a InstructionData) -> Self {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
let mut m = Match::new("*inst"); let mut m = Match::new("*inst");
for format in registry.iter() { for format in formats {
m.arm( m.arm(
format!("InstructionData::{}", format.name), format!("InstructionData::{}", format.name),
vec![".."], vec![".."],
@@ -67,12 +65,12 @@ fn gen_formats(registry: &FormatRegistry, fmt: &mut Formatter) {
/// Every variant must contain an `opcode` field. The size of `InstructionData` should be kept at /// Every variant must contain an `opcode` field. The size of `InstructionData` should be kept at
/// 16 bytes on 64-bit architectures. If more space is needed to represent an instruction, use a /// 16 bytes on 64-bit architectures. If more space is needed to represent an instruction, use a
/// `ValueList` to store the additional information out of line. /// `ValueList` to store the additional information out of line.
fn gen_instruction_data(registry: &FormatRegistry, fmt: &mut Formatter) { fn gen_instruction_data(formats: &Vec<&InstructionFormat>, fmt: &mut Formatter) {
fmt.line("#[derive(Clone, Debug)]"); fmt.line("#[derive(Clone, Debug)]");
fmt.line("#[allow(missing_docs)]"); fmt.line("#[allow(missing_docs)]");
fmt.line("pub enum InstructionData {"); fmt.line("pub enum InstructionData {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
for format in registry.iter() { for format in formats {
fmtln!(fmt, "{} {{", format.name); fmtln!(fmt, "{} {{", format.name);
fmt.indent(|fmt| { fmt.indent(|fmt| {
fmt.line("opcode: Opcode,"); fmt.line("opcode: Opcode,");
@@ -95,7 +93,7 @@ fn gen_instruction_data(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.line("}"); fmt.line("}");
} }
fn gen_arguments_method(registry: &FormatRegistry, fmt: &mut Formatter, is_mut: bool) { fn gen_arguments_method(formats: &Vec<&InstructionFormat>, fmt: &mut Formatter, is_mut: bool) {
let (method, mut_, rslice, as_slice) = if is_mut { let (method, mut_, rslice, as_slice) = if is_mut {
( (
"arguments_mut", "arguments_mut",
@@ -117,7 +115,7 @@ fn gen_arguments_method(registry: &FormatRegistry, fmt: &mut Formatter, is_mut:
); );
fmt.indent(|fmt| { fmt.indent(|fmt| {
let mut m = Match::new("*self"); let mut m = Match::new("*self");
for format in registry.iter() { for format in formats {
let name = format!("InstructionData::{}", format.name); let name = format!("InstructionData::{}", format.name);
// Formats with a value list put all of their arguments in the list. We don't split // Formats with a value list put all of their arguments in the list. We don't split
@@ -165,14 +163,14 @@ fn gen_arguments_method(registry: &FormatRegistry, fmt: &mut Formatter, is_mut:
/// - `pub fn put_value_list(&mut self, args: ir::ValueList>` /// - `pub fn put_value_list(&mut self, args: ir::ValueList>`
/// - `pub fn eq(&self, &other: Self, &pool) -> bool` /// - `pub fn eq(&self, &other: Self, &pool) -> bool`
/// - `pub fn hash<H: Hasher>(&self, state: &mut H, &pool)` /// - `pub fn hash<H: Hasher>(&self, state: &mut H, &pool)`
fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) { fn gen_instruction_data_impl(formats: &Vec<&InstructionFormat>, fmt: &mut Formatter) {
fmt.line("impl InstructionData {"); fmt.line("impl InstructionData {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
fmt.doc_comment("Get the opcode of this instruction."); fmt.doc_comment("Get the opcode of this instruction.");
fmt.line("pub fn opcode(&self) -> Opcode {"); fmt.line("pub fn opcode(&self) -> Opcode {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
let mut m = Match::new("*self"); let mut m = Match::new("*self");
for format in registry.iter() { for format in formats {
m.arm(format!("InstructionData::{}", format.name), vec!["opcode", ".."], m.arm(format!("InstructionData::{}", format.name), vec!["opcode", ".."],
"opcode".to_string()); "opcode".to_string());
} }
@@ -185,7 +183,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.line("pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> Option<Value> {"); fmt.line("pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> Option<Value> {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
let mut m = Match::new("*self"); let mut m = Match::new("*self");
for format in registry.iter() { for format in formats {
let name = format!("InstructionData::{}", format.name); let name = format!("InstructionData::{}", format.name);
if format.typevar_operand.is_none() { if format.typevar_operand.is_none() {
m.arm(name, vec![".."], "None".to_string()); m.arm(name, vec![".."], "None".to_string());
@@ -208,12 +206,12 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.empty_line(); fmt.empty_line();
fmt.doc_comment("Get the value arguments to this instruction."); fmt.doc_comment("Get the value arguments to this instruction.");
gen_arguments_method(registry, fmt, false); gen_arguments_method(formats, fmt, false);
fmt.empty_line(); fmt.empty_line();
fmt.doc_comment(r#"Get mutable references to the value arguments to this fmt.doc_comment(r#"Get mutable references to the value arguments to this
instruction."#); instruction."#);
gen_arguments_method(registry, fmt, true); gen_arguments_method(formats, fmt, true);
fmt.empty_line(); fmt.empty_line();
fmt.doc_comment(r#" fmt.doc_comment(r#"
@@ -227,7 +225,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.indent(|fmt| { fmt.indent(|fmt| {
let mut m = Match::new("*self"); let mut m = Match::new("*self");
for format in registry.iter() { for format in formats {
if format.has_value_list { if format.has_value_list {
m.arm(format!("InstructionData::{}", format.name), m.arm(format!("InstructionData::{}", format.name),
vec!["ref mut args", ".."], vec!["ref mut args", ".."],
@@ -254,7 +252,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.indent(|fmt| { fmt.indent(|fmt| {
fmt.line("let args = match *self {"); fmt.line("let args = match *self {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
for format in registry.iter() { for format in formats {
if format.has_value_list { if format.has_value_list {
fmtln!(fmt, "InstructionData::{} {{ ref mut args, .. }} => args,", format.name); fmtln!(fmt, "InstructionData::{} {{ ref mut args, .. }} => args,", format.name);
} }
@@ -284,7 +282,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.line("match (self, other) {"); fmt.line("match (self, other) {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
for format in registry.iter() { for format in formats {
let name = format!("&InstructionData::{}", format.name); let name = format!("&InstructionData::{}", format.name);
let mut members = vec!["opcode"]; let mut members = vec!["opcode"];
@@ -336,7 +334,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
fmt.indent(|fmt| { fmt.indent(|fmt| {
fmt.line("match *self {"); fmt.line("match *self {");
fmt.indent(|fmt| { fmt.indent(|fmt| {
for format in registry.iter() { for format in formats {
let name = format!("InstructionData::{}", format.name); let name = format!("InstructionData::{}", format.name);
let mut members = vec!["opcode"]; let mut members = vec!["opcode"];
@@ -1037,7 +1035,11 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo
} }
/// Generate a Builder trait with methods for all instructions. /// Generate a Builder trait with methods for all instructions.
fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &mut Formatter) { fn gen_builder(
instructions: &AllInstructions,
formats: &Vec<&InstructionFormat>,
fmt: &mut Formatter,
) {
fmt.doc_comment( fmt.doc_comment(
r#" r#"
Convenience methods for building instructions. Convenience methods for building instructions.
@@ -1060,7 +1062,7 @@ fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &m
for inst in instructions.values() { for inst in instructions.values() {
gen_inst_builder(inst, &*inst.format, fmt); gen_inst_builder(inst, &*inst.format, fmt);
} }
for format in formats.iter() { for format in formats {
gen_format_constructor(format, fmt); gen_format_constructor(format, fmt);
} }
}); });
@@ -1068,20 +1070,18 @@ fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &m
} }
pub(crate) fn generate( pub(crate) fn generate(
shared_defs: &SharedDefinitions, formats: Vec<&InstructionFormat>,
all_inst: &AllInstructions,
opcode_filename: &str, opcode_filename: &str,
inst_builder_filename: &str, inst_builder_filename: &str,
out_dir: &str, out_dir: &str,
) -> Result<(), error::Error> { ) -> Result<(), error::Error> {
let format_registry = &shared_defs.format_registry;
let all_inst = &shared_defs.all_instructions;
// Opcodes. // Opcodes.
let mut fmt = Formatter::new(); let mut fmt = Formatter::new();
gen_formats(format_registry, &mut fmt); gen_formats(&formats, &mut fmt);
gen_instruction_data(format_registry, &mut fmt); gen_instruction_data(&formats, &mut fmt);
fmt.empty_line(); fmt.empty_line();
gen_instruction_data_impl(format_registry, &mut fmt); gen_instruction_data_impl(&formats, &mut fmt);
fmt.empty_line(); fmt.empty_line();
gen_opcodes(all_inst, &mut fmt); gen_opcodes(all_inst, &mut fmt);
gen_type_constraints(all_inst, &mut fmt); gen_type_constraints(all_inst, &mut fmt);
@@ -1089,7 +1089,7 @@ pub(crate) fn generate(
// Instruction builder. // Instruction builder.
let mut fmt = Formatter::new(); let mut fmt = Formatter::new();
gen_builder(all_inst, format_registry, &mut fmt); gen_builder(all_inst, &formats, &mut fmt);
fmt.update_file(inst_builder_filename, out_dir)?; fmt.update_file(inst_builder_filename, out_dir)?;
Ok(()) Ok(())

View File

@@ -53,11 +53,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let settings = define_settings(&shared_defs.settings); let settings = define_settings(&shared_defs.settings);
let regs = define_regs(); let regs = define_regs();
let inst_group = InstructionGroupBuilder::new( let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build();
&mut shared_defs.all_instructions,
&shared_defs.format_registry,
)
.build();
// CPU modes for 32-bit ARM and Thumb2. // CPU modes for 32-bit ARM and Thumb2.
let mut a32 = CpuMode::new("A32"); let mut a32 = CpuMode::new("A32");

View File

@@ -49,11 +49,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let settings = define_settings(&shared_defs.settings); let settings = define_settings(&shared_defs.settings);
let regs = define_registers(); let regs = define_registers();
let inst_group = InstructionGroupBuilder::new( let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build();
&mut shared_defs.all_instructions,
&shared_defs.format_registry,
)
.build();
let mut a64 = CpuMode::new("A64"); let mut a64 = CpuMode::new("A64");

View File

@@ -89,11 +89,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let settings = define_settings(&shared_defs.settings); let settings = define_settings(&shared_defs.settings);
let regs = define_registers(); let regs = define_registers();
let inst_group = InstructionGroupBuilder::new( let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build();
&mut shared_defs.all_instructions,
&shared_defs.format_registry,
)
.build();
// CPU modes for 32-bit and 64-bit operation. // CPU modes for 32-bit and 64-bit operation.
let mut rv_32 = CpuMode::new("RV32"); let mut rv_32 = CpuMode::new("RV32");

View File

@@ -46,23 +46,7 @@ impl RecipeGroup {
} }
pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeGroup { pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeGroup {
// Format shorthands. let formats = &shared_defs.formats;
let formats = &shared_defs.format_registry;
let f_binary = formats.by_name("Binary");
let f_binary_imm = formats.by_name("BinaryImm");
let f_branch = formats.by_name("Branch");
let f_branch_icmp = formats.by_name("BranchIcmp");
let f_call = formats.by_name("Call");
let f_call_indirect = formats.by_name("CallIndirect");
let f_copy_to_ssa = formats.by_name("CopyToSsa");
let f_int_compare = formats.by_name("IntCompare");
let f_int_compare_imm = formats.by_name("IntCompareImm");
let f_jump = formats.by_name("Jump");
let f_multiary = formats.by_name("MultiAry");
let f_regmove = formats.by_name("RegMove");
let f_unary = formats.by_name("Unary");
let f_unary_imm = formats.by_name("UnaryImm");
// Register classes shorthands. // Register classes shorthands.
let gpr = regs.class_by_name("GPR"); let gpr = regs.class_by_name("GPR");
@@ -73,7 +57,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// R-type 32-bit instructions: These are mostly binary arithmetic instructions. // R-type 32-bit instructions: These are mostly binary arithmetic instructions.
// The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8) // The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8)
recipes.push( recipes.push(
EncodingRecipeBuilder::new("R", f_binary, 4) EncodingRecipeBuilder::new("R", &formats.binary, 4)
.operands_in(vec![gpr, gpr]) .operands_in(vec![gpr, gpr])
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"), .emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"),
@@ -81,7 +65,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// R-type with an immediate shift amount instead of rs2. // R-type with an immediate shift amount instead of rs2.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("Rshamt", f_binary_imm, 4) EncodingRecipeBuilder::new("Rshamt", &formats.binary_imm, 4)
.operands_in(vec![gpr]) .operands_in(vec![gpr])
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.emit("put_rshamt(bits, in_reg0, imm.into(), out_reg0, sink);"), .emit("put_rshamt(bits, in_reg0, imm.into(), out_reg0, sink);"),
@@ -89,18 +73,18 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// R-type encoding of an integer comparison. // R-type encoding of an integer comparison.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("Ricmp", f_int_compare, 4) EncodingRecipeBuilder::new("Ricmp", &formats.int_compare, 4)
.operands_in(vec![gpr, gpr]) .operands_in(vec![gpr, gpr])
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"), .emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"),
); );
recipes.push( recipes.push(
EncodingRecipeBuilder::new("Ii", f_binary_imm, 4) EncodingRecipeBuilder::new("Ii", &formats.binary_imm, 4)
.operands_in(vec![gpr]) .operands_in(vec![gpr])
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.inst_predicate(InstructionPredicate::new_is_signed_int( .inst_predicate(InstructionPredicate::new_is_signed_int(
&*f_binary_imm, &*formats.binary_imm,
"imm", "imm",
12, 12,
0, 0,
@@ -110,10 +94,10 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// I-type instruction with a hardcoded %x0 rs1. // I-type instruction with a hardcoded %x0 rs1.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("Iz", f_unary_imm, 4) EncodingRecipeBuilder::new("Iz", &formats.unary_imm, 4)
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.inst_predicate(InstructionPredicate::new_is_signed_int( .inst_predicate(InstructionPredicate::new_is_signed_int(
&*f_unary_imm, &*&formats.unary_imm,
"imm", "imm",
12, 12,
0, 0,
@@ -123,11 +107,11 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// I-type encoding of an integer comparison. // I-type encoding of an integer comparison.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("Iicmp", f_int_compare_imm, 4) EncodingRecipeBuilder::new("Iicmp", &formats.int_compare_imm, 4)
.operands_in(vec![gpr]) .operands_in(vec![gpr])
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.inst_predicate(InstructionPredicate::new_is_signed_int( .inst_predicate(InstructionPredicate::new_is_signed_int(
&*f_int_compare_imm, &*&formats.int_compare_imm,
"imm", "imm",
12, 12,
0, 0,
@@ -137,7 +121,8 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// I-type encoding for `jalr` as a return instruction. We won't use the immediate offset. The // I-type encoding for `jalr` as a return instruction. We won't use the immediate offset. The
// variable return values are not encoded. // variable return values are not encoded.
recipes.push(EncodingRecipeBuilder::new("Iret", f_multiary, 4).emit( recipes.push(
EncodingRecipeBuilder::new("Iret", &formats.multiary, 4).emit(
r#" r#"
// Return instructions are always a jalr to %x1. // Return instructions are always a jalr to %x1.
// The return address is provided as a special-purpose link argument. // The return address is provided as a special-purpose link argument.
@@ -149,11 +134,12 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
sink, sink,
); );
"#, "#,
)); ),
);
// I-type encoding for `jalr` as a call_indirect. // I-type encoding for `jalr` as a call_indirect.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("Icall", f_call_indirect, 4) EncodingRecipeBuilder::new("Icall", &formats.call_indirect, 4)
.operands_in(vec![gpr]) .operands_in(vec![gpr])
.emit( .emit(
r#" r#"
@@ -171,7 +157,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// Copy of a GPR is implemented as addi x, 0. // Copy of a GPR is implemented as addi x, 0.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("Icopy", f_unary, 4) EncodingRecipeBuilder::new("Icopy", &formats.unary, 4)
.operands_in(vec![gpr]) .operands_in(vec![gpr])
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.emit("put_i(bits, in_reg0, 0, out_reg0, sink);"), .emit("put_i(bits, in_reg0, 0, out_reg0, sink);"),
@@ -179,14 +165,14 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// Same for a GPR regmove. // Same for a GPR regmove.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("Irmov", f_regmove, 4) EncodingRecipeBuilder::new("Irmov", &formats.reg_move, 4)
.operands_in(vec![gpr]) .operands_in(vec![gpr])
.emit("put_i(bits, src, 0, dst, sink);"), .emit("put_i(bits, src, 0, dst, sink);"),
); );
// Same for copy-to-SSA -- GPR regmove. // Same for copy-to-SSA -- GPR regmove.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("copytossa", f_copy_to_ssa, 4) EncodingRecipeBuilder::new("copytossa", &formats.copy_to_ssa, 4)
// No operands_in to mention, because a source register is specified directly. // No operands_in to mention, because a source register is specified directly.
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.emit("put_i(bits, src, 0, out_reg0, sink);"), .emit("put_i(bits, src, 0, out_reg0, sink);"),
@@ -194,10 +180,10 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// U-type instructions have a 20-bit immediate that targets bits 12-31. // U-type instructions have a 20-bit immediate that targets bits 12-31.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("U", f_unary_imm, 4) EncodingRecipeBuilder::new("U", &formats.unary_imm, 4)
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.inst_predicate(InstructionPredicate::new_is_signed_int( .inst_predicate(InstructionPredicate::new_is_signed_int(
&*f_unary_imm, &*&formats.unary_imm,
"imm", "imm",
32, 32,
12, 12,
@@ -207,7 +193,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// UJ-type unconditional branch instructions. // UJ-type unconditional branch instructions.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("UJ", f_jump, 4) EncodingRecipeBuilder::new("UJ", &formats.jump, 4)
.branch_range((0, 21)) .branch_range((0, 21))
.emit( .emit(
r#" r#"
@@ -218,7 +204,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
), ),
); );
recipes.push(EncodingRecipeBuilder::new("UJcall", f_call, 4).emit( recipes.push(EncodingRecipeBuilder::new("UJcall", &formats.call, 4).emit(
r#" r#"
sink.reloc_external(Reloc::RiscvCall, sink.reloc_external(Reloc::RiscvCall,
&func.dfg.ext_funcs[func_ref].name, &func.dfg.ext_funcs[func_ref].name,
@@ -230,7 +216,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// SB-type branch instructions. // SB-type branch instructions.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("SB", f_branch_icmp, 4) EncodingRecipeBuilder::new("SB", &formats.branch_icmp, 4)
.operands_in(vec![gpr, gpr]) .operands_in(vec![gpr, gpr])
.branch_range((0, 13)) .branch_range((0, 13))
.emit( .emit(
@@ -244,7 +230,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// SB-type branch instruction with rs2 fixed to zero. // SB-type branch instruction with rs2 fixed to zero.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("SBzero", f_branch, 4) EncodingRecipeBuilder::new("SBzero", &formats.branch, 4)
.operands_in(vec![gpr]) .operands_in(vec![gpr])
.branch_range((0, 13)) .branch_range((0, 13))
.emit( .emit(
@@ -258,7 +244,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// Spill of a GPR. // Spill of a GPR.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("GPsp", f_unary, 4) EncodingRecipeBuilder::new("GPsp", &formats.unary, 4)
.operands_in(vec![gpr]) .operands_in(vec![gpr])
.operands_out(vec![Stack::new(gpr)]) .operands_out(vec![Stack::new(gpr)])
.emit("unimplemented!();"), .emit("unimplemented!();"),
@@ -266,7 +252,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// Fill of a GPR. // Fill of a GPR.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("GPfi", f_unary, 4) EncodingRecipeBuilder::new("GPfi", &formats.unary, 4)
.operands_in(vec![Stack::new(gpr)]) .operands_in(vec![Stack::new(gpr)])
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.emit("unimplemented!();"), .emit("unimplemented!();"),
@@ -274,7 +260,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// Stack-slot to same stack-slot copy, which is guaranteed to turn into a no-op. // Stack-slot to same stack-slot copy, which is guaranteed to turn into a no-op.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("stacknull", f_unary, 0) EncodingRecipeBuilder::new("stacknull", &formats.unary, 0)
.operands_in(vec![Stack::new(gpr)]) .operands_in(vec![Stack::new(gpr)])
.operands_out(vec![Stack::new(gpr)]) .operands_out(vec![Stack::new(gpr)])
.emit(""), .emit(""),
@@ -282,7 +268,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
// No-op fills, created by late-stage redundant-fill removal. // No-op fills, created by late-stage redundant-fill removal.
recipes.push( recipes.push(
EncodingRecipeBuilder::new("fillnull", f_unary, 0) EncodingRecipeBuilder::new("fillnull", &formats.unary, 0)
.operands_in(vec![Stack::new(gpr)]) .operands_in(vec![Stack::new(gpr)])
.operands_out(vec![gpr]) .operands_out(vec![gpr])
.clobbers_flags(false) .clobbers_flags(false)

View File

@@ -374,7 +374,7 @@ pub(crate) fn define(
r: &RecipeGroup, r: &RecipeGroup,
) -> PerCpuModeEncodings { ) -> PerCpuModeEncodings {
let shared = &shared_defs.instructions; let shared = &shared_defs.instructions;
let formats = &shared_defs.format_registry; let formats = &shared_defs.formats;
// Shorthands for instructions. // Shorthands for instructions.
let adjust_sp_down = shared.by_name("adjust_sp_down"); let adjust_sp_down = shared.by_name("adjust_sp_down");
@@ -774,8 +774,8 @@ pub(crate) fn define(
e.enc64(iconst.bind(I32), rec_pu_id.opcodes(&MOV_IMM)); e.enc64(iconst.bind(I32), rec_pu_id.opcodes(&MOV_IMM));
// The 32-bit immediate movl also zero-extends to 64 bits. // The 32-bit immediate movl also zero-extends to 64 bits.
let f_unary_imm = formats.by_name("UnaryImm"); let is_unsigned_int32 =
let is_unsigned_int32 = InstructionPredicate::new_is_unsigned_int(&*f_unary_imm, "imm", 32, 0); InstructionPredicate::new_is_unsigned_int(&*formats.unary_imm, "imm", 32, 0);
e.enc64_func( e.enc64_func(
iconst.bind(I64), iconst.bind(I64),
@@ -801,7 +801,7 @@ pub(crate) fn define(
} }
e.enc64(bconst.bind(B64), rec_pu_id_bool.opcodes(&MOV_IMM).rex()); e.enc64(bconst.bind(B64), rec_pu_id_bool.opcodes(&MOV_IMM).rex());
let is_zero_int = InstructionPredicate::new_is_zero_int(f_unary_imm, "imm"); let is_zero_int = InstructionPredicate::new_is_zero_int(&formats.unary_imm, "imm");
e.enc_both_instp( e.enc_both_instp(
iconst.bind(I8), iconst.bind(I8),
rec_u_id_z.opcodes(&XORB), rec_u_id_z.opcodes(&XORB),
@@ -880,8 +880,8 @@ pub(crate) fn define(
e.enc64_isap(ctz.bind(I32), rec_urm.opcodes(&TZCNT), use_bmi1); e.enc64_isap(ctz.bind(I32), rec_urm.opcodes(&TZCNT), use_bmi1);
// Loads and stores. // Loads and stores.
let f_load_complex = formats.by_name("LoadComplex"); let is_load_complex_length_two =
let is_load_complex_length_two = InstructionPredicate::new_length_equals(&*f_load_complex, 2); InstructionPredicate::new_length_equals(&*formats.load_complex, 2);
for recipe in &[rec_ldWithIndex, rec_ldWithIndexDisp8, rec_ldWithIndexDisp32] { for recipe in &[rec_ldWithIndex, rec_ldWithIndexDisp8, rec_ldWithIndexDisp32] {
e.enc_i32_i64_instp( e.enc_i32_i64_instp(
@@ -925,9 +925,8 @@ pub(crate) fn define(
); );
} }
let f_store_complex = formats.by_name("StoreComplex");
let is_store_complex_length_three = let is_store_complex_length_three =
InstructionPredicate::new_length_equals(&*f_store_complex, 3); InstructionPredicate::new_length_equals(&*formats.store_complex, 3);
for recipe in &[rec_stWithIndex, rec_stWithIndexDisp8, rec_stWithIndexDisp32] { for recipe in &[rec_stWithIndex, rec_stWithIndexDisp8, rec_stWithIndexDisp32] {
e.enc_i32_i64_instp( e.enc_i32_i64_instp(
@@ -1233,8 +1232,8 @@ pub(crate) fn define(
); );
// 64-bit, colocated, both PIC and non-PIC. Use the lea instruction's pc-relative field. // 64-bit, colocated, both PIC and non-PIC. Use the lea instruction's pc-relative field.
let f_func_addr = formats.by_name("FuncAddr"); let is_colocated_func =
let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*f_func_addr, "func_ref"); InstructionPredicate::new_is_colocated_func(&*formats.func_addr, "func_ref");
e.enc64_instp( e.enc64_instp(
func_addr.bind(I64), func_addr.bind(I64),
rec_pcrel_fnaddr8.opcodes(&LEA).rex().w(), rec_pcrel_fnaddr8.opcodes(&LEA).rex().w(),
@@ -1293,8 +1292,7 @@ pub(crate) fn define(
e.enc32(call, rec_call_id.opcodes(&CALL_RELATIVE)); e.enc32(call, rec_call_id.opcodes(&CALL_RELATIVE));
// 64-bit, colocated, both PIC and non-PIC. Use the call instruction's pc-relative field. // 64-bit, colocated, both PIC and non-PIC. Use the call instruction's pc-relative field.
let f_call = formats.by_name("Call"); let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*formats.call, "func_ref");
let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*f_call, "func_ref");
e.enc64_instp(call, rec_call_id.opcodes(&CALL_RELATIVE), is_colocated_func); e.enc64_instp(call, rec_call_id.opcodes(&CALL_RELATIVE), is_colocated_func);
// 64-bit, non-colocated, PIC. There is no 64-bit non-colocated non-PIC version, since non-PIC // 64-bit, non-colocated, PIC. There is no 64-bit non-colocated non-PIC version, since non-PIC
@@ -1564,18 +1562,16 @@ pub(crate) fn define(
// Floating-point constants equal to 0.0 can be encoded using either `xorps` or `xorpd`, for // Floating-point constants equal to 0.0 can be encoded using either `xorps` or `xorpd`, for
// 32-bit and 64-bit floats respectively. // 32-bit and 64-bit floats respectively.
let f_unary_ieee32 = formats.by_name("UnaryIeee32");
let is_zero_32_bit_float = let is_zero_32_bit_float =
InstructionPredicate::new_is_zero_32bit_float(&*f_unary_ieee32, "imm"); InstructionPredicate::new_is_zero_32bit_float(&*formats.unary_ieee32, "imm");
e.enc32_instp( e.enc32_instp(
f32const, f32const,
rec_f32imm_z.opcodes(&XORPS), rec_f32imm_z.opcodes(&XORPS),
is_zero_32_bit_float.clone(), is_zero_32_bit_float.clone(),
); );
let f_unary_ieee64 = formats.by_name("UnaryIeee64");
let is_zero_64_bit_float = let is_zero_64_bit_float =
InstructionPredicate::new_is_zero_64bit_float(&*f_unary_ieee64, "imm"); InstructionPredicate::new_is_zero_64bit_float(&*formats.unary_ieee64, "imm");
e.enc32_instp( e.enc32_instp(
f64const, f64const,
rec_f64imm_z.opcodes(&XORPD), rec_f64imm_z.opcodes(&XORPD),
@@ -1847,18 +1843,17 @@ pub(crate) fn define(
// this must be encoded prior to the MOVUPS implementation (below) so the compiler sees this // this must be encoded prior to the MOVUPS implementation (below) so the compiler sees this
// encoding first // encoding first
for ty in ValueType::all_lane_types().filter(allowed_simd_type) { for ty in ValueType::all_lane_types().filter(allowed_simd_type) {
let f_unary_const = formats.by_name("UnaryConst");
let instruction = vconst.bind(vector(ty, sse_vector_size)); let instruction = vconst.bind(vector(ty, sse_vector_size));
let is_zero_128bit = let is_zero_128bit =
InstructionPredicate::new_is_all_zeroes(&*f_unary_const, "constant_handle"); InstructionPredicate::new_is_all_zeroes(&*formats.unary_const, "constant_handle");
let template = rec_vconst_optimized.nonrex().opcodes(&PXOR); let template = rec_vconst_optimized.nonrex().opcodes(&PXOR);
e.enc_32_64_func(instruction.clone(), template, |builder| { e.enc_32_64_func(instruction.clone(), template, |builder| {
builder.inst_predicate(is_zero_128bit) builder.inst_predicate(is_zero_128bit)
}); });
let is_ones_128bit = let is_ones_128bit =
InstructionPredicate::new_is_all_ones(&*f_unary_const, "constant_handle"); InstructionPredicate::new_is_all_ones(&*formats.unary_const, "constant_handle");
let template = rec_vconst_optimized.nonrex().opcodes(&PCMPEQB); let template = rec_vconst_optimized.nonrex().opcodes(&PCMPEQB);
e.enc_32_64_func(instruction, template, |builder| { e.enc_32_64_func(instruction, template, |builder| {
builder.inst_predicate(is_ones_128bit) builder.inst_predicate(is_ones_128bit)
@@ -2038,9 +2033,11 @@ pub(crate) fn define(
}; };
let instruction = icmp.bind(vector(ty, sse_vector_size)); let instruction = icmp.bind(vector(ty, sse_vector_size));
let f_int_compare = formats.by_name("IntCompare"); let has_eq_condition_code = InstructionPredicate::new_has_condition_code(
let has_eq_condition_code = &*formats.int_compare,
InstructionPredicate::new_has_condition_code(&*f_int_compare, IntCC::Equal, "cond"); IntCC::Equal,
"cond",
);
let template = rec_icscc_fpr.nonrex().opcodes(opcodes); let template = rec_icscc_fpr.nonrex().opcodes(opcodes);
e.enc_32_64_func(instruction, template, |builder| { e.enc_32_64_func(instruction, template, |builder| {
let builder = builder.inst_predicate(has_eq_condition_code); let builder = builder.inst_predicate(has_eq_condition_code);

View File

@@ -1,21 +1,22 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::instructions::{ use crate::cdsl::instructions::{
AllInstructions, InstructionBuilder as Inst, InstructionGroup, InstructionGroupBuilder, AllInstructions, InstructionBuilder as Inst, InstructionGroup, InstructionGroupBuilder,
}; };
use crate::cdsl::operands::{create_operand as operand, create_operand_doc as operand_doc}; use crate::cdsl::operands::{create_operand as operand, create_operand_doc as operand_doc};
use crate::cdsl::types::ValueType; use crate::cdsl::types::ValueType;
use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar}; use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar};
use crate::shared::formats::Formats;
use crate::shared::immediates::Immediates; use crate::shared::immediates::Immediates;
use crate::shared::types; use crate::shared::types;
pub(crate) fn define( pub(crate) fn define(
mut all_instructions: &mut AllInstructions, mut all_instructions: &mut AllInstructions,
format_registry: &FormatRegistry, formats: &Formats,
immediates: &Immediates, immediates: &Immediates,
) -> InstructionGroup { ) -> InstructionGroup {
let mut ig = InstructionGroupBuilder::new(&mut all_instructions, format_registry); let mut ig = InstructionGroupBuilder::new(&mut all_instructions);
let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into(); let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into();
@@ -43,6 +44,7 @@ pub(crate) fn define(
Return both quotient and remainder. Return both quotient and remainder.
"#, "#,
&formats.ternary,
) )
.operands_in(vec![nlo, nhi, d]) .operands_in(vec![nlo, nhi, d])
.operands_out(vec![q, r]) .operands_out(vec![q, r])
@@ -62,6 +64,7 @@ pub(crate) fn define(
Return both quotient and remainder. Return both quotient and remainder.
"#, "#,
&formats.ternary,
) )
.operands_in(vec![nlo, nhi, d]) .operands_in(vec![nlo, nhi, d])
.operands_out(vec![q, r]) .operands_out(vec![q, r])
@@ -82,6 +85,7 @@ pub(crate) fn define(
Polymorphic over all scalar integer types, but does not support vector Polymorphic over all scalar integer types, but does not support vector
types. types.
"#, "#,
&formats.binary,
) )
.operands_in(vec![argL, argR]) .operands_in(vec![argL, argR])
.operands_out(vec![resLo, resHi]), .operands_out(vec![resLo, resHi]),
@@ -96,6 +100,7 @@ pub(crate) fn define(
Polymorphic over all scalar integer types, but does not support vector Polymorphic over all scalar integer types, but does not support vector
types. types.
"#, "#,
&formats.binary,
) )
.operands_in(vec![argL, argR]) .operands_in(vec![argL, argR])
.operands_out(vec![resLo, resHi]), .operands_out(vec![resLo, resHi]),
@@ -132,6 +137,7 @@ pub(crate) fn define(
This instruction does not trap. This instruction does not trap.
"#, "#,
&formats.unary,
) )
.operands_in(vec![x]) .operands_in(vec![x])
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -154,6 +160,7 @@ pub(crate) fn define(
When the two operands don't compare as LT, `y` is returned unchanged, When the two operands don't compare as LT, `y` is returned unchanged,
even if it is a signalling NaN. even if it is a signalling NaN.
"#, "#,
&formats.binary,
) )
.operands_in(vec![x, y]) .operands_in(vec![x, y])
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -172,6 +179,7 @@ pub(crate) fn define(
When the two operands don't compare as GT, `y` is returned unchanged, When the two operands don't compare as GT, `y` is returned unchanged,
even if it is a signalling NaN. even if it is a signalling NaN.
"#, "#,
&formats.binary,
) )
.operands_in(vec![x, y]) .operands_in(vec![x, y])
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -190,6 +198,7 @@ pub(crate) fn define(
This is polymorphic in i32 and i64. However, it is only implemented for i64 This is polymorphic in i32 and i64. However, it is only implemented for i64
in 64-bit mode, and only for i32 in 32-bit mode. in 64-bit mode, and only for i32 in 32-bit mode.
"#, "#,
&formats.unary,
) )
.operands_in(vec![x]) .operands_in(vec![x])
.other_side_effects(true) .other_side_effects(true)
@@ -208,6 +217,7 @@ pub(crate) fn define(
This is polymorphic in i32 and i64. However, it is only implemented for i64 This is polymorphic in i32 and i64. However, it is only implemented for i64
in 64-bit mode, and only for i32 in 32-bit mode. in 64-bit mode, and only for i32 in 32-bit mode.
"#, "#,
&formats.nullary,
) )
.operands_out(vec![x]) .operands_out(vec![x])
.other_side_effects(true) .other_side_effects(true)
@@ -229,6 +239,7 @@ pub(crate) fn define(
This is polymorphic in i32 and i64. It is implemented for both i64 and This is polymorphic in i32 and i64. It is implemented for both i64 and
i32 in 64-bit mode, and only for i32 in 32-bit mode. i32 in 64-bit mode, and only for i32 in 32-bit mode.
"#, "#,
&formats.unary,
) )
.operands_in(vec![x]) .operands_in(vec![x])
.operands_out(vec![y, rflags]), .operands_out(vec![y, rflags]),
@@ -241,6 +252,7 @@ pub(crate) fn define(
Bit Scan Forwards -- returns the bit-index of the least significant 1 Bit Scan Forwards -- returns the bit-index of the least significant 1
in the word. Is otherwise identical to 'bsr', just above. in the word. Is otherwise identical to 'bsr', just above.
"#, "#,
&formats.unary,
) )
.operands_in(vec![x]) .operands_in(vec![x])
.operands_out(vec![y, rflags]), .operands_out(vec![y, rflags]),
@@ -269,6 +281,7 @@ pub(crate) fn define(
Packed Shuffle Doublewords -- copies data from either memory or lanes in an extended Packed Shuffle Doublewords -- copies data from either memory or lanes in an extended
register and re-orders the data according to the passed immediate byte. register and re-orders the data according to the passed immediate byte.
"#, "#,
&formats.extract_lane,
) )
.operands_in(vec![a, i]) // TODO allow copying from memory here (need more permissive type than TxN) .operands_in(vec![a, i]) // TODO allow copying from memory here (need more permissive type than TxN)
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -281,6 +294,7 @@ pub(crate) fn define(
Packed Shuffle Bytes -- re-orders data in an extended register using a shuffle Packed Shuffle Bytes -- re-orders data in an extended register using a shuffle
mask from either memory or another extended register mask from either memory or another extended register
"#, "#,
&formats.binary,
) )
.operands_in(vec![a, b]) // TODO allow re-ordering from memory here (need more permissive type than TxN) .operands_in(vec![a, b]) // TODO allow re-ordering from memory here (need more permissive type than TxN)
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -298,6 +312,7 @@ pub(crate) fn define(
The lane index, ``Idx``, is an immediate value, not an SSA value. It The lane index, ``Idx``, is an immediate value, not an SSA value. It
must indicate a valid lane index for the type of ``x``. must indicate a valid lane index for the type of ``x``.
"#, "#,
&formats.extract_lane,
) )
.operands_in(vec![x, Idx]) .operands_in(vec![x, Idx])
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -325,6 +340,7 @@ pub(crate) fn define(
The lane index, ``Idx``, is an immediate value, not an SSA value. It The lane index, ``Idx``, is an immediate value, not an SSA value. It
must indicate a valid lane index for the type of ``x``. must indicate a valid lane index for the type of ``x``.
"#, "#,
&formats.insert_lane,
) )
.operands_in(vec![x, Idx, y]) .operands_in(vec![x, Idx, y])
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -351,6 +367,7 @@ pub(crate) fn define(
extracted from and which it is inserted to. This is similar to x86_pinsr but inserts extracted from and which it is inserted to. This is similar to x86_pinsr but inserts
floats, which are already stored in an XMM register. floats, which are already stored in an XMM register.
"#, "#,
&formats.insert_lane,
) )
.operands_in(vec![x, Idx, y]) .operands_in(vec![x, Idx, y])
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -366,6 +383,7 @@ pub(crate) fn define(
r#" r#"
Move the low 64 bits of the float vector ``y`` to the low 64 bits of float vector ``x`` Move the low 64 bits of the float vector ``y`` to the low 64 bits of float vector ``x``
"#, "#,
&formats.binary,
) )
.operands_in(vec![x, y]) .operands_in(vec![x, y])
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -377,6 +395,7 @@ pub(crate) fn define(
r#" r#"
Move the low 64 bits of the float vector ``y`` to the high 64 bits of float vector ``x`` Move the low 64 bits of the float vector ``y`` to the high 64 bits of float vector ``x``
"#, "#,
&formats.binary,
) )
.operands_in(vec![x, y]) .operands_in(vec![x, y])
.operands_out(vec![a]), .operands_out(vec![a]),
@@ -401,9 +420,11 @@ pub(crate) fn define(
.includes_scalars(false) .includes_scalars(false)
.build(), .build(),
); );
let x = &operand_doc("x", IxN, "Vector value to shift"); let x = &operand_doc("x", IxN, "Vector value to shift");
let y = &operand_doc("y", I64x2, "Number of bits to shift"); let y = &operand_doc("y", I64x2, "Number of bits to shift");
let a = &operand("a", IxN); let a = &operand("a", IxN);
ig.push( ig.push(
Inst::new( Inst::new(
"x86_psll", "x86_psll",
@@ -412,10 +433,12 @@ pub(crate) fn define(
``ishl`` but alters the shift operand to live in an XMM register as expected by the PSLL* ``ishl`` but alters the shift operand to live in an XMM register as expected by the PSLL*
family of instructions. family of instructions.
"#, "#,
&formats.binary,
) )
.operands_in(vec![x, y]) .operands_in(vec![x, y])
.operands_out(vec![a]), .operands_out(vec![a]),
); );
ig.push( ig.push(
Inst::new( Inst::new(
"x86_psrl", "x86_psrl",
@@ -424,10 +447,12 @@ pub(crate) fn define(
``ushr`` but alters the shift operand to live in an XMM register as expected by the PSRL* ``ushr`` but alters the shift operand to live in an XMM register as expected by the PSRL*
family of instructions. family of instructions.
"#, "#,
&formats.binary,
) )
.operands_in(vec![x, y]) .operands_in(vec![x, y])
.operands_out(vec![a]), .operands_out(vec![a]),
); );
ig.push( ig.push(
Inst::new( Inst::new(
"x86_psra", "x86_psra",
@@ -436,6 +461,7 @@ pub(crate) fn define(
instruction ``sshr`` but alters the shift operand to live in an XMM register as expected by instruction ``sshr`` but alters the shift operand to live in an XMM register as expected by
the PSRA* family of instructions. the PSRA* family of instructions.
"#, "#,
&formats.binary,
) )
.operands_in(vec![x, y]) .operands_in(vec![x, y])
.operands_out(vec![a]), .operands_out(vec![a]),

View File

@@ -20,7 +20,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let inst_group = instructions::define( let inst_group = instructions::define(
&mut shared_defs.all_instructions, &mut shared_defs.all_instructions,
&shared_defs.format_registry, &shared_defs.formats,
&shared_defs.imm, &shared_defs.imm,
); );
legalize::define(shared_defs, &inst_group); legalize::define(shared_defs, &inst_group);

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,8 @@ pub fn isa_from_arch(arch: &str) -> Result<isa::Isa, String> {
/// Generates all the Rust source files used in Cranelift from the meta-language. /// Generates all the Rust source files used in Cranelift from the meta-language.
pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error> { pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error> {
// Common definitions. // Create all the definitions:
// - common definitions.
let mut shared_defs = shared::define(); let mut shared_defs = shared::define();
gen_settings::generate( gen_settings::generate(
@@ -34,10 +35,20 @@ pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error>
)?; )?;
gen_types::generate("types.rs", &out_dir)?; gen_types::generate("types.rs", &out_dir)?;
// Per ISA definitions. // - per ISA definitions.
let isas = isa::define(isas, &mut shared_defs); let isas = isa::define(isas, &mut shared_defs);
gen_inst::generate(&shared_defs, "opcodes.rs", "inst_builder.rs", &out_dir)?; // At this point, all definitions are done.
let all_formats = shared_defs.verify_instruction_formats();
// Generate all the code.
gen_inst::generate(
all_formats,
&shared_defs.all_instructions,
"opcodes.rs",
"inst_builder.rs",
&out_dir,
)?;
gen_legalizer::generate(&isas, &shared_defs.transform_groups, "legalize", &out_dir)?; gen_legalizer::generate(&isas, &shared_defs.transform_groups, "legalize", &out_dir)?;

View File

@@ -1,226 +1,303 @@
use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder as Builder}; use crate::cdsl::formats::{InstructionFormat, InstructionFormatBuilder as Builder};
use crate::shared::{entities::EntityRefs, immediates::Immediates}; use crate::shared::{entities::EntityRefs, immediates::Immediates};
use std::rc::Rc;
pub(crate) fn define(imm: &Immediates, entities: &EntityRefs) -> FormatRegistry { pub(crate) struct Formats {
let mut registry = FormatRegistry::new(); pub(crate) binary: Rc<InstructionFormat>,
pub(crate) binary_imm: Rc<InstructionFormat>,
pub(crate) branch: Rc<InstructionFormat>,
pub(crate) branch_float: Rc<InstructionFormat>,
pub(crate) branch_icmp: Rc<InstructionFormat>,
pub(crate) branch_int: Rc<InstructionFormat>,
pub(crate) branch_table: Rc<InstructionFormat>,
pub(crate) branch_table_base: Rc<InstructionFormat>,
pub(crate) branch_table_entry: Rc<InstructionFormat>,
pub(crate) call: Rc<InstructionFormat>,
pub(crate) call_indirect: Rc<InstructionFormat>,
pub(crate) cond_trap: Rc<InstructionFormat>,
pub(crate) copy_special: Rc<InstructionFormat>,
pub(crate) copy_to_ssa: Rc<InstructionFormat>,
pub(crate) extract_lane: Rc<InstructionFormat>,
pub(crate) float_compare: Rc<InstructionFormat>,
pub(crate) float_cond: Rc<InstructionFormat>,
pub(crate) float_cond_trap: Rc<InstructionFormat>,
pub(crate) func_addr: Rc<InstructionFormat>,
pub(crate) heap_addr: Rc<InstructionFormat>,
pub(crate) indirect_jump: Rc<InstructionFormat>,
pub(crate) insert_lane: Rc<InstructionFormat>,
pub(crate) int_compare: Rc<InstructionFormat>,
pub(crate) int_compare_imm: Rc<InstructionFormat>,
pub(crate) int_cond: Rc<InstructionFormat>,
pub(crate) int_cond_trap: Rc<InstructionFormat>,
pub(crate) int_select: Rc<InstructionFormat>,
pub(crate) jump: Rc<InstructionFormat>,
pub(crate) load: Rc<InstructionFormat>,
pub(crate) load_complex: Rc<InstructionFormat>,
pub(crate) multiary: Rc<InstructionFormat>,
pub(crate) nullary: Rc<InstructionFormat>,
pub(crate) reg_fill: Rc<InstructionFormat>,
pub(crate) reg_move: Rc<InstructionFormat>,
pub(crate) reg_spill: Rc<InstructionFormat>,
pub(crate) shuffle: Rc<InstructionFormat>,
pub(crate) stack_load: Rc<InstructionFormat>,
pub(crate) stack_store: Rc<InstructionFormat>,
pub(crate) store: Rc<InstructionFormat>,
pub(crate) store_complex: Rc<InstructionFormat>,
pub(crate) table_addr: Rc<InstructionFormat>,
pub(crate) ternary: Rc<InstructionFormat>,
pub(crate) trap: Rc<InstructionFormat>,
pub(crate) unary: Rc<InstructionFormat>,
pub(crate) unary_bool: Rc<InstructionFormat>,
pub(crate) unary_const: Rc<InstructionFormat>,
pub(crate) unary_global_value: Rc<InstructionFormat>,
pub(crate) unary_ieee32: Rc<InstructionFormat>,
pub(crate) unary_ieee64: Rc<InstructionFormat>,
pub(crate) unary_imm: Rc<InstructionFormat>,
}
registry.insert(Builder::new("Unary").value()); impl Formats {
registry.insert(Builder::new("UnaryImm").imm(&imm.imm64)); pub fn new(imm: &Immediates, entities: &EntityRefs) -> Self {
registry.insert(Builder::new("UnaryIeee32").imm(&imm.ieee32)); Self {
registry.insert(Builder::new("UnaryIeee64").imm(&imm.ieee64)); unary: Builder::new("Unary").value().build(),
registry.insert(Builder::new("UnaryBool").imm(&imm.boolean));
registry.insert(Builder::new("UnaryConst").imm(&imm.pool_constant));
registry.insert(Builder::new("UnaryGlobalValue").imm(&entities.global_value));
registry.insert(Builder::new("Binary").value().value()); unary_imm: Builder::new("UnaryImm").imm(&imm.imm64).build(),
registry.insert(Builder::new("BinaryImm").value().imm(&imm.imm64));
unary_ieee32: Builder::new("UnaryIeee32").imm(&imm.ieee32).build(),
unary_ieee64: Builder::new("UnaryIeee64").imm(&imm.ieee64).build(),
unary_bool: Builder::new("UnaryBool").imm(&imm.boolean).build(),
unary_const: Builder::new("UnaryConst").imm(&imm.pool_constant).build(),
unary_global_value: Builder::new("UnaryGlobalValue")
.imm(&entities.global_value)
.build(),
binary: Builder::new("Binary").value().value().build(),
binary_imm: Builder::new("BinaryImm").value().imm(&imm.imm64).build(),
// The select instructions are controlled by the second VALUE operand. // The select instructions are controlled by the second VALUE operand.
// The first VALUE operand is the controlling flag which has a derived type. // The first VALUE operand is the controlling flag which has a derived type.
// The fma instruction has the same constraint on all inputs. // The fma instruction has the same constraint on all inputs.
registry.insert( ternary: Builder::new("Ternary")
Builder::new("Ternary")
.value() .value()
.value() .value()
.value() .value()
.typevar_operand(1), .typevar_operand(1)
); .build(),
// Catch-all for instructions with many outputs and inputs and no immediate // Catch-all for instructions with many outputs and inputs and no immediate
// operands. // operands.
registry.insert(Builder::new("MultiAry").varargs()); multiary: Builder::new("MultiAry").varargs().build(),
registry.insert(Builder::new("NullAry")); nullary: Builder::new("NullAry").build(),
registry.insert( insert_lane: Builder::new("InsertLane")
Builder::new("InsertLane")
.value() .value()
.imm_with_name("lane", &imm.uimm8) .imm_with_name("lane", &imm.uimm8)
.value(),
);
registry.insert(
Builder::new("ExtractLane")
.value() .value()
.imm_with_name("lane", &imm.uimm8), .build(),
);
registry.insert(
Builder::new("Shuffle")
.value()
.value()
.imm_with_name("mask", &imm.uimm128),
);
registry.insert(Builder::new("IntCompare").imm(&imm.intcc).value().value()); extract_lane: Builder::new("ExtractLane")
registry.insert( .value()
Builder::new("IntCompareImm") .imm_with_name("lane", &imm.uimm8)
.build(),
shuffle: Builder::new("Shuffle")
.value()
.value()
.imm_with_name("mask", &imm.uimm128)
.build(),
int_compare: Builder::new("IntCompare")
.imm(&imm.intcc) .imm(&imm.intcc)
.value() .value()
.imm(&imm.imm64), .value()
); .build(),
registry.insert(Builder::new("IntCond").imm(&imm.intcc).value());
registry.insert( int_compare_imm: Builder::new("IntCompareImm")
Builder::new("FloatCompare") .imm(&imm.intcc)
.value()
.imm(&imm.imm64)
.build(),
int_cond: Builder::new("IntCond").imm(&imm.intcc).value().build(),
float_compare: Builder::new("FloatCompare")
.imm(&imm.floatcc) .imm(&imm.floatcc)
.value() .value()
.value(), .value()
); .build(),
registry.insert(Builder::new("FloatCond").imm(&imm.floatcc).value());
registry.insert( float_cond: Builder::new("FloatCond").imm(&imm.floatcc).value().build(),
Builder::new("IntSelect")
int_select: Builder::new("IntSelect")
.imm(&imm.intcc) .imm(&imm.intcc)
.value() .value()
.value() .value()
.value(), .value()
); .build(),
registry.insert(Builder::new("Jump").imm(&entities.ebb).varargs()); jump: Builder::new("Jump").imm(&entities.ebb).varargs().build(),
registry.insert(Builder::new("Branch").value().imm(&entities.ebb).varargs());
registry.insert( branch: Builder::new("Branch")
Builder::new("BranchInt") .value()
.imm(&entities.ebb)
.varargs()
.build(),
branch_int: Builder::new("BranchInt")
.imm(&imm.intcc) .imm(&imm.intcc)
.value() .value()
.imm(&entities.ebb) .imm(&entities.ebb)
.varargs(), .varargs()
); .build(),
registry.insert(
Builder::new("BranchFloat") branch_float: Builder::new("BranchFloat")
.imm(&imm.floatcc) .imm(&imm.floatcc)
.value() .value()
.imm(&entities.ebb) .imm(&entities.ebb)
.varargs(), .varargs()
); .build(),
registry.insert(
Builder::new("BranchIcmp") branch_icmp: Builder::new("BranchIcmp")
.imm(&imm.intcc) .imm(&imm.intcc)
.value() .value()
.value() .value()
.imm(&entities.ebb) .imm(&entities.ebb)
.varargs(), .varargs()
); .build(),
registry.insert(
Builder::new("BranchTable") branch_table: Builder::new("BranchTable")
.value() .value()
.imm(&entities.ebb) .imm(&entities.ebb)
.imm(&entities.jump_table), .imm(&entities.jump_table)
); .build(),
registry.insert(
Builder::new("BranchTableEntry") branch_table_entry: Builder::new("BranchTableEntry")
.value() .value()
.value() .value()
.imm(&imm.uimm8) .imm(&imm.uimm8)
.imm(&entities.jump_table), .imm(&entities.jump_table)
); .build(),
registry.insert(Builder::new("BranchTableBase").imm(&entities.jump_table));
registry.insert(
Builder::new("IndirectJump")
.value()
.imm(&entities.jump_table),
);
registry.insert(Builder::new("Call").imm(&entities.func_ref).varargs()); branch_table_base: Builder::new("BranchTableBase")
registry.insert( .imm(&entities.jump_table)
Builder::new("CallIndirect") .build(),
indirect_jump: Builder::new("IndirectJump")
.value()
.imm(&entities.jump_table)
.build(),
call: Builder::new("Call")
.imm(&entities.func_ref)
.varargs()
.build(),
call_indirect: Builder::new("CallIndirect")
.imm(&entities.sig_ref) .imm(&entities.sig_ref)
.value() .value()
.varargs(), .varargs()
); .build(),
registry.insert(Builder::new("FuncAddr").imm(&entities.func_ref));
registry.insert( func_addr: Builder::new("FuncAddr").imm(&entities.func_ref).build(),
Builder::new("Load")
load: Builder::new("Load")
.imm(&imm.memflags) .imm(&imm.memflags)
.value() .value()
.imm(&imm.offset32), .imm(&imm.offset32)
); .build(),
registry.insert(
Builder::new("LoadComplex") load_complex: Builder::new("LoadComplex")
.imm(&imm.memflags) .imm(&imm.memflags)
.varargs() .varargs()
.imm(&imm.offset32), .imm(&imm.offset32)
); .build(),
registry.insert(
Builder::new("Store") store: Builder::new("Store")
.imm(&imm.memflags) .imm(&imm.memflags)
.value() .value()
.value() .value()
.imm(&imm.offset32), .imm(&imm.offset32)
); .build(),
registry.insert(
Builder::new("StoreComplex") store_complex: Builder::new("StoreComplex")
.imm(&imm.memflags) .imm(&imm.memflags)
.value() .value()
.varargs() .varargs()
.imm(&imm.offset32), .imm(&imm.offset32)
); .build(),
registry.insert(
Builder::new("StackLoad") stack_load: Builder::new("StackLoad")
.imm(&entities.stack_slot) .imm(&entities.stack_slot)
.imm(&imm.offset32), .imm(&imm.offset32)
); .build(),
registry.insert(
Builder::new("StackStore") stack_store: Builder::new("StackStore")
.value() .value()
.imm(&entities.stack_slot) .imm(&entities.stack_slot)
.imm(&imm.offset32), .imm(&imm.offset32)
); .build(),
// Accessing a WebAssembly heap. // Accessing a WebAssembly heap.
registry.insert( heap_addr: Builder::new("HeapAddr")
Builder::new("HeapAddr")
.imm(&entities.heap) .imm(&entities.heap)
.value() .value()
.imm(&imm.uimm32), .imm(&imm.uimm32)
); .build(),
// Accessing a WebAssembly table. // Accessing a WebAssembly table.
registry.insert( table_addr: Builder::new("TableAddr")
Builder::new("TableAddr")
.imm(&entities.table) .imm(&entities.table)
.value() .value()
.imm(&imm.offset32), .imm(&imm.offset32)
); .build(),
registry.insert( reg_move: Builder::new("RegMove")
Builder::new("RegMove")
.value() .value()
.imm_with_name("src", &imm.regunit) .imm_with_name("src", &imm.regunit)
.imm_with_name("dst", &imm.regunit), .imm_with_name("dst", &imm.regunit)
); .build(),
registry.insert(
Builder::new("CopySpecial") copy_special: Builder::new("CopySpecial")
.imm_with_name("src", &imm.regunit) .imm_with_name("src", &imm.regunit)
.imm_with_name("dst", &imm.regunit), .imm_with_name("dst", &imm.regunit)
); .build(),
registry.insert(Builder::new("CopyToSsa").imm_with_name("src", &imm.regunit));
registry.insert( copy_to_ssa: Builder::new("CopyToSsa")
Builder::new("RegSpill") .imm_with_name("src", &imm.regunit)
.build(),
reg_spill: Builder::new("RegSpill")
.value() .value()
.imm_with_name("src", &imm.regunit) .imm_with_name("src", &imm.regunit)
.imm_with_name("dst", &entities.stack_slot), .imm_with_name("dst", &entities.stack_slot)
); .build(),
registry.insert(
Builder::new("RegFill") reg_fill: Builder::new("RegFill")
.value() .value()
.imm_with_name("src", &entities.stack_slot) .imm_with_name("src", &entities.stack_slot)
.imm_with_name("dst", &imm.regunit), .imm_with_name("dst", &imm.regunit)
); .build(),
registry.insert(Builder::new("Trap").imm(&imm.trapcode)); trap: Builder::new("Trap").imm(&imm.trapcode).build(),
registry.insert(Builder::new("CondTrap").value().imm(&imm.trapcode));
registry.insert( cond_trap: Builder::new("CondTrap").value().imm(&imm.trapcode).build(),
Builder::new("IntCondTrap")
int_cond_trap: Builder::new("IntCondTrap")
.imm(&imm.intcc) .imm(&imm.intcc)
.value() .value()
.imm(&imm.trapcode), .imm(&imm.trapcode)
); .build(),
registry.insert(
Builder::new("FloatCondTrap") float_cond_trap: Builder::new("FloatCondTrap")
.imm(&imm.floatcc) .imm(&imm.floatcc)
.value() .value()
.imm(&imm.trapcode), .imm(&imm.trapcode)
); .build(),
}
registry }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -8,20 +8,25 @@ pub mod legalize;
pub mod settings; pub mod settings;
pub mod types; pub mod types;
use crate::cdsl::formats::FormatRegistry; use crate::cdsl::formats::{FormatStructure, InstructionFormat};
use crate::cdsl::instructions::{AllInstructions, InstructionGroup}; use crate::cdsl::instructions::{AllInstructions, InstructionGroup};
use crate::cdsl::settings::SettingGroup; use crate::cdsl::settings::SettingGroup;
use crate::cdsl::xform::TransformGroups; use crate::cdsl::xform::TransformGroups;
use crate::shared::entities::EntityRefs; use crate::shared::entities::EntityRefs;
use crate::shared::formats::Formats;
use crate::shared::immediates::Immediates; use crate::shared::immediates::Immediates;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::rc::Rc;
pub(crate) struct Definitions { pub(crate) struct Definitions {
pub settings: SettingGroup, pub settings: SettingGroup,
pub all_instructions: AllInstructions, pub all_instructions: AllInstructions,
pub instructions: InstructionGroup, pub instructions: InstructionGroup,
pub imm: Immediates, pub imm: Immediates,
pub format_registry: FormatRegistry, pub formats: Formats,
pub transform_groups: TransformGroups, pub transform_groups: TransformGroups,
} }
@@ -30,13 +35,9 @@ pub(crate) fn define() -> Definitions {
let immediates = Immediates::new(); let immediates = Immediates::new();
let entities = EntityRefs::new(); let entities = EntityRefs::new();
let format_registry = formats::define(&immediates, &entities); let formats = Formats::new(&immediates, &entities);
let instructions = instructions::define( let instructions =
&mut all_instructions, instructions::define(&mut all_instructions, &formats, &immediates, &entities);
&format_registry,
&immediates,
&entities,
);
let transform_groups = legalize::define(&instructions, &immediates); let transform_groups = legalize::define(&instructions, &immediates);
Definitions { Definitions {
@@ -44,7 +45,53 @@ pub(crate) fn define() -> Definitions {
all_instructions, all_instructions,
instructions, instructions,
imm: immediates, imm: immediates,
format_registry, formats,
transform_groups, transform_groups,
} }
} }
impl Definitions {
/// Verifies certain properties of formats.
///
/// - Formats must be uniquely named: if two formats have the same name, they must refer to the
/// same data. Otherwise, two format variants in the codegen crate would have the same name.
/// - Formats must be structurally different from each other. Otherwise, this would lead to
/// code duplicate in the codegen crate.
///
/// Returns a list of all the instruction formats effectively used.
pub fn verify_instruction_formats(&self) -> Vec<&InstructionFormat> {
let mut format_names: HashMap<&'static str, &Rc<InstructionFormat>> = HashMap::new();
// A structure is: number of input value operands / whether there's varargs or not / names
// of immediate fields.
let mut format_structures: HashMap<FormatStructure, &InstructionFormat> = HashMap::new();
for inst in self.all_instructions.values() {
// Check name.
if let Some(existing_format) = format_names.get(&inst.format.name) {
assert!(
Rc::ptr_eq(&existing_format, &inst.format),
"formats must uniquely named; there's a\
conflict on the name '{}', please make sure it is used only once.",
existing_format.name
);
} else {
format_names.insert(inst.format.name, &inst.format);
}
// Check structure.
let key = inst.format.structure();
if let Some(existing_format) = format_structures.get(&key) {
assert_eq!(
existing_format.name, inst.format.name,
"duplicate instruction formats {} and {}; please remove one.",
existing_format.name, inst.format.name
);
} else {
format_structures.insert(key, &inst.format);
}
}
Vec::from_iter(format_structures.into_iter().map(|(_, v)| v))
}
}