diff --git a/cranelift/codegen/meta/src/cdsl/formats.rs b/cranelift/codegen/meta/src/cdsl/formats.rs index 0e71f37a97..d03929b5f6 100644 --- a/cranelift/codegen/meta/src/cdsl/formats.rs +++ b/cranelift/codegen/meta/src/cdsl/formats.rs @@ -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, } +/// 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::>(), + } + } } -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 { 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, usize, bool), usize>, - formats: Vec>, - 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) -> &Rc { - 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 { - &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> { - self.formats.iter() + }) } } diff --git a/cranelift/codegen/meta/src/cdsl/instructions.rs b/cranelift/codegen/meta/src/cdsl/instructions.rs index fb92b85572..7663d5e22a 100644 --- a/cranelift/codegen/meta/src/cdsl/instructions.rs +++ b/cranelift/codegen/meta/src/cdsl/instructions.rs @@ -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; -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, } -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, operands_in: Option>, operands_out: Option>, constraints: Option>, @@ -219,10 +217,11 @@ pub(crate) struct InstructionBuilder { } impl InstructionBuilder { - pub fn new>(name: S, doc: S) -> Self { + pub fn new>(name: S, doc: S, format: &Rc) -> 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, 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, @@ -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, ) -> 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] diff --git a/cranelift/codegen/meta/src/cdsl/operands.rs b/cranelift/codegen/meta/src/cdsl/operands.rs index a219584c9e..631f0807aa 100644 --- a/cranelift/codegen/meta/src/cdsl/operands.rs +++ b/cranelift/codegen/meta/src/cdsl/operands.rs @@ -138,11 +138,11 @@ pub struct OperandKind { } impl OperandKind { - pub fn imm_key(&self) -> Option { + 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, } } diff --git a/cranelift/codegen/meta/src/cdsl/xform.rs b/cranelift/codegen/meta/src/cdsl/xform.rs index 6980998d70..e9798c404a 100644 --- a/cranelift/codegen/meta/src/cdsl/xform.rs +++ b/cranelift/codegen/meta/src/cdsl/xform.rs @@ -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"); diff --git a/cranelift/codegen/meta/src/gen_inst.rs b/cranelift/codegen/meta/src/gen_inst.rs index 053eba2517..c1a6277b95 100644 --- a/cranelift/codegen/meta/src/gen_inst.rs +++ b/cranelift/codegen/meta/src/gen_inst.rs @@ -4,13 +4,11 @@ use cranelift_codegen_shared::constant_hash; use cranelift_entity::EntityRef; 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::operands::Operand; use crate::cdsl::typevar::{TypeSet, TypeVar}; -use crate::shared::Definitions as SharedDefinitions; - use crate::error; use crate::srcgen::{Formatter, Match}; use crate::unique_table::{UniqueSeqTable, UniqueTable}; @@ -19,7 +17,7 @@ use crate::unique_table::{UniqueSeqTable, UniqueTable}; const TYPESET_LIMIT: usize = 0xff; /// 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( r#" 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("pub enum InstructionFormat {"); fmt.indent(|fmt| { - for format in registry.iter() { + for format in formats { fmt.doc_comment(format.to_string()); 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.indent(|fmt| { let mut m = Match::new("*inst"); - for format in registry.iter() { + for format in formats { m.arm( format!("InstructionData::{}", format.name), 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 /// 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. -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("#[allow(missing_docs)]"); fmt.line("pub enum InstructionData {"); fmt.indent(|fmt| { - for format in registry.iter() { + for format in formats { fmtln!(fmt, "{} {{", format.name); fmt.indent(|fmt| { fmt.line("opcode: Opcode,"); @@ -95,7 +93,7 @@ fn gen_instruction_data(registry: &FormatRegistry, fmt: &mut Formatter) { 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 { ( "arguments_mut", @@ -117,7 +115,7 @@ fn gen_arguments_method(registry: &FormatRegistry, fmt: &mut Formatter, is_mut: ); fmt.indent(|fmt| { let mut m = Match::new("*self"); - for format in registry.iter() { + for format in formats { let name = format!("InstructionData::{}", format.name); // 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 eq(&self, &other: Self, &pool) -> bool` /// - `pub fn hash(&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.indent(|fmt| { fmt.doc_comment("Get the opcode of this instruction."); fmt.line("pub fn opcode(&self) -> Opcode {"); fmt.indent(|fmt| { let mut m = Match::new("*self"); - for format in registry.iter() { + for format in formats { m.arm(format!("InstructionData::{}", format.name), vec!["opcode", ".."], "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 {"); fmt.indent(|fmt| { let mut m = Match::new("*self"); - for format in registry.iter() { + for format in formats { let name = format!("InstructionData::{}", format.name); if format.typevar_operand.is_none() { 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.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.doc_comment(r#"Get mutable references to the value arguments to this instruction."#); - gen_arguments_method(registry, fmt, true); + gen_arguments_method(formats, fmt, true); fmt.empty_line(); fmt.doc_comment(r#" @@ -227,7 +225,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) { fmt.indent(|fmt| { let mut m = Match::new("*self"); - for format in registry.iter() { + for format in formats { if format.has_value_list { m.arm(format!("InstructionData::{}", format.name), vec!["ref mut args", ".."], @@ -254,7 +252,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) { fmt.indent(|fmt| { fmt.line("let args = match *self {"); fmt.indent(|fmt| { - for format in registry.iter() { + for format in formats { if format.has_value_list { 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.indent(|fmt| { - for format in registry.iter() { + for format in formats { let name = format!("&InstructionData::{}", format.name); let mut members = vec!["opcode"]; @@ -336,7 +334,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) { fmt.indent(|fmt| { fmt.line("match *self {"); fmt.indent(|fmt| { - for format in registry.iter() { + for format in formats { let name = format!("InstructionData::{}", format.name); 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. -fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &mut Formatter) { +fn gen_builder( + instructions: &AllInstructions, + formats: &Vec<&InstructionFormat>, + fmt: &mut Formatter, +) { fmt.doc_comment( r#" Convenience methods for building instructions. @@ -1060,7 +1062,7 @@ fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &m for inst in instructions.values() { gen_inst_builder(inst, &*inst.format, fmt); } - for format in formats.iter() { + for format in formats { gen_format_constructor(format, fmt); } }); @@ -1068,20 +1070,18 @@ fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &m } pub(crate) fn generate( - shared_defs: &SharedDefinitions, + formats: Vec<&InstructionFormat>, + all_inst: &AllInstructions, opcode_filename: &str, inst_builder_filename: &str, out_dir: &str, ) -> Result<(), error::Error> { - let format_registry = &shared_defs.format_registry; - let all_inst = &shared_defs.all_instructions; - // Opcodes. let mut fmt = Formatter::new(); - gen_formats(format_registry, &mut fmt); - gen_instruction_data(format_registry, &mut fmt); + gen_formats(&formats, &mut fmt); + gen_instruction_data(&formats, &mut fmt); fmt.empty_line(); - gen_instruction_data_impl(format_registry, &mut fmt); + gen_instruction_data_impl(&formats, &mut fmt); fmt.empty_line(); gen_opcodes(all_inst, &mut fmt); gen_type_constraints(all_inst, &mut fmt); @@ -1089,7 +1089,7 @@ pub(crate) fn generate( // Instruction builder. 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)?; Ok(()) diff --git a/cranelift/codegen/meta/src/isa/arm32/mod.rs b/cranelift/codegen/meta/src/isa/arm32/mod.rs index 152d57d598..5cb1761a7e 100644 --- a/cranelift/codegen/meta/src/isa/arm32/mod.rs +++ b/cranelift/codegen/meta/src/isa/arm32/mod.rs @@ -53,11 +53,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { let settings = define_settings(&shared_defs.settings); let regs = define_regs(); - let inst_group = InstructionGroupBuilder::new( - &mut shared_defs.all_instructions, - &shared_defs.format_registry, - ) - .build(); + let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build(); // CPU modes for 32-bit ARM and Thumb2. let mut a32 = CpuMode::new("A32"); diff --git a/cranelift/codegen/meta/src/isa/arm64/mod.rs b/cranelift/codegen/meta/src/isa/arm64/mod.rs index 3811cc25a6..3440c8af82 100644 --- a/cranelift/codegen/meta/src/isa/arm64/mod.rs +++ b/cranelift/codegen/meta/src/isa/arm64/mod.rs @@ -49,11 +49,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { let settings = define_settings(&shared_defs.settings); let regs = define_registers(); - let inst_group = InstructionGroupBuilder::new( - &mut shared_defs.all_instructions, - &shared_defs.format_registry, - ) - .build(); + let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build(); let mut a64 = CpuMode::new("A64"); diff --git a/cranelift/codegen/meta/src/isa/riscv/mod.rs b/cranelift/codegen/meta/src/isa/riscv/mod.rs index ef173176c0..801e61a3d2 100644 --- a/cranelift/codegen/meta/src/isa/riscv/mod.rs +++ b/cranelift/codegen/meta/src/isa/riscv/mod.rs @@ -89,11 +89,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { let settings = define_settings(&shared_defs.settings); let regs = define_registers(); - let inst_group = InstructionGroupBuilder::new( - &mut shared_defs.all_instructions, - &shared_defs.format_registry, - ) - .build(); + let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build(); // CPU modes for 32-bit and 64-bit operation. let mut rv_32 = CpuMode::new("RV32"); diff --git a/cranelift/codegen/meta/src/isa/riscv/recipes.rs b/cranelift/codegen/meta/src/isa/riscv/recipes.rs index ab70595b34..9f68ed79bd 100644 --- a/cranelift/codegen/meta/src/isa/riscv/recipes.rs +++ b/cranelift/codegen/meta/src/isa/riscv/recipes.rs @@ -46,23 +46,7 @@ impl RecipeGroup { } pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeGroup { - // Format shorthands. - 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"); + let formats = &shared_defs.formats; // Register classes shorthands. 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. // The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8) recipes.push( - EncodingRecipeBuilder::new("R", f_binary, 4) + EncodingRecipeBuilder::new("R", &formats.binary, 4) .operands_in(vec![gpr, gpr]) .operands_out(vec![gpr]) .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. recipes.push( - EncodingRecipeBuilder::new("Rshamt", f_binary_imm, 4) + EncodingRecipeBuilder::new("Rshamt", &formats.binary_imm, 4) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .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. recipes.push( - EncodingRecipeBuilder::new("Ricmp", f_int_compare, 4) + EncodingRecipeBuilder::new("Ricmp", &formats.int_compare, 4) .operands_in(vec![gpr, gpr]) .operands_out(vec![gpr]) .emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"), ); recipes.push( - EncodingRecipeBuilder::new("Ii", f_binary_imm, 4) + EncodingRecipeBuilder::new("Ii", &formats.binary_imm, 4) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_binary_imm, + &*formats.binary_imm, "imm", 12, 0, @@ -110,10 +94,10 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG // I-type instruction with a hardcoded %x0 rs1. recipes.push( - EncodingRecipeBuilder::new("Iz", f_unary_imm, 4) + EncodingRecipeBuilder::new("Iz", &formats.unary_imm, 4) .operands_out(vec![gpr]) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_unary_imm, + &*&formats.unary_imm, "imm", 12, 0, @@ -123,11 +107,11 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG // I-type encoding of an integer comparison. recipes.push( - EncodingRecipeBuilder::new("Iicmp", f_int_compare_imm, 4) + EncodingRecipeBuilder::new("Iicmp", &formats.int_compare_imm, 4) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_int_compare_imm, + &*&formats.int_compare_imm, "imm", 12, 0, @@ -137,8 +121,9 @@ 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 // variable return values are not encoded. - recipes.push(EncodingRecipeBuilder::new("Iret", f_multiary, 4).emit( - r#" + recipes.push( + EncodingRecipeBuilder::new("Iret", &formats.multiary, 4).emit( + r#" // Return instructions are always a jalr to %x1. // The return address is provided as a special-purpose link argument. put_i( @@ -149,11 +134,12 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG sink, ); "#, - )); + ), + ); // I-type encoding for `jalr` as a call_indirect. recipes.push( - EncodingRecipeBuilder::new("Icall", f_call_indirect, 4) + EncodingRecipeBuilder::new("Icall", &formats.call_indirect, 4) .operands_in(vec![gpr]) .emit( 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. recipes.push( - EncodingRecipeBuilder::new("Icopy", f_unary, 4) + EncodingRecipeBuilder::new("Icopy", &formats.unary, 4) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .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. recipes.push( - EncodingRecipeBuilder::new("Irmov", f_regmove, 4) + EncodingRecipeBuilder::new("Irmov", &formats.reg_move, 4) .operands_in(vec![gpr]) .emit("put_i(bits, src, 0, dst, sink);"), ); // Same for copy-to-SSA -- GPR regmove. 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. .operands_out(vec![gpr]) .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. recipes.push( - EncodingRecipeBuilder::new("U", f_unary_imm, 4) + EncodingRecipeBuilder::new("U", &formats.unary_imm, 4) .operands_out(vec![gpr]) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_unary_imm, + &*&formats.unary_imm, "imm", 32, 12, @@ -207,7 +193,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG // UJ-type unconditional branch instructions. recipes.push( - EncodingRecipeBuilder::new("UJ", f_jump, 4) + EncodingRecipeBuilder::new("UJ", &formats.jump, 4) .branch_range((0, 21)) .emit( 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#" sink.reloc_external(Reloc::RiscvCall, &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. recipes.push( - EncodingRecipeBuilder::new("SB", f_branch_icmp, 4) + EncodingRecipeBuilder::new("SB", &formats.branch_icmp, 4) .operands_in(vec![gpr, gpr]) .branch_range((0, 13)) .emit( @@ -244,7 +230,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG // SB-type branch instruction with rs2 fixed to zero. recipes.push( - EncodingRecipeBuilder::new("SBzero", f_branch, 4) + EncodingRecipeBuilder::new("SBzero", &formats.branch, 4) .operands_in(vec![gpr]) .branch_range((0, 13)) .emit( @@ -258,7 +244,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG // Spill of a GPR. recipes.push( - EncodingRecipeBuilder::new("GPsp", f_unary, 4) + EncodingRecipeBuilder::new("GPsp", &formats.unary, 4) .operands_in(vec![gpr]) .operands_out(vec![Stack::new(gpr)]) .emit("unimplemented!();"), @@ -266,7 +252,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG // Fill of a GPR. recipes.push( - EncodingRecipeBuilder::new("GPfi", f_unary, 4) + EncodingRecipeBuilder::new("GPfi", &formats.unary, 4) .operands_in(vec![Stack::new(gpr)]) .operands_out(vec![gpr]) .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. recipes.push( - EncodingRecipeBuilder::new("stacknull", f_unary, 0) + EncodingRecipeBuilder::new("stacknull", &formats.unary, 0) .operands_in(vec![Stack::new(gpr)]) .operands_out(vec![Stack::new(gpr)]) .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. recipes.push( - EncodingRecipeBuilder::new("fillnull", f_unary, 0) + EncodingRecipeBuilder::new("fillnull", &formats.unary, 0) .operands_in(vec![Stack::new(gpr)]) .operands_out(vec![gpr]) .clobbers_flags(false) diff --git a/cranelift/codegen/meta/src/isa/x86/encodings.rs b/cranelift/codegen/meta/src/isa/x86/encodings.rs index a9a6f700ac..94ade711a1 100644 --- a/cranelift/codegen/meta/src/isa/x86/encodings.rs +++ b/cranelift/codegen/meta/src/isa/x86/encodings.rs @@ -374,7 +374,7 @@ pub(crate) fn define( r: &RecipeGroup, ) -> PerCpuModeEncodings { let shared = &shared_defs.instructions; - let formats = &shared_defs.format_registry; + let formats = &shared_defs.formats; // Shorthands for instructions. 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)); // The 32-bit immediate movl also zero-extends to 64 bits. - let f_unary_imm = formats.by_name("UnaryImm"); - let is_unsigned_int32 = InstructionPredicate::new_is_unsigned_int(&*f_unary_imm, "imm", 32, 0); + let is_unsigned_int32 = + InstructionPredicate::new_is_unsigned_int(&*formats.unary_imm, "imm", 32, 0); e.enc64_func( iconst.bind(I64), @@ -801,7 +801,7 @@ pub(crate) fn define( } 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( iconst.bind(I8), 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); // Loads and stores. - let f_load_complex = formats.by_name("LoadComplex"); - let is_load_complex_length_two = InstructionPredicate::new_length_equals(&*f_load_complex, 2); + let is_load_complex_length_two = + InstructionPredicate::new_length_equals(&*formats.load_complex, 2); for recipe in &[rec_ldWithIndex, rec_ldWithIndexDisp8, rec_ldWithIndexDisp32] { 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 = - 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] { 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. - let f_func_addr = formats.by_name("FuncAddr"); - let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*f_func_addr, "func_ref"); + let is_colocated_func = + InstructionPredicate::new_is_colocated_func(&*formats.func_addr, "func_ref"); e.enc64_instp( func_addr.bind(I64), rec_pcrel_fnaddr8.opcodes(&LEA).rex().w(), @@ -1293,8 +1292,7 @@ pub(crate) fn define( 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. - let f_call = formats.by_name("Call"); - let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*f_call, "func_ref"); + let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*formats.call, "func_ref"); 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 @@ -1564,18 +1562,16 @@ pub(crate) fn define( // Floating-point constants equal to 0.0 can be encoded using either `xorps` or `xorpd`, for // 32-bit and 64-bit floats respectively. - let f_unary_ieee32 = formats.by_name("UnaryIeee32"); 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( f32const, rec_f32imm_z.opcodes(&XORPS), is_zero_32_bit_float.clone(), ); - let f_unary_ieee64 = formats.by_name("UnaryIeee64"); 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( f64const, 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 // encoding first 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 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); e.enc_32_64_func(instruction.clone(), template, |builder| { builder.inst_predicate(is_zero_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); e.enc_32_64_func(instruction, template, |builder| { builder.inst_predicate(is_ones_128bit) @@ -2038,9 +2033,11 @@ pub(crate) fn define( }; 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(&*f_int_compare, IntCC::Equal, "cond"); + let has_eq_condition_code = InstructionPredicate::new_has_condition_code( + &*formats.int_compare, + IntCC::Equal, + "cond", + ); let template = rec_icscc_fpr.nonrex().opcodes(opcodes); e.enc_32_64_func(instruction, template, |builder| { let builder = builder.inst_predicate(has_eq_condition_code); diff --git a/cranelift/codegen/meta/src/isa/x86/instructions.rs b/cranelift/codegen/meta/src/isa/x86/instructions.rs index d5d02b1ac5..21e51982ce 100644 --- a/cranelift/codegen/meta/src/isa/x86/instructions.rs +++ b/cranelift/codegen/meta/src/isa/x86/instructions.rs @@ -1,21 +1,22 @@ #![allow(non_snake_case)] -use crate::cdsl::formats::FormatRegistry; use crate::cdsl::instructions::{ AllInstructions, InstructionBuilder as Inst, InstructionGroup, InstructionGroupBuilder, }; use crate::cdsl::operands::{create_operand as operand, create_operand_doc as operand_doc}; use crate::cdsl::types::ValueType; use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar}; + +use crate::shared::formats::Formats; use crate::shared::immediates::Immediates; use crate::shared::types; pub(crate) fn define( mut all_instructions: &mut AllInstructions, - format_registry: &FormatRegistry, + formats: &Formats, immediates: &Immediates, ) -> 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(); @@ -43,6 +44,7 @@ pub(crate) fn define( Return both quotient and remainder. "#, + &formats.ternary, ) .operands_in(vec![nlo, nhi, d]) .operands_out(vec![q, r]) @@ -62,6 +64,7 @@ pub(crate) fn define( Return both quotient and remainder. "#, + &formats.ternary, ) .operands_in(vec![nlo, nhi, d]) .operands_out(vec![q, r]) @@ -82,6 +85,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary, ) .operands_in(vec![argL, argR]) .operands_out(vec![resLo, resHi]), @@ -96,6 +100,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary, ) .operands_in(vec![argL, argR]) .operands_out(vec![resLo, resHi]), @@ -132,6 +137,7 @@ pub(crate) fn define( This instruction does not trap. "#, + &formats.unary, ) .operands_in(vec![x]) .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, even if it is a signalling NaN. "#, + &formats.binary, ) .operands_in(vec![x, y]) .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, even if it is a signalling NaN. "#, + &formats.binary, ) .operands_in(vec![x, y]) .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 in 64-bit mode, and only for i32 in 32-bit mode. "#, + &formats.unary, ) .operands_in(vec![x]) .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 in 64-bit mode, and only for i32 in 32-bit mode. "#, + &formats.nullary, ) .operands_out(vec![x]) .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 i32 in 64-bit mode, and only for i32 in 32-bit mode. "#, + &formats.unary, ) .operands_in(vec![x]) .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 in the word. Is otherwise identical to 'bsr', just above. "#, + &formats.unary, ) .operands_in(vec![x]) .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 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_out(vec![a]), @@ -281,6 +294,7 @@ pub(crate) fn define( Packed Shuffle Bytes -- re-orders data in an extended register using a shuffle 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_out(vec![a]), @@ -298,6 +312,7 @@ pub(crate) fn define( The lane index, ``Idx``, is an immediate value, not an SSA value. It must indicate a valid lane index for the type of ``x``. "#, + &formats.extract_lane, ) .operands_in(vec![x, Idx]) .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 must indicate a valid lane index for the type of ``x``. "#, + &formats.insert_lane, ) .operands_in(vec![x, Idx, y]) .operands_out(vec![a]), @@ -347,10 +363,11 @@ pub(crate) fn define( Inst::new( "x86_insertps", r#" - Insert a lane of ``y`` into ``x`` at using ``Idx`` to encode both which lane the value is - extracted from and which it is inserted to. This is similar to x86_pinsr but inserts + Insert a lane of ``y`` into ``x`` at using ``Idx`` to encode both which lane the value is + 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. "#, + &formats.insert_lane, ) .operands_in(vec![x, Idx, y]) .operands_out(vec![a]), @@ -366,6 +383,7 @@ pub(crate) fn define( r#" 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_out(vec![a]), @@ -377,6 +395,7 @@ pub(crate) fn define( r#" 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_out(vec![a]), @@ -401,41 +420,48 @@ pub(crate) fn define( .includes_scalars(false) .build(), ); + let x = &operand_doc("x", IxN, "Vector value to shift"); let y = &operand_doc("y", I64x2, "Number of bits to shift"); let a = &operand("a", IxN); + ig.push( Inst::new( "x86_psll", r#" - Shift Packed Data Left Logical -- This implements the behavior of the shared instruction + Shift Packed Data Left Logical -- This implements the behavior of the shared instruction ``ishl`` but alters the shift operand to live in an XMM register as expected by the PSLL* family of instructions. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), ); + ig.push( Inst::new( "x86_psrl", r#" - Shift Packed Data Right Logical -- This implements the behavior of the shared instruction + Shift Packed Data Right Logical -- This implements the behavior of the shared instruction ``ushr`` but alters the shift operand to live in an XMM register as expected by the PSRL* family of instructions. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), ); + ig.push( Inst::new( "x86_psra", r#" - Shift Packed Data Right Arithmetic -- This implements the behavior of the shared - instruction ``sshr`` but alters the shift operand to live in an XMM register as expected by + Shift Packed Data Right Arithmetic -- This implements the behavior of the shared + instruction ``sshr`` but alters the shift operand to live in an XMM register as expected by the PSRA* family of instructions. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), diff --git a/cranelift/codegen/meta/src/isa/x86/mod.rs b/cranelift/codegen/meta/src/isa/x86/mod.rs index e18c54ad64..1322368265 100644 --- a/cranelift/codegen/meta/src/isa/x86/mod.rs +++ b/cranelift/codegen/meta/src/isa/x86/mod.rs @@ -20,7 +20,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { let inst_group = instructions::define( &mut shared_defs.all_instructions, - &shared_defs.format_registry, + &shared_defs.formats, &shared_defs.imm, ); legalize::define(shared_defs, &inst_group); diff --git a/cranelift/codegen/meta/src/isa/x86/recipes.rs b/cranelift/codegen/meta/src/isa/x86/recipes.rs index bf91a6326c..c1fd7a2c17 100644 --- a/cranelift/codegen/meta/src/isa/x86/recipes.rs +++ b/cranelift/codegen/meta/src/isa/x86/recipes.rs @@ -345,52 +345,7 @@ pub(crate) fn define<'shared>( let stack_gpr32 = Stack::new(gpr); let stack_fpr32 = Stack::new(fpr); - // Format shorthands, prefixed with f_. - 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_float = formats.by_name("BranchFloat"); - let f_branch_int = formats.by_name("BranchInt"); - let f_branch_table_entry = formats.by_name("BranchTableEntry"); - let f_branch_table_base = formats.by_name("BranchTableBase"); - let f_call = formats.by_name("Call"); - let f_call_indirect = formats.by_name("CallIndirect"); - let f_copy_special = formats.by_name("CopySpecial"); - let f_copy_to_ssa = formats.by_name("CopyToSsa"); - let f_extract_lane = formats.by_name("ExtractLane"); // TODO this would preferably retrieve a BinaryImm8 format but because formats are compared structurally and ExtractLane has the same structure this is impossible--if we rename ExtractLane, it may even impact parsing - let f_float_compare = formats.by_name("FloatCompare"); - let f_float_cond = formats.by_name("FloatCond"); - let f_float_cond_trap = formats.by_name("FloatCondTrap"); - let f_func_addr = formats.by_name("FuncAddr"); - let f_indirect_jump = formats.by_name("IndirectJump"); - let f_insert_lane = formats.by_name("InsertLane"); - let f_int_compare = formats.by_name("IntCompare"); - let f_int_compare_imm = formats.by_name("IntCompareImm"); - let f_int_cond = formats.by_name("IntCond"); - let f_int_cond_trap = formats.by_name("IntCondTrap"); - let f_int_select = formats.by_name("IntSelect"); - let f_jump = formats.by_name("Jump"); - let f_load = formats.by_name("Load"); - let f_load_complex = formats.by_name("LoadComplex"); - let f_multiary = formats.by_name("MultiAry"); - let f_nullary = formats.by_name("NullAry"); - let f_reg_fill = formats.by_name("RegFill"); - let f_reg_move = formats.by_name("RegMove"); - let f_reg_spill = formats.by_name("RegSpill"); - let f_stack_load = formats.by_name("StackLoad"); - let f_store = formats.by_name("Store"); - let f_store_complex = formats.by_name("StoreComplex"); - let f_ternary = formats.by_name("Ternary"); - let f_trap = formats.by_name("Trap"); - let f_unary = formats.by_name("Unary"); - let f_unary_bool = formats.by_name("UnaryBool"); - let f_unary_const = formats.by_name("UnaryConst"); - let f_unary_global_value = formats.by_name("UnaryGlobalValue"); - let f_unary_ieee32 = formats.by_name("UnaryIeee32"); - let f_unary_ieee64 = formats.by_name("UnaryIeee64"); - let f_unary_imm = formats.by_name("UnaryImm"); + let formats = &shared_defs.formats; // Predicates shorthands. let use_sse41 = settings.predicate_by_name("use_sse41"); @@ -401,32 +356,32 @@ pub(crate) fn define<'shared>( // A null unary instruction that takes a GPR register. Can be used for identity copies and // no-op conversions. recipes.add_recipe( - EncodingRecipeBuilder::new("null", f_unary, 0) + EncodingRecipeBuilder::new("null", &formats.unary, 0) .operands_in(vec![gpr]) .operands_out(vec![0]) .emit(""), ); recipes.add_recipe( - EncodingRecipeBuilder::new("null_fpr", f_unary, 0) + EncodingRecipeBuilder::new("null_fpr", &formats.unary, 0) .operands_in(vec![fpr]) .operands_out(vec![0]) .emit(""), ); recipes.add_recipe( - EncodingRecipeBuilder::new("stacknull", f_unary, 0) + EncodingRecipeBuilder::new("stacknull", &formats.unary, 0) .operands_in(vec![stack_gpr32]) .operands_out(vec![stack_gpr32]) .emit(""), ); recipes.add_recipe( - EncodingRecipeBuilder::new("get_pinned_reg", f_nullary, 0) + EncodingRecipeBuilder::new("get_pinned_reg", &formats.nullary, 0) .operands_out(vec![reg_r15]) .emit(""), ); // umr with a fixed register output that's r15. recipes.add_template_recipe( - EncodingRecipeBuilder::new("set_pinned_reg", f_unary, 1) + EncodingRecipeBuilder::new("set_pinned_reg", &formats.unary, 1) .operands_in(vec![gpr]) .clobbers_flags(false) .emit( @@ -440,25 +395,26 @@ pub(crate) fn define<'shared>( // No-op fills, created by late-stage redundant-fill removal. recipes.add_recipe( - EncodingRecipeBuilder::new("fillnull", f_unary, 0) + EncodingRecipeBuilder::new("fillnull", &formats.unary, 0) .operands_in(vec![stack_gpr32]) .operands_out(vec![gpr]) .clobbers_flags(false) .emit(""), ); recipes.add_recipe( - EncodingRecipeBuilder::new("ffillnull", f_unary, 0) + EncodingRecipeBuilder::new("ffillnull", &formats.unary, 0) .operands_in(vec![stack_gpr32]) .operands_out(vec![fpr]) .clobbers_flags(false) .emit(""), ); - recipes - .add_recipe(EncodingRecipeBuilder::new("debugtrap", f_nullary, 1).emit("sink.put1(0xcc);")); + recipes.add_recipe( + EncodingRecipeBuilder::new("debugtrap", &formats.nullary, 1).emit("sink.put1(0xcc);"), + ); // XX opcode, no ModR/M. - recipes.add_template_recipe(EncodingRecipeBuilder::new("trap", f_trap, 0).emit( + recipes.add_template_recipe(EncodingRecipeBuilder::new("trap", &formats.trap, 0).emit( r#" sink.trap(code, func.srclocs[inst]); {{PUT_OP}}(bits, BASE_REX, sink); @@ -467,7 +423,7 @@ pub(crate) fn define<'shared>( // Macro: conditional jump over a ud2. recipes.add_recipe( - EncodingRecipeBuilder::new("trapif", f_int_cond_trap, 4) + EncodingRecipeBuilder::new("trapif", &formats.int_cond_trap, 4) .operands_in(vec![reg_rflags]) .clobbers_flags(false) .emit( @@ -484,12 +440,12 @@ pub(crate) fn define<'shared>( ); recipes.add_recipe( - EncodingRecipeBuilder::new("trapff", f_float_cond_trap, 4) + EncodingRecipeBuilder::new("trapff", &formats.float_cond_trap, 4) .operands_in(vec![reg_rflags]) .clobbers_flags(false) .inst_predicate(supported_floatccs_predicate( &supported_floatccs, - &*f_float_cond_trap, + &*formats.float_cond_trap, )) .emit( r#" @@ -506,7 +462,7 @@ pub(crate) fn define<'shared>( // XX /r recipes.add_template_recipe( - EncodingRecipeBuilder::new("rr", f_binary, 1) + EncodingRecipeBuilder::new("rr", &formats.binary, 1) .operands_in(vec![gpr, gpr]) .operands_out(vec![0]) .emit( @@ -519,7 +475,7 @@ pub(crate) fn define<'shared>( // XX /r with operands swapped. (RM form). recipes.add_template_recipe( - EncodingRecipeBuilder::new("rrx", f_binary, 1) + EncodingRecipeBuilder::new("rrx", &formats.binary, 1) .operands_in(vec![gpr, gpr]) .operands_out(vec![0]) .emit( @@ -532,7 +488,7 @@ pub(crate) fn define<'shared>( // XX /r with FPR ins and outs. A form. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fa", f_binary, 1) + EncodingRecipeBuilder::new("fa", &formats.binary, 1) .operands_in(vec![fpr, fpr]) .operands_out(vec![0]) .emit( @@ -545,7 +501,7 @@ pub(crate) fn define<'shared>( // XX /r with FPR ins and outs. A form with input operands swapped. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fax", f_binary, 1) + EncodingRecipeBuilder::new("fax", &formats.binary, 1) .operands_in(vec![fpr, fpr]) .operands_out(vec![1]) .emit( @@ -559,11 +515,11 @@ pub(crate) fn define<'shared>( // XX /r with FPR ins and outs. A form with a byte immediate. { recipes.add_template_recipe( - EncodingRecipeBuilder::new("fa_ib", f_insert_lane, 2) + EncodingRecipeBuilder::new("fa_ib", &formats.insert_lane, 2) .operands_in(vec![fpr, fpr]) .operands_out(vec![0]) .inst_predicate(InstructionPredicate::new_is_unsigned_int( - &*f_insert_lane, + &*formats.insert_lane, "lane", 8, 0, @@ -581,7 +537,7 @@ pub(crate) fn define<'shared>( // XX /n for a unary operation with extension bits. recipes.add_template_recipe( - EncodingRecipeBuilder::new("ur", f_unary, 1) + EncodingRecipeBuilder::new("ur", &formats.unary, 1) .operands_in(vec![gpr]) .operands_out(vec![0]) .emit( @@ -595,7 +551,7 @@ pub(crate) fn define<'shared>( // XX /r, but for a unary operator with separate input/output register, like // copies. MR form, preserving flags. recipes.add_template_recipe( - EncodingRecipeBuilder::new("umr", f_unary, 1) + EncodingRecipeBuilder::new("umr", &formats.unary, 1) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .clobbers_flags(false) @@ -609,7 +565,7 @@ pub(crate) fn define<'shared>( // Same as umr, but with FPR -> GPR registers. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rfumr", f_unary, 1) + EncodingRecipeBuilder::new("rfumr", &formats.unary, 1) .operands_in(vec![fpr]) .operands_out(vec![gpr]) .clobbers_flags(false) @@ -623,7 +579,7 @@ pub(crate) fn define<'shared>( // Same as umr, but with the source register specified directly. recipes.add_template_recipe( - EncodingRecipeBuilder::new("umr_reg_to_ssa", f_copy_to_ssa, 1) + EncodingRecipeBuilder::new("umr_reg_to_ssa", &formats.copy_to_ssa, 1) // No operands_in to mention, because a source register is specified directly. .operands_out(vec![gpr]) .clobbers_flags(false) @@ -638,7 +594,7 @@ pub(crate) fn define<'shared>( // XX /r, but for a unary operator with separate input/output register. // RM form. Clobbers FLAGS. recipes.add_template_recipe( - EncodingRecipeBuilder::new("urm", f_unary, 1) + EncodingRecipeBuilder::new("urm", &formats.unary, 1) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .emit( @@ -651,7 +607,7 @@ pub(crate) fn define<'shared>( // XX /r. Same as urm, but doesn't clobber FLAGS. let urm_noflags = recipes.add_template_recipe( - EncodingRecipeBuilder::new("urm_noflags", f_unary, 1) + EncodingRecipeBuilder::new("urm_noflags", &formats.unary, 1) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .clobbers_flags(false) @@ -666,7 +622,7 @@ pub(crate) fn define<'shared>( // XX /r. Same as urm_noflags, but input limited to ABCD. recipes.add_template( Template::new( - EncodingRecipeBuilder::new("urm_noflags_abcd", f_unary, 1) + EncodingRecipeBuilder::new("urm_noflags_abcd", &formats.unary, 1) .operands_in(vec![abcd]) .operands_out(vec![gpr]) .clobbers_flags(false) @@ -683,7 +639,7 @@ pub(crate) fn define<'shared>( // XX /r, RM form, FPR -> FPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("furm", f_unary, 1) + EncodingRecipeBuilder::new("furm", &formats.unary, 1) .operands_in(vec![fpr]) .operands_out(vec![fpr]) .clobbers_flags(false) @@ -697,7 +653,7 @@ pub(crate) fn define<'shared>( // Same as furm, but with the source register specified directly. recipes.add_template_recipe( - EncodingRecipeBuilder::new("furm_reg_to_ssa", f_copy_to_ssa, 1) + EncodingRecipeBuilder::new("furm_reg_to_ssa", &formats.copy_to_ssa, 1) // No operands_in to mention, because a source register is specified directly. .operands_out(vec![fpr]) .clobbers_flags(false) @@ -711,7 +667,7 @@ pub(crate) fn define<'shared>( // XX /r, RM form, GPR -> FPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("frurm", f_unary, 1) + EncodingRecipeBuilder::new("frurm", &formats.unary, 1) .operands_in(vec![gpr]) .operands_out(vec![fpr]) .clobbers_flags(false) @@ -725,7 +681,7 @@ pub(crate) fn define<'shared>( // XX /r, RM form, FPR -> GPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rfurm", f_unary, 1) + EncodingRecipeBuilder::new("rfurm", &formats.unary, 1) .operands_in(vec![fpr]) .operands_out(vec![gpr]) .clobbers_flags(false) @@ -739,7 +695,7 @@ pub(crate) fn define<'shared>( // XX /r, RMI form for one of the roundXX SSE 4.1 instructions. recipes.add_template_recipe( - EncodingRecipeBuilder::new("furmi_rnd", f_unary, 2) + EncodingRecipeBuilder::new("furmi_rnd", &formats.unary, 2) .operands_in(vec![fpr]) .operands_out(vec![fpr]) .isa_predicate(use_sse41) @@ -760,7 +716,7 @@ pub(crate) fn define<'shared>( // XX /r, for regmove instructions. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rmov", f_reg_move, 1) + EncodingRecipeBuilder::new("rmov", &formats.reg_move, 1) .operands_in(vec![gpr]) .clobbers_flags(false) .emit( @@ -773,7 +729,7 @@ pub(crate) fn define<'shared>( // XX /r, for regmove instructions (FPR version, RM encoded). recipes.add_template_recipe( - EncodingRecipeBuilder::new("frmov", f_reg_move, 1) + EncodingRecipeBuilder::new("frmov", &formats.reg_move, 1) .operands_in(vec![fpr]) .clobbers_flags(false) .emit( @@ -786,7 +742,7 @@ pub(crate) fn define<'shared>( // XX /n with one arg in %rcx, for shifts. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rc", f_binary, 1) + EncodingRecipeBuilder::new("rc", &formats.binary, 1) .operands_in(vec![ OperandConstraint::RegClass(gpr), OperandConstraint::FixedReg(reg_rcx), @@ -802,7 +758,7 @@ pub(crate) fn define<'shared>( // XX /n for division: inputs in %rax, %rdx, r. Outputs in %rax, %rdx. recipes.add_template_recipe( - EncodingRecipeBuilder::new("div", f_ternary, 1) + EncodingRecipeBuilder::new("div", &formats.ternary, 1) .operands_in(vec![ OperandConstraint::FixedReg(reg_rax), OperandConstraint::FixedReg(reg_rdx), @@ -820,7 +776,7 @@ pub(crate) fn define<'shared>( // XX /n for {s,u}mulx: inputs in %rax, r. Outputs in %rdx(hi):%rax(lo) recipes.add_template_recipe( - EncodingRecipeBuilder::new("mulx", f_binary, 1) + EncodingRecipeBuilder::new("mulx", &formats.binary, 1) .operands_in(vec![ OperandConstraint::FixedReg(reg_rax), OperandConstraint::RegClass(gpr), @@ -840,11 +796,11 @@ pub(crate) fn define<'shared>( // XX /n ib with 8-bit immediate sign-extended. { recipes.add_template_recipe( - EncodingRecipeBuilder::new("r_ib", f_binary_imm, 2) + EncodingRecipeBuilder::new("r_ib", &formats.binary_imm, 2) .operands_in(vec![gpr]) .operands_out(vec![0]) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_binary_imm, + &*formats.binary_imm, "imm", 8, 0, @@ -861,11 +817,11 @@ pub(crate) fn define<'shared>( // XX /n id with 32-bit immediate sign-extended. recipes.add_template_recipe( - EncodingRecipeBuilder::new("r_id", f_binary_imm, 5) + EncodingRecipeBuilder::new("r_id", &formats.binary_imm, 5) .operands_in(vec![gpr]) .operands_out(vec![0]) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_binary_imm, + &*formats.binary_imm, "imm", 32, 0, @@ -884,11 +840,11 @@ pub(crate) fn define<'shared>( // XX /r ib with 8-bit unsigned immediate (e.g. for pshufd) { recipes.add_template_recipe( - EncodingRecipeBuilder::new("r_ib_unsigned_fpr", f_extract_lane, 2) + EncodingRecipeBuilder::new("r_ib_unsigned_fpr", &formats.extract_lane, 2) .operands_in(vec![fpr]) .operands_out(vec![fpr]) .inst_predicate(InstructionPredicate::new_is_unsigned_int( - &*f_extract_lane, + &*formats.extract_lane, "lane", 8, 0, @@ -907,11 +863,11 @@ pub(crate) fn define<'shared>( // XX /r ib with 8-bit unsigned immediate (e.g. for extractlane) { recipes.add_template_recipe( - EncodingRecipeBuilder::new("r_ib_unsigned_gpr", f_extract_lane, 2) + EncodingRecipeBuilder::new("r_ib_unsigned_gpr", &formats.extract_lane, 2) .operands_in(vec![fpr]) .operands_out(vec![gpr]) .inst_predicate(InstructionPredicate::new_is_unsigned_int( - &*f_extract_lane, "lane", 8, 0, + &*formats.extract_lane, "lane", 8, 0, )) .emit( r#" @@ -927,11 +883,11 @@ pub(crate) fn define<'shared>( // XX /r ib with 8-bit unsigned immediate (e.g. for insertlane) { recipes.add_template_recipe( - EncodingRecipeBuilder::new("r_ib_unsigned_r", f_insert_lane, 2) + EncodingRecipeBuilder::new("r_ib_unsigned_r", &formats.insert_lane, 2) .operands_in(vec![fpr, gpr]) .operands_out(vec![0]) .inst_predicate(InstructionPredicate::new_is_unsigned_int( - &*f_insert_lane, + &*formats.insert_lane, "lane", 8, 0, @@ -950,10 +906,10 @@ pub(crate) fn define<'shared>( { // XX /n id with 32-bit immediate sign-extended. UnaryImm version. recipes.add_template_recipe( - EncodingRecipeBuilder::new("u_id", f_unary_imm, 5) + EncodingRecipeBuilder::new("u_id", &formats.unary_imm, 5) .operands_out(vec![gpr]) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_unary_imm, + &*formats.unary_imm, "imm", 32, 0, @@ -971,7 +927,7 @@ pub(crate) fn define<'shared>( // XX+rd id unary with 32-bit immediate. Note no recipe predicate. recipes.add_template_recipe( - EncodingRecipeBuilder::new("pu_id", f_unary_imm, 4) + EncodingRecipeBuilder::new("pu_id", &formats.unary_imm, 4) .operands_out(vec![gpr]) .emit( r#" @@ -986,7 +942,7 @@ pub(crate) fn define<'shared>( // XX+rd id unary with bool immediate. Note no recipe predicate. recipes.add_template_recipe( - EncodingRecipeBuilder::new("pu_id_bool", f_unary_bool, 4) + EncodingRecipeBuilder::new("pu_id_bool", &formats.unary_bool, 4) .operands_out(vec![gpr]) .emit( r#" @@ -1001,7 +957,7 @@ pub(crate) fn define<'shared>( // XX+rd id nullary with 0 as 32-bit immediate. Note no recipe predicate. recipes.add_template_recipe( - EncodingRecipeBuilder::new("pu_id_ref", f_nullary, 4) + EncodingRecipeBuilder::new("pu_id_ref", &formats.nullary, 4) .operands_out(vec![gpr]) .emit( r#" @@ -1015,7 +971,7 @@ pub(crate) fn define<'shared>( // XX+rd iq unary with 64-bit immediate. recipes.add_template_recipe( - EncodingRecipeBuilder::new("pu_iq", f_unary_imm, 8) + EncodingRecipeBuilder::new("pu_iq", &formats.unary_imm, 8) .operands_out(vec![gpr]) .emit( r#" @@ -1028,7 +984,7 @@ pub(crate) fn define<'shared>( // XX+rd id unary with zero immediate. recipes.add_template_recipe( - EncodingRecipeBuilder::new("u_id_z", f_unary_imm, 1) + EncodingRecipeBuilder::new("u_id_z", &formats.unary_imm, 1) .operands_out(vec![gpr]) .emit( r#" @@ -1041,10 +997,10 @@ pub(crate) fn define<'shared>( // XX /n Unary with floating point 32-bit immediate equal to zero. { recipes.add_template_recipe( - EncodingRecipeBuilder::new("f32imm_z", f_unary_ieee32, 1) + EncodingRecipeBuilder::new("f32imm_z", &formats.unary_ieee32, 1) .operands_out(vec![fpr]) .inst_predicate(InstructionPredicate::new_is_zero_32bit_float( - &*f_unary_ieee32, + &*formats.unary_ieee32, "imm", )) .emit( @@ -1059,10 +1015,10 @@ pub(crate) fn define<'shared>( // XX /n Unary with floating point 64-bit immediate equal to zero. { recipes.add_template_recipe( - EncodingRecipeBuilder::new("f64imm_z", f_unary_ieee64, 1) + EncodingRecipeBuilder::new("f64imm_z", &formats.unary_ieee64, 1) .operands_out(vec![fpr]) .inst_predicate(InstructionPredicate::new_is_zero_64bit_float( - &*f_unary_ieee64, + &*formats.unary_ieee64, "imm", )) .emit( @@ -1075,7 +1031,7 @@ pub(crate) fn define<'shared>( } recipes.add_template_recipe( - EncodingRecipeBuilder::new("pushq", f_unary, 0) + EncodingRecipeBuilder::new("pushq", &formats.unary, 0) .operands_in(vec![gpr]) .emit( r#" @@ -1086,7 +1042,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("popq", f_nullary, 0) + EncodingRecipeBuilder::new("popq", &formats.nullary, 0) .operands_out(vec![gpr]) .emit( r#" @@ -1097,7 +1053,7 @@ pub(crate) fn define<'shared>( // XX /r, for regmove instructions. recipes.add_template_recipe( - EncodingRecipeBuilder::new("copysp", f_copy_special, 1) + EncodingRecipeBuilder::new("copysp", &formats.copy_special, 1) .clobbers_flags(false) .emit( r#" @@ -1108,7 +1064,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("adjustsp", f_unary, 1) + EncodingRecipeBuilder::new("adjustsp", &formats.unary, 1) .operands_in(vec![gpr]) .emit( r#" @@ -1120,9 +1076,9 @@ pub(crate) fn define<'shared>( { recipes.add_template_recipe( - EncodingRecipeBuilder::new("adjustsp_ib", f_unary_imm, 2) + EncodingRecipeBuilder::new("adjustsp_ib", &formats.unary_imm, 2) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_unary_imm, + &*formats.unary_imm, "imm", 8, 0, @@ -1138,9 +1094,9 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("adjustsp_id", f_unary_imm, 5) + EncodingRecipeBuilder::new("adjustsp_id", &formats.unary_imm, 5) .inst_predicate(InstructionPredicate::new_is_signed_int( - &*f_unary_imm, + &*formats.unary_imm, "imm", 32, 0, @@ -1158,7 +1114,7 @@ pub(crate) fn define<'shared>( // XX+rd id with Abs4 function relocation. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fnaddr4", f_func_addr, 4) + EncodingRecipeBuilder::new("fnaddr4", &formats.func_addr, 4) .operands_out(vec![gpr]) .emit( r#" @@ -1173,7 +1129,7 @@ pub(crate) fn define<'shared>( // XX+rd iq with Abs8 function relocation. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fnaddr8", f_func_addr, 8) + EncodingRecipeBuilder::new("fnaddr8", &formats.func_addr, 8) .operands_out(vec![gpr]) .emit( r#" @@ -1188,7 +1144,7 @@ pub(crate) fn define<'shared>( // Similar to fnaddr4, but writes !0 (this is used by BaldrMonkey). recipes.add_template_recipe( - EncodingRecipeBuilder::new("allones_fnaddr4", f_func_addr, 4) + EncodingRecipeBuilder::new("allones_fnaddr4", &formats.func_addr, 4) .operands_out(vec![gpr]) .emit( r#" @@ -1204,7 +1160,7 @@ pub(crate) fn define<'shared>( // Similar to fnaddr8, but writes !0 (this is used by BaldrMonkey). recipes.add_template_recipe( - EncodingRecipeBuilder::new("allones_fnaddr8", f_func_addr, 8) + EncodingRecipeBuilder::new("allones_fnaddr8", &formats.func_addr, 8) .operands_out(vec![gpr]) .emit( r#" @@ -1219,7 +1175,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("pcrel_fnaddr8", f_func_addr, 5) + EncodingRecipeBuilder::new("pcrel_fnaddr8", &formats.func_addr, 5) .operands_out(vec![gpr]) // rex2 gets passed 0 for r/m register because the upper bit of // r/m doesn't get decoded when in rip-relative addressing mode. @@ -1238,7 +1194,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("got_fnaddr8", f_func_addr, 5) + EncodingRecipeBuilder::new("got_fnaddr8", &formats.func_addr, 5) .operands_out(vec![gpr]) // rex2 gets passed 0 for r/m register because the upper bit of // r/m doesn't get decoded when in rip-relative addressing mode. @@ -1258,7 +1214,7 @@ pub(crate) fn define<'shared>( // XX+rd id with Abs4 globalsym relocation. recipes.add_template_recipe( - EncodingRecipeBuilder::new("gvaddr4", f_unary_global_value, 4) + EncodingRecipeBuilder::new("gvaddr4", &formats.unary_global_value, 4) .operands_out(vec![gpr]) .emit( r#" @@ -1273,7 +1229,7 @@ pub(crate) fn define<'shared>( // XX+rd iq with Abs8 globalsym relocation. recipes.add_template_recipe( - EncodingRecipeBuilder::new("gvaddr8", f_unary_global_value, 8) + EncodingRecipeBuilder::new("gvaddr8", &formats.unary_global_value, 8) .operands_out(vec![gpr]) .emit( r#" @@ -1288,7 +1244,7 @@ pub(crate) fn define<'shared>( // XX+rd iq with PCRel4 globalsym relocation. recipes.add_template_recipe( - EncodingRecipeBuilder::new("pcrel_gvaddr8", f_unary_global_value, 5) + EncodingRecipeBuilder::new("pcrel_gvaddr8", &formats.unary_global_value, 5) .operands_out(vec![gpr]) .emit( r#" @@ -1306,7 +1262,7 @@ pub(crate) fn define<'shared>( // XX+rd iq with Abs8 globalsym relocation. recipes.add_template_recipe( - EncodingRecipeBuilder::new("got_gvaddr8", f_unary_global_value, 5) + EncodingRecipeBuilder::new("got_gvaddr8", &formats.unary_global_value, 5) .operands_out(vec![gpr]) .emit( r#" @@ -1327,7 +1283,7 @@ pub(crate) fn define<'shared>( // TODO Alternative forms for 8-bit immediates, when applicable. recipes.add_template_recipe( - EncodingRecipeBuilder::new("spaddr4_id", f_stack_load, 6) + EncodingRecipeBuilder::new("spaddr4_id", &formats.stack_load, 6) .operands_out(vec![gpr]) .emit( r#" @@ -1343,7 +1299,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("spaddr8_id", f_stack_load, 6) + EncodingRecipeBuilder::new("spaddr8_id", &formats.stack_load, 6) .operands_out(vec![gpr]) .emit( r#" @@ -1365,11 +1321,11 @@ pub(crate) fn define<'shared>( // A predicate asking if the offset is zero. let has_no_offset = - InstructionPredicate::new_is_field_equal(&*f_store, "offset", "0".into()); + InstructionPredicate::new_is_field_equal(&*formats.store, "offset", "0".into()); // XX /r register-indirect store with no offset. let st = recipes.add_template_recipe( - EncodingRecipeBuilder::new("st", f_store, 1) + EncodingRecipeBuilder::new("st", &formats.store, 1) .operands_in(vec![gpr, gpr]) .inst_predicate(has_no_offset.clone()) .clobbers_flags(false) @@ -1397,7 +1353,7 @@ pub(crate) fn define<'shared>( // Only ABCD allowed for stored value. This is for byte stores with no REX. recipes.add_template( Template::new( - EncodingRecipeBuilder::new("st_abcd", f_store, 1) + EncodingRecipeBuilder::new("st_abcd", &formats.store, 1) .operands_in(vec![abcd, gpr]) .inst_predicate(has_no_offset.clone()) .clobbers_flags(false) @@ -1426,7 +1382,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store of FPR with no offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fst", f_store, 1) + EncodingRecipeBuilder::new("fst", &formats.store, 1) .operands_in(vec![fpr, gpr]) .inst_predicate(has_no_offset.clone()) .clobbers_flags(false) @@ -1450,11 +1406,12 @@ pub(crate) fn define<'shared>( ), ); - let has_small_offset = InstructionPredicate::new_is_signed_int(&*f_store, "offset", 8, 0); + let has_small_offset = + InstructionPredicate::new_is_signed_int(&*formats.store, "offset", 8, 0); // XX /r register-indirect store with 8-bit offset. let st_disp8 = recipes.add_template_recipe( - EncodingRecipeBuilder::new("stDisp8", f_store, 2) + EncodingRecipeBuilder::new("stDisp8", &formats.store, 2) .operands_in(vec![gpr, gpr]) .inst_predicate(has_small_offset.clone()) .clobbers_flags(false) @@ -1481,7 +1438,7 @@ pub(crate) fn define<'shared>( // Only ABCD allowed for stored value. This is for byte stores with no REX. recipes.add_template( Template::new( - EncodingRecipeBuilder::new("stDisp8_abcd", f_store, 2) + EncodingRecipeBuilder::new("stDisp8_abcd", &formats.store, 2) .operands_in(vec![abcd, gpr]) .inst_predicate(has_small_offset.clone()) .clobbers_flags(false) @@ -1509,7 +1466,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with 8-bit offset of FPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fstDisp8", f_store, 2) + EncodingRecipeBuilder::new("fstDisp8", &formats.store, 2) .operands_in(vec![fpr, gpr]) .inst_predicate(has_small_offset.clone()) .clobbers_flags(false) @@ -1534,7 +1491,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with 32-bit offset. let st_disp32 = recipes.add_template_recipe( - EncodingRecipeBuilder::new("stDisp32", f_store, 5) + EncodingRecipeBuilder::new("stDisp32", &formats.store, 5) .operands_in(vec![gpr, gpr]) .clobbers_flags(false) .compute_size("size_plus_maybe_sib_for_in_reg_1") @@ -1560,7 +1517,7 @@ pub(crate) fn define<'shared>( // Only ABCD allowed for stored value. This is for byte stores with no REX. recipes.add_template( Template::new( - EncodingRecipeBuilder::new("stDisp32_abcd", f_store, 5) + EncodingRecipeBuilder::new("stDisp32_abcd", &formats.store, 5) .operands_in(vec![abcd, gpr]) .clobbers_flags(false) .compute_size("size_plus_maybe_sib_for_in_reg_1") @@ -1587,7 +1544,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with 32-bit offset of FPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fstDisp32", f_store, 5) + EncodingRecipeBuilder::new("fstDisp32", &formats.store, 5) .operands_in(vec![fpr, gpr]) .clobbers_flags(false) .compute_size("size_plus_maybe_sib_for_in_reg_1") @@ -1615,11 +1572,11 @@ pub(crate) fn define<'shared>( // A predicate asking if the offset is zero. let has_no_offset = - InstructionPredicate::new_is_field_equal(&*f_store_complex, "offset", "0".into()); + InstructionPredicate::new_is_field_equal(&*formats.store_complex, "offset", "0".into()); // XX /r register-indirect store with index and no offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("stWithIndex", f_store_complex, 2) + EncodingRecipeBuilder::new("stWithIndex", &formats.store_complex, 2) .operands_in(vec![gpr, gpr, gpr]) .inst_predicate(has_no_offset.clone()) .clobbers_flags(false) @@ -1646,7 +1603,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with index and no offset. // Only ABCD allowed for stored value. This is for byte stores with no REX. recipes.add_template_recipe( - EncodingRecipeBuilder::new("stWithIndex_abcd", f_store_complex, 2) + EncodingRecipeBuilder::new("stWithIndex_abcd", &formats.store_complex, 2) .operands_in(vec![abcd, gpr, gpr]) .inst_predicate(has_no_offset.clone()) .clobbers_flags(false) @@ -1672,7 +1629,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with index and no offset of FPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fstWithIndex", f_store_complex, 2) + EncodingRecipeBuilder::new("fstWithIndex", &formats.store_complex, 2) .operands_in(vec![fpr, gpr, gpr]) .inst_predicate(has_no_offset.clone()) .clobbers_flags(false) @@ -1697,11 +1654,11 @@ pub(crate) fn define<'shared>( ); let has_small_offset = - InstructionPredicate::new_is_signed_int(&*f_store_complex, "offset", 8, 0); + InstructionPredicate::new_is_signed_int(&*formats.store_complex, "offset", 8, 0); // XX /r register-indirect store with index and 8-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("stWithIndexDisp8", f_store_complex, 3) + EncodingRecipeBuilder::new("stWithIndexDisp8", &formats.store_complex, 3) .operands_in(vec![gpr, gpr, gpr]) .inst_predicate(has_small_offset.clone()) .clobbers_flags(false) @@ -1722,7 +1679,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with index and 8-bit offset. // Only ABCD allowed for stored value. This is for byte stores with no REX. recipes.add_template_recipe( - EncodingRecipeBuilder::new("stWithIndexDisp8_abcd", f_store_complex, 3) + EncodingRecipeBuilder::new("stWithIndexDisp8_abcd", &formats.store_complex, 3) .operands_in(vec![abcd, gpr, gpr]) .inst_predicate(has_small_offset.clone()) .clobbers_flags(false) @@ -1742,7 +1699,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with index and 8-bit offset of FPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fstWithIndexDisp8", f_store_complex, 3) + EncodingRecipeBuilder::new("fstWithIndexDisp8", &formats.store_complex, 3) .operands_in(vec![fpr, gpr, gpr]) .inst_predicate(has_small_offset.clone()) .clobbers_flags(false) @@ -1761,11 +1718,11 @@ pub(crate) fn define<'shared>( ); let has_big_offset = - InstructionPredicate::new_is_signed_int(&*f_store_complex, "offset", 32, 0); + InstructionPredicate::new_is_signed_int(&*formats.store_complex, "offset", 32, 0); // XX /r register-indirect store with index and 32-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("stWithIndexDisp32", f_store_complex, 6) + EncodingRecipeBuilder::new("stWithIndexDisp32", &formats.store_complex, 6) .operands_in(vec![gpr, gpr, gpr]) .inst_predicate(has_big_offset.clone()) .clobbers_flags(false) @@ -1786,7 +1743,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with index and 32-bit offset. // Only ABCD allowed for stored value. This is for byte stores with no REX. recipes.add_template_recipe( - EncodingRecipeBuilder::new("stWithIndexDisp32_abcd", f_store_complex, 6) + EncodingRecipeBuilder::new("stWithIndexDisp32_abcd", &formats.store_complex, 6) .operands_in(vec![abcd, gpr, gpr]) .inst_predicate(has_big_offset.clone()) .clobbers_flags(false) @@ -1806,7 +1763,7 @@ pub(crate) fn define<'shared>( // XX /r register-indirect store with index and 32-bit offset of FPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fstWithIndexDisp32", f_store_complex, 6) + EncodingRecipeBuilder::new("fstWithIndexDisp32", &formats.store_complex, 6) .operands_in(vec![fpr, gpr, gpr]) .inst_predicate(has_big_offset.clone()) .clobbers_flags(false) @@ -1827,7 +1784,7 @@ pub(crate) fn define<'shared>( // Unary spill with SIB and 32-bit displacement. recipes.add_template_recipe( - EncodingRecipeBuilder::new("spillSib32", f_unary, 6) + EncodingRecipeBuilder::new("spillSib32", &formats.unary, 6) .operands_in(vec![gpr]) .operands_out(vec![stack_gpr32]) .clobbers_flags(false) @@ -1845,7 +1802,7 @@ pub(crate) fn define<'shared>( // Like spillSib32, but targeting an FPR rather than a GPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fspillSib32", f_unary, 6) + EncodingRecipeBuilder::new("fspillSib32", &formats.unary, 6) .operands_in(vec![fpr]) .operands_out(vec![stack_fpr32]) .clobbers_flags(false) @@ -1863,7 +1820,7 @@ pub(crate) fn define<'shared>( // Regspill using RSP-relative addressing. recipes.add_template_recipe( - EncodingRecipeBuilder::new("regspill32", f_reg_spill, 6) + EncodingRecipeBuilder::new("regspill32", &formats.reg_spill, 6) .operands_in(vec![gpr]) .clobbers_flags(false) .emit( @@ -1881,7 +1838,7 @@ pub(crate) fn define<'shared>( // Like regspill32, but targeting an FPR rather than a GPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fregspill32", f_reg_spill, 6) + EncodingRecipeBuilder::new("fregspill32", &formats.reg_spill, 6) .operands_in(vec![fpr]) .clobbers_flags(false) .emit( @@ -1904,11 +1861,11 @@ pub(crate) fn define<'shared>( // A predicate asking if the offset is zero. let has_no_offset = - InstructionPredicate::new_is_field_equal(&*f_load, "offset", "0".into()); + InstructionPredicate::new_is_field_equal(&*formats.load, "offset", "0".into()); // XX /r load with no offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("ld", f_load, 1) + EncodingRecipeBuilder::new("ld", &formats.load, 1) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .inst_predicate(has_no_offset.clone()) @@ -1935,7 +1892,7 @@ pub(crate) fn define<'shared>( // XX /r float load with no offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fld", f_load, 1) + EncodingRecipeBuilder::new("fld", &formats.load, 1) .operands_in(vec![gpr]) .operands_out(vec![fpr]) .inst_predicate(has_no_offset.clone()) @@ -1960,11 +1917,12 @@ pub(crate) fn define<'shared>( ), ); - let has_small_offset = InstructionPredicate::new_is_signed_int(&*f_load, "offset", 8, 0); + let has_small_offset = + InstructionPredicate::new_is_signed_int(&*formats.load, "offset", 8, 0); // XX /r load with 8-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("ldDisp8", f_load, 2) + EncodingRecipeBuilder::new("ldDisp8", &formats.load, 2) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .inst_predicate(has_small_offset.clone()) @@ -1990,7 +1948,7 @@ pub(crate) fn define<'shared>( // XX /r float load with 8-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fldDisp8", f_load, 2) + EncodingRecipeBuilder::new("fldDisp8", &formats.load, 2) .operands_in(vec![gpr]) .operands_out(vec![fpr]) .inst_predicate(has_small_offset.clone()) @@ -2014,11 +1972,12 @@ pub(crate) fn define<'shared>( ), ); - let has_big_offset = InstructionPredicate::new_is_signed_int(&*f_load, "offset", 32, 0); + let has_big_offset = + InstructionPredicate::new_is_signed_int(&*formats.load, "offset", 32, 0); // XX /r load with 32-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("ldDisp32", f_load, 5) + EncodingRecipeBuilder::new("ldDisp32", &formats.load, 5) .operands_in(vec![gpr]) .operands_out(vec![gpr]) .inst_predicate(has_big_offset.clone()) @@ -2044,7 +2003,7 @@ pub(crate) fn define<'shared>( // XX /r float load with 32-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fldDisp32", f_load, 5) + EncodingRecipeBuilder::new("fldDisp32", &formats.load, 5) .operands_in(vec![gpr]) .operands_out(vec![fpr]) .inst_predicate(has_big_offset.clone()) @@ -2074,11 +2033,11 @@ pub(crate) fn define<'shared>( // A predicate asking if the offset is zero. let has_no_offset = - InstructionPredicate::new_is_field_equal(&*f_load_complex, "offset", "0".into()); + InstructionPredicate::new_is_field_equal(&*formats.load_complex, "offset", "0".into()); // XX /r load with index and no offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("ldWithIndex", f_load_complex, 2) + EncodingRecipeBuilder::new("ldWithIndex", &formats.load_complex, 2) .operands_in(vec![gpr, gpr]) .operands_out(vec![gpr]) .inst_predicate(has_no_offset.clone()) @@ -2105,7 +2064,7 @@ pub(crate) fn define<'shared>( // XX /r float load with index and no offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fldWithIndex", f_load_complex, 2) + EncodingRecipeBuilder::new("fldWithIndex", &formats.load_complex, 2) .operands_in(vec![gpr, gpr]) .operands_out(vec![fpr]) .inst_predicate(has_no_offset.clone()) @@ -2131,11 +2090,11 @@ pub(crate) fn define<'shared>( ); let has_small_offset = - InstructionPredicate::new_is_signed_int(&*f_load_complex, "offset", 8, 0); + InstructionPredicate::new_is_signed_int(&*formats.load_complex, "offset", 8, 0); // XX /r load with index and 8-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("ldWithIndexDisp8", f_load_complex, 3) + EncodingRecipeBuilder::new("ldWithIndexDisp8", &formats.load_complex, 3) .operands_in(vec![gpr, gpr]) .operands_out(vec![gpr]) .inst_predicate(has_small_offset.clone()) @@ -2156,7 +2115,7 @@ pub(crate) fn define<'shared>( // XX /r float load with 8-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fldWithIndexDisp8", f_load_complex, 3) + EncodingRecipeBuilder::new("fldWithIndexDisp8", &formats.load_complex, 3) .operands_in(vec![gpr, gpr]) .operands_out(vec![fpr]) .inst_predicate(has_small_offset.clone()) @@ -2176,11 +2135,11 @@ pub(crate) fn define<'shared>( ); let has_big_offset = - InstructionPredicate::new_is_signed_int(&*f_load_complex, "offset", 32, 0); + InstructionPredicate::new_is_signed_int(&*formats.load_complex, "offset", 32, 0); // XX /r load with index and 32-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("ldWithIndexDisp32", f_load_complex, 6) + EncodingRecipeBuilder::new("ldWithIndexDisp32", &formats.load_complex, 6) .operands_in(vec![gpr, gpr]) .operands_out(vec![gpr]) .inst_predicate(has_big_offset.clone()) @@ -2201,7 +2160,7 @@ pub(crate) fn define<'shared>( // XX /r float load with index and 32-bit offset. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fldWithIndexDisp32", f_load_complex, 6) + EncodingRecipeBuilder::new("fldWithIndexDisp32", &formats.load_complex, 6) .operands_in(vec![gpr, gpr]) .operands_out(vec![fpr]) .inst_predicate(has_big_offset.clone()) @@ -2223,7 +2182,7 @@ pub(crate) fn define<'shared>( // Unary fill with SIB and 32-bit displacement. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fillSib32", f_unary, 6) + EncodingRecipeBuilder::new("fillSib32", &formats.unary, 6) .operands_in(vec![stack_gpr32]) .operands_out(vec![gpr]) .clobbers_flags(false) @@ -2240,7 +2199,7 @@ pub(crate) fn define<'shared>( // Like fillSib32, but targeting an FPR rather than a GPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("ffillSib32", f_unary, 6) + EncodingRecipeBuilder::new("ffillSib32", &formats.unary, 6) .operands_in(vec![stack_fpr32]) .operands_out(vec![fpr]) .clobbers_flags(false) @@ -2257,7 +2216,7 @@ pub(crate) fn define<'shared>( // Regfill with RSP-relative 32-bit displacement. recipes.add_template_recipe( - EncodingRecipeBuilder::new("regfill32", f_reg_fill, 6) + EncodingRecipeBuilder::new("regfill32", &formats.reg_fill, 6) .operands_in(vec![stack_gpr32]) .clobbers_flags(false) .emit( @@ -2274,7 +2233,7 @@ pub(crate) fn define<'shared>( // Like regfill32, but targeting an FPR rather than a GPR. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fregfill32", f_reg_fill, 6) + EncodingRecipeBuilder::new("fregfill32", &formats.reg_fill, 6) .operands_in(vec![stack_fpr32]) .clobbers_flags(false) .emit( @@ -2291,8 +2250,9 @@ pub(crate) fn define<'shared>( // Call/return. - recipes.add_template_recipe(EncodingRecipeBuilder::new("call_id", f_call, 4).emit( - r#" + recipes.add_template_recipe( + EncodingRecipeBuilder::new("call_id", &formats.call, 4).emit( + r#" sink.trap(TrapCode::StackOverflow, func.srclocs[inst]); {{PUT_OP}}(bits, BASE_REX, sink); // The addend adjusts for the difference between the end of the @@ -2302,10 +2262,12 @@ pub(crate) fn define<'shared>( -4); sink.put4(0); "#, - )); + ), + ); - recipes.add_template_recipe(EncodingRecipeBuilder::new("call_plt_id", f_call, 4).emit( - r#" + recipes.add_template_recipe( + EncodingRecipeBuilder::new("call_plt_id", &formats.call, 4).emit( + r#" sink.trap(TrapCode::StackOverflow, func.srclocs[inst]); {{PUT_OP}}(bits, BASE_REX, sink); sink.reloc_external(Reloc::X86CallPLTRel4, @@ -2313,10 +2275,11 @@ pub(crate) fn define<'shared>( -4); sink.put4(0); "#, - )); + ), + ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("call_r", f_call_indirect, 1) + EncodingRecipeBuilder::new("call_r", &formats.call_indirect, 1) .operands_in(vec![gpr]) .emit( r#" @@ -2328,13 +2291,14 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("ret", f_multiary, 0).emit("{{PUT_OP}}(bits, BASE_REX, sink);"), + EncodingRecipeBuilder::new("ret", &formats.multiary, 0) + .emit("{{PUT_OP}}(bits, BASE_REX, sink);"), ); // Branches. recipes.add_template_recipe( - EncodingRecipeBuilder::new("jmpb", f_jump, 1) + EncodingRecipeBuilder::new("jmpb", &formats.jump, 1) .branch_range((1, 8)) .clobbers_flags(false) .emit( @@ -2346,7 +2310,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("jmpd", f_jump, 4) + EncodingRecipeBuilder::new("jmpd", &formats.jump, 4) .branch_range((4, 32)) .clobbers_flags(false) .emit( @@ -2358,7 +2322,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("brib", f_branch_int, 1) + EncodingRecipeBuilder::new("brib", &formats.branch_int, 1) .operands_in(vec![reg_rflags]) .branch_range((1, 8)) .clobbers_flags(false) @@ -2371,7 +2335,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("brid", f_branch_int, 4) + EncodingRecipeBuilder::new("brid", &formats.branch_int, 4) .operands_in(vec![reg_rflags]) .branch_range((4, 32)) .clobbers_flags(false) @@ -2384,13 +2348,13 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("brfb", f_branch_float, 1) + EncodingRecipeBuilder::new("brfb", &formats.branch_float, 1) .operands_in(vec![reg_rflags]) .branch_range((1, 8)) .clobbers_flags(false) .inst_predicate(supported_floatccs_predicate( &supported_floatccs, - &*f_branch_float, + &*formats.branch_float, )) .emit( r#" @@ -2401,13 +2365,13 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("brfd", f_branch_float, 4) + EncodingRecipeBuilder::new("brfd", &formats.branch_float, 4) .operands_in(vec![reg_rflags]) .branch_range((4, 32)) .clobbers_flags(false) .inst_predicate(supported_floatccs_predicate( &supported_floatccs, - &*f_branch_float, + &*formats.branch_float, )) .emit( r#" @@ -2418,7 +2382,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("indirect_jmp", f_indirect_jump, 1) + EncodingRecipeBuilder::new("indirect_jmp", &formats.indirect_jump, 1) .operands_in(vec![gpr]) .clobbers_flags(false) .emit( @@ -2430,11 +2394,11 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("jt_entry", f_branch_table_entry, 2) + EncodingRecipeBuilder::new("jt_entry", &formats.branch_table_entry, 2) .operands_in(vec![gpr, gpr]) .operands_out(vec![gpr]) .clobbers_flags(false) - .inst_predicate(valid_scale(&*f_branch_table_entry)) + .inst_predicate(valid_scale(&*formats.branch_table_entry)) .compute_size("size_plus_maybe_offset_for_in_reg_1") .emit( r#" @@ -2452,7 +2416,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("vconst", f_unary_const, 5) + EncodingRecipeBuilder::new("vconst", &formats.unary_const, 5) .operands_out(vec![fpr]) .clobbers_flags(false) .emit( @@ -2465,7 +2429,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("vconst_optimized", f_unary_const, 1) + EncodingRecipeBuilder::new("vconst_optimized", &formats.unary_const, 1) .operands_out(vec![fpr]) .clobbers_flags(false) .emit( @@ -2477,7 +2441,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("jt_base", f_branch_table_base, 5) + EncodingRecipeBuilder::new("jt_base", &formats.branch_table_base, 5) .operands_out(vec![gpr]) .clobbers_flags(false) .emit( @@ -2502,7 +2466,7 @@ pub(crate) fn define<'shared>( let seti = recipes.add_template( Template::new( - EncodingRecipeBuilder::new("seti", f_int_cond, 1) + EncodingRecipeBuilder::new("seti", &formats.int_cond, 1) .operands_in(vec![reg_rflags]) .operands_out(vec![gpr]) .clobbers_flags(false) @@ -2519,7 +2483,7 @@ pub(crate) fn define<'shared>( recipes.add_template( Template::new( - EncodingRecipeBuilder::new("seti_abcd", f_int_cond, 1) + EncodingRecipeBuilder::new("seti_abcd", &formats.int_cond, 1) .operands_in(vec![reg_rflags]) .operands_out(vec![abcd]) .clobbers_flags(false) @@ -2536,7 +2500,7 @@ pub(crate) fn define<'shared>( let setf = recipes.add_template( Template::new( - EncodingRecipeBuilder::new("setf", f_float_cond, 1) + EncodingRecipeBuilder::new("setf", &formats.float_cond, 1) .operands_in(vec![reg_rflags]) .operands_out(vec![gpr]) .clobbers_flags(false) @@ -2553,7 +2517,7 @@ pub(crate) fn define<'shared>( recipes.add_template( Template::new( - EncodingRecipeBuilder::new("setf_abcd", f_float_cond, 1) + EncodingRecipeBuilder::new("setf_abcd", &formats.float_cond, 1) .operands_in(vec![reg_rflags]) .operands_out(vec![abcd]) .clobbers_flags(false) @@ -2572,7 +2536,7 @@ pub(crate) fn define<'shared>( // (maybe-REX.W) 0F 4x modrm(r,r) // 1 byte, modrm(r,r), is after the opcode recipes.add_template_recipe( - EncodingRecipeBuilder::new("cmov", f_int_select, 1) + EncodingRecipeBuilder::new("cmov", &formats.int_select, 1) .operands_in(vec![ OperandConstraint::FixedReg(reg_rflags), OperandConstraint::RegClass(gpr), @@ -2590,7 +2554,7 @@ pub(crate) fn define<'shared>( // Bit scan forwards and reverse recipes.add_template_recipe( - EncodingRecipeBuilder::new("bsf_and_bsr", f_unary, 1) + EncodingRecipeBuilder::new("bsf_and_bsr", &formats.unary, 1) .operands_in(vec![gpr]) .operands_out(vec![ OperandConstraint::RegClass(gpr), @@ -2608,7 +2572,7 @@ pub(crate) fn define<'shared>( // XX /r, MR form. Add two GPR registers and set carry flag. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rout", f_binary, 1) + EncodingRecipeBuilder::new("rout", &formats.binary, 1) .operands_in(vec![gpr, gpr]) .operands_out(vec![ OperandConstraint::TiedInput(0), @@ -2625,7 +2589,7 @@ pub(crate) fn define<'shared>( // XX /r, MR form. Add two GPR registers and get carry flag. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rin", f_ternary, 1) + EncodingRecipeBuilder::new("rin", &formats.ternary, 1) .operands_in(vec![ OperandConstraint::RegClass(gpr), OperandConstraint::RegClass(gpr), @@ -2643,7 +2607,7 @@ pub(crate) fn define<'shared>( // XX /r, MR form. Add two GPR registers with carry flag. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rio", f_ternary, 1) + EncodingRecipeBuilder::new("rio", &formats.ternary, 1) .operands_in(vec![ OperandConstraint::RegClass(gpr), OperandConstraint::RegClass(gpr), @@ -2666,7 +2630,7 @@ pub(crate) fn define<'shared>( // XX /r, MR form. Compare two GPR registers and set flags. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rcmp", f_binary, 1) + EncodingRecipeBuilder::new("rcmp", &formats.binary, 1) .operands_in(vec![gpr, gpr]) .operands_out(vec![reg_rflags]) .emit( @@ -2679,7 +2643,7 @@ pub(crate) fn define<'shared>( // Same as rcmp, but second operand is the stack pointer. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rcmp_sp", f_unary, 1) + EncodingRecipeBuilder::new("rcmp_sp", &formats.unary, 1) .operands_in(vec![gpr]) .operands_out(vec![reg_rflags]) .emit( @@ -2692,7 +2656,7 @@ pub(crate) fn define<'shared>( // XX /r, RM form. Compare two FPR registers and set flags. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fcmp", f_binary, 1) + EncodingRecipeBuilder::new("fcmp", &formats.binary, 1) .operands_in(vec![fpr, fpr]) .operands_out(vec![reg_rflags]) .emit( @@ -2704,11 +2668,12 @@ pub(crate) fn define<'shared>( ); { - let has_small_offset = InstructionPredicate::new_is_signed_int(&*f_binary_imm, "imm", 8, 0); + let has_small_offset = + InstructionPredicate::new_is_signed_int(&*formats.binary_imm, "imm", 8, 0); // XX /n, MI form with imm8. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rcmp_ib", f_binary_imm, 2) + EncodingRecipeBuilder::new("rcmp_ib", &formats.binary_imm, 2) .operands_in(vec![gpr]) .operands_out(vec![reg_rflags]) .inst_predicate(has_small_offset) @@ -2722,11 +2687,12 @@ pub(crate) fn define<'shared>( ), ); - let has_big_offset = InstructionPredicate::new_is_signed_int(&*f_binary_imm, "imm", 32, 0); + let has_big_offset = + InstructionPredicate::new_is_signed_int(&*formats.binary_imm, "imm", 32, 0); // XX /n, MI form with imm32. recipes.add_template_recipe( - EncodingRecipeBuilder::new("rcmp_id", f_binary_imm, 5) + EncodingRecipeBuilder::new("rcmp_id", &formats.binary_imm, 5) .operands_in(vec![gpr]) .operands_out(vec![reg_rflags]) .inst_predicate(has_big_offset) @@ -2756,7 +2722,7 @@ pub(crate) fn define<'shared>( // Bits 8-15 control the test instruction which always has opcode byte 0x85. recipes.add_template_recipe( - EncodingRecipeBuilder::new("tjccb", f_branch, 1 + 2) + EncodingRecipeBuilder::new("tjccb", &formats.branch, 1 + 2) .operands_in(vec![gpr]) .branch_range((3, 8)) .emit( @@ -2772,7 +2738,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("tjccd", f_branch, 1 + 6) + EncodingRecipeBuilder::new("tjccd", &formats.branch, 1 + 6) .operands_in(vec![gpr]) .branch_range((7, 32)) .emit( @@ -2792,7 +2758,7 @@ pub(crate) fn define<'shared>( let t8jccb = recipes.add_template( Template::new( - EncodingRecipeBuilder::new("t8jccb", f_branch, 1 + 2) + EncodingRecipeBuilder::new("t8jccb", &formats.branch, 1 + 2) .operands_in(vec![gpr]) .branch_range((3, 8)) .emit( @@ -2812,7 +2778,7 @@ pub(crate) fn define<'shared>( recipes.add_template( Template::new( - EncodingRecipeBuilder::new("t8jccb_abcd", f_branch, 1 + 2) + EncodingRecipeBuilder::new("t8jccb_abcd", &formats.branch, 1 + 2) .operands_in(vec![abcd]) .branch_range((3, 8)) .emit( @@ -2832,7 +2798,7 @@ pub(crate) fn define<'shared>( let t8jccd = recipes.add_template( Template::new( - EncodingRecipeBuilder::new("t8jccd", f_branch, 1 + 6) + EncodingRecipeBuilder::new("t8jccd", &formats.branch, 1 + 6) .operands_in(vec![gpr]) .branch_range((7, 32)) .emit( @@ -2853,7 +2819,7 @@ pub(crate) fn define<'shared>( recipes.add_template( Template::new( - EncodingRecipeBuilder::new("t8jccd_abcd", f_branch, 1 + 6) + EncodingRecipeBuilder::new("t8jccd_abcd", &formats.branch, 1 + 6) .operands_in(vec![abcd]) .branch_range((7, 32)) .emit( @@ -2879,7 +2845,7 @@ pub(crate) fn define<'shared>( // a 0xff immediate. recipes.add_template_recipe( - EncodingRecipeBuilder::new("t8jccd_long", f_branch, 5 + 6) + EncodingRecipeBuilder::new("t8jccd_long", &formats.branch, 5 + 6) .operands_in(vec![gpr]) .branch_range((11, 32)) .emit( @@ -2916,7 +2882,7 @@ pub(crate) fn define<'shared>( // The omission of a `when_prefixed` alternative is deliberate here. recipes.add_template_recipe( - EncodingRecipeBuilder::new("icscc", f_int_compare, 1 + 3) + EncodingRecipeBuilder::new("icscc", &formats.int_compare, 1 + 3) .operands_in(vec![gpr, gpr]) .operands_out(vec![abcd]) .emit( @@ -2934,7 +2900,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("icscc_fpr", f_int_compare, 1) + EncodingRecipeBuilder::new("icscc_fpr", &formats.int_compare, 1) .operands_in(vec![fpr, fpr]) .operands_out(vec![0]) .emit( @@ -2948,10 +2914,10 @@ pub(crate) fn define<'shared>( { let is_small_imm = - InstructionPredicate::new_is_signed_int(&*f_int_compare_imm, "imm", 8, 0); + InstructionPredicate::new_is_signed_int(&*formats.int_compare_imm, "imm", 8, 0); recipes.add_template_recipe( - EncodingRecipeBuilder::new("icscc_ib", f_int_compare_imm, 2 + 3) + EncodingRecipeBuilder::new("icscc_ib", &formats.int_compare_imm, 2 + 3) .operands_in(vec![gpr]) .operands_out(vec![abcd]) .inst_predicate(is_small_imm) @@ -2971,10 +2937,11 @@ pub(crate) fn define<'shared>( ), ); - let is_big_imm = InstructionPredicate::new_is_signed_int(&*f_int_compare_imm, "imm", 32, 0); + let is_big_imm = + InstructionPredicate::new_is_signed_int(&*formats.int_compare_imm, "imm", 32, 0); recipes.add_template_recipe( - EncodingRecipeBuilder::new("icscc_id", f_int_compare_imm, 5 + 3) + EncodingRecipeBuilder::new("icscc_id", &formats.int_compare_imm, 5 + 3) .operands_in(vec![gpr]) .operands_out(vec![abcd]) .inst_predicate(is_big_imm) @@ -3011,12 +2978,12 @@ pub(crate) fn define<'shared>( // The omission of a `when_prefixed` alternative is deliberate here. recipes.add_template_recipe( - EncodingRecipeBuilder::new("fcscc", f_float_compare, 1 + 3) + EncodingRecipeBuilder::new("fcscc", &formats.float_compare, 1 + 3) .operands_in(vec![fpr, fpr]) .operands_out(vec![abcd]) .inst_predicate(supported_floatccs_predicate( &supported_floatccs, - &*f_float_compare, + &*formats.float_compare, )) .emit( r#" @@ -3050,7 +3017,7 @@ pub(crate) fn define<'shared>( ); recipes.add_template_recipe( - EncodingRecipeBuilder::new("is_zero", f_unary, 2 + 2) + EncodingRecipeBuilder::new("is_zero", &formats.unary, 2 + 2) .operands_in(vec![gpr]) .operands_out(vec![abcd]) .emit( @@ -3066,11 +3033,13 @@ pub(crate) fn define<'shared>( ), ); - recipes.add_recipe(EncodingRecipeBuilder::new("safepoint", f_multiary, 0).emit( - r#" + recipes.add_recipe( + EncodingRecipeBuilder::new("safepoint", &formats.multiary, 0).emit( + r#" sink.add_stackmap(args, func, isa); "#, - )); + ), + ); recipes } diff --git a/cranelift/codegen/meta/src/lib.rs b/cranelift/codegen/meta/src/lib.rs index 1513b09e18..00fae4b455 100644 --- a/cranelift/codegen/meta/src/lib.rs +++ b/cranelift/codegen/meta/src/lib.rs @@ -23,7 +23,8 @@ pub fn isa_from_arch(arch: &str) -> Result { /// Generates all the Rust source files used in Cranelift from the meta-language. pub fn generate(isas: &Vec, out_dir: &str) -> Result<(), error::Error> { - // Common definitions. + // Create all the definitions: + // - common definitions. let mut shared_defs = shared::define(); gen_settings::generate( @@ -34,10 +35,20 @@ pub fn generate(isas: &Vec, out_dir: &str) -> Result<(), error::Error> )?; gen_types::generate("types.rs", &out_dir)?; - // Per ISA definitions. + // - per ISA definitions. 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)?; diff --git a/cranelift/codegen/meta/src/shared/formats.rs b/cranelift/codegen/meta/src/shared/formats.rs index 73aaa42634..3cc3d343d7 100644 --- a/cranelift/codegen/meta/src/shared/formats.rs +++ b/cranelift/codegen/meta/src/shared/formats.rs @@ -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 std::rc::Rc; -pub(crate) fn define(imm: &Immediates, entities: &EntityRefs) -> FormatRegistry { - let mut registry = FormatRegistry::new(); - - registry.insert(Builder::new("Unary").value()); - registry.insert(Builder::new("UnaryImm").imm(&imm.imm64)); - registry.insert(Builder::new("UnaryIeee32").imm(&imm.ieee32)); - registry.insert(Builder::new("UnaryIeee64").imm(&imm.ieee64)); - 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()); - registry.insert(Builder::new("BinaryImm").value().imm(&imm.imm64)); - - // The select instructions are controlled by the second VALUE operand. - // The first VALUE operand is the controlling flag which has a derived type. - // The fma instruction has the same constraint on all inputs. - registry.insert( - Builder::new("Ternary") - .value() - .value() - .value() - .typevar_operand(1), - ); - - // Catch-all for instructions with many outputs and inputs and no immediate - // operands. - registry.insert(Builder::new("MultiAry").varargs()); - - registry.insert(Builder::new("NullAry")); - - registry.insert( - Builder::new("InsertLane") - .value() - .imm_with_name("lane", &imm.uimm8) - .value(), - ); - registry.insert( - Builder::new("ExtractLane") - .value() - .imm_with_name("lane", &imm.uimm8), - ); - registry.insert( - Builder::new("Shuffle") - .value() - .value() - .imm_with_name("mask", &imm.uimm128), - ); - - registry.insert(Builder::new("IntCompare").imm(&imm.intcc).value().value()); - registry.insert( - Builder::new("IntCompareImm") - .imm(&imm.intcc) - .value() - .imm(&imm.imm64), - ); - registry.insert(Builder::new("IntCond").imm(&imm.intcc).value()); - - registry.insert( - Builder::new("FloatCompare") - .imm(&imm.floatcc) - .value() - .value(), - ); - registry.insert(Builder::new("FloatCond").imm(&imm.floatcc).value()); - - registry.insert( - Builder::new("IntSelect") - .imm(&imm.intcc) - .value() - .value() - .value(), - ); - - registry.insert(Builder::new("Jump").imm(&entities.ebb).varargs()); - registry.insert(Builder::new("Branch").value().imm(&entities.ebb).varargs()); - registry.insert( - Builder::new("BranchInt") - .imm(&imm.intcc) - .value() - .imm(&entities.ebb) - .varargs(), - ); - registry.insert( - Builder::new("BranchFloat") - .imm(&imm.floatcc) - .value() - .imm(&entities.ebb) - .varargs(), - ); - registry.insert( - Builder::new("BranchIcmp") - .imm(&imm.intcc) - .value() - .value() - .imm(&entities.ebb) - .varargs(), - ); - registry.insert( - Builder::new("BranchTable") - .value() - .imm(&entities.ebb) - .imm(&entities.jump_table), - ); - registry.insert( - Builder::new("BranchTableEntry") - .value() - .value() - .imm(&imm.uimm8) - .imm(&entities.jump_table), - ); - 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()); - registry.insert( - Builder::new("CallIndirect") - .imm(&entities.sig_ref) - .value() - .varargs(), - ); - registry.insert(Builder::new("FuncAddr").imm(&entities.func_ref)); - - registry.insert( - Builder::new("Load") - .imm(&imm.memflags) - .value() - .imm(&imm.offset32), - ); - registry.insert( - Builder::new("LoadComplex") - .imm(&imm.memflags) - .varargs() - .imm(&imm.offset32), - ); - registry.insert( - Builder::new("Store") - .imm(&imm.memflags) - .value() - .value() - .imm(&imm.offset32), - ); - registry.insert( - Builder::new("StoreComplex") - .imm(&imm.memflags) - .value() - .varargs() - .imm(&imm.offset32), - ); - registry.insert( - Builder::new("StackLoad") - .imm(&entities.stack_slot) - .imm(&imm.offset32), - ); - registry.insert( - Builder::new("StackStore") - .value() - .imm(&entities.stack_slot) - .imm(&imm.offset32), - ); - - // Accessing a WebAssembly heap. - registry.insert( - Builder::new("HeapAddr") - .imm(&entities.heap) - .value() - .imm(&imm.uimm32), - ); - - // Accessing a WebAssembly table. - registry.insert( - Builder::new("TableAddr") - .imm(&entities.table) - .value() - .imm(&imm.offset32), - ); - - registry.insert( - Builder::new("RegMove") - .value() - .imm_with_name("src", &imm.regunit) - .imm_with_name("dst", &imm.regunit), - ); - registry.insert( - Builder::new("CopySpecial") - .imm_with_name("src", &imm.regunit) - .imm_with_name("dst", &imm.regunit), - ); - registry.insert(Builder::new("CopyToSsa").imm_with_name("src", &imm.regunit)); - registry.insert( - Builder::new("RegSpill") - .value() - .imm_with_name("src", &imm.regunit) - .imm_with_name("dst", &entities.stack_slot), - ); - registry.insert( - Builder::new("RegFill") - .value() - .imm_with_name("src", &entities.stack_slot) - .imm_with_name("dst", &imm.regunit), - ); - - registry.insert(Builder::new("Trap").imm(&imm.trapcode)); - registry.insert(Builder::new("CondTrap").value().imm(&imm.trapcode)); - registry.insert( - Builder::new("IntCondTrap") - .imm(&imm.intcc) - .value() - .imm(&imm.trapcode), - ); - registry.insert( - Builder::new("FloatCondTrap") - .imm(&imm.floatcc) - .value() - .imm(&imm.trapcode), - ); - - registry +pub(crate) struct Formats { + pub(crate) binary: Rc, + pub(crate) binary_imm: Rc, + pub(crate) branch: Rc, + pub(crate) branch_float: Rc, + pub(crate) branch_icmp: Rc, + pub(crate) branch_int: Rc, + pub(crate) branch_table: Rc, + pub(crate) branch_table_base: Rc, + pub(crate) branch_table_entry: Rc, + pub(crate) call: Rc, + pub(crate) call_indirect: Rc, + pub(crate) cond_trap: Rc, + pub(crate) copy_special: Rc, + pub(crate) copy_to_ssa: Rc, + pub(crate) extract_lane: Rc, + pub(crate) float_compare: Rc, + pub(crate) float_cond: Rc, + pub(crate) float_cond_trap: Rc, + pub(crate) func_addr: Rc, + pub(crate) heap_addr: Rc, + pub(crate) indirect_jump: Rc, + pub(crate) insert_lane: Rc, + pub(crate) int_compare: Rc, + pub(crate) int_compare_imm: Rc, + pub(crate) int_cond: Rc, + pub(crate) int_cond_trap: Rc, + pub(crate) int_select: Rc, + pub(crate) jump: Rc, + pub(crate) load: Rc, + pub(crate) load_complex: Rc, + pub(crate) multiary: Rc, + pub(crate) nullary: Rc, + pub(crate) reg_fill: Rc, + pub(crate) reg_move: Rc, + pub(crate) reg_spill: Rc, + pub(crate) shuffle: Rc, + pub(crate) stack_load: Rc, + pub(crate) stack_store: Rc, + pub(crate) store: Rc, + pub(crate) store_complex: Rc, + pub(crate) table_addr: Rc, + pub(crate) ternary: Rc, + pub(crate) trap: Rc, + pub(crate) unary: Rc, + pub(crate) unary_bool: Rc, + pub(crate) unary_const: Rc, + pub(crate) unary_global_value: Rc, + pub(crate) unary_ieee32: Rc, + pub(crate) unary_ieee64: Rc, + pub(crate) unary_imm: Rc, +} + +impl Formats { + pub fn new(imm: &Immediates, entities: &EntityRefs) -> Self { + Self { + unary: Builder::new("Unary").value().build(), + + unary_imm: Builder::new("UnaryImm").imm(&imm.imm64).build(), + + 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 first VALUE operand is the controlling flag which has a derived type. + // The fma instruction has the same constraint on all inputs. + ternary: Builder::new("Ternary") + .value() + .value() + .value() + .typevar_operand(1) + .build(), + + // Catch-all for instructions with many outputs and inputs and no immediate + // operands. + multiary: Builder::new("MultiAry").varargs().build(), + + nullary: Builder::new("NullAry").build(), + + insert_lane: Builder::new("InsertLane") + .value() + .imm_with_name("lane", &imm.uimm8) + .value() + .build(), + + extract_lane: Builder::new("ExtractLane") + .value() + .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) + .value() + .value() + .build(), + + int_compare_imm: Builder::new("IntCompareImm") + .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) + .value() + .value() + .build(), + + float_cond: Builder::new("FloatCond").imm(&imm.floatcc).value().build(), + + int_select: Builder::new("IntSelect") + .imm(&imm.intcc) + .value() + .value() + .value() + .build(), + + jump: Builder::new("Jump").imm(&entities.ebb).varargs().build(), + + branch: Builder::new("Branch") + .value() + .imm(&entities.ebb) + .varargs() + .build(), + + branch_int: Builder::new("BranchInt") + .imm(&imm.intcc) + .value() + .imm(&entities.ebb) + .varargs() + .build(), + + branch_float: Builder::new("BranchFloat") + .imm(&imm.floatcc) + .value() + .imm(&entities.ebb) + .varargs() + .build(), + + branch_icmp: Builder::new("BranchIcmp") + .imm(&imm.intcc) + .value() + .value() + .imm(&entities.ebb) + .varargs() + .build(), + + branch_table: Builder::new("BranchTable") + .value() + .imm(&entities.ebb) + .imm(&entities.jump_table) + .build(), + + branch_table_entry: Builder::new("BranchTableEntry") + .value() + .value() + .imm(&imm.uimm8) + .imm(&entities.jump_table) + .build(), + + branch_table_base: Builder::new("BranchTableBase") + .imm(&entities.jump_table) + .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) + .value() + .varargs() + .build(), + + func_addr: Builder::new("FuncAddr").imm(&entities.func_ref).build(), + + load: Builder::new("Load") + .imm(&imm.memflags) + .value() + .imm(&imm.offset32) + .build(), + + load_complex: Builder::new("LoadComplex") + .imm(&imm.memflags) + .varargs() + .imm(&imm.offset32) + .build(), + + store: Builder::new("Store") + .imm(&imm.memflags) + .value() + .value() + .imm(&imm.offset32) + .build(), + + store_complex: Builder::new("StoreComplex") + .imm(&imm.memflags) + .value() + .varargs() + .imm(&imm.offset32) + .build(), + + stack_load: Builder::new("StackLoad") + .imm(&entities.stack_slot) + .imm(&imm.offset32) + .build(), + + stack_store: Builder::new("StackStore") + .value() + .imm(&entities.stack_slot) + .imm(&imm.offset32) + .build(), + + // Accessing a WebAssembly heap. + heap_addr: Builder::new("HeapAddr") + .imm(&entities.heap) + .value() + .imm(&imm.uimm32) + .build(), + + // Accessing a WebAssembly table. + table_addr: Builder::new("TableAddr") + .imm(&entities.table) + .value() + .imm(&imm.offset32) + .build(), + + reg_move: Builder::new("RegMove") + .value() + .imm_with_name("src", &imm.regunit) + .imm_with_name("dst", &imm.regunit) + .build(), + + copy_special: Builder::new("CopySpecial") + .imm_with_name("src", &imm.regunit) + .imm_with_name("dst", &imm.regunit) + .build(), + + copy_to_ssa: Builder::new("CopyToSsa") + .imm_with_name("src", &imm.regunit) + .build(), + + reg_spill: Builder::new("RegSpill") + .value() + .imm_with_name("src", &imm.regunit) + .imm_with_name("dst", &entities.stack_slot) + .build(), + + reg_fill: Builder::new("RegFill") + .value() + .imm_with_name("src", &entities.stack_slot) + .imm_with_name("dst", &imm.regunit) + .build(), + + trap: Builder::new("Trap").imm(&imm.trapcode).build(), + + cond_trap: Builder::new("CondTrap").value().imm(&imm.trapcode).build(), + + int_cond_trap: Builder::new("IntCondTrap") + .imm(&imm.intcc) + .value() + .imm(&imm.trapcode) + .build(), + + float_cond_trap: Builder::new("FloatCondTrap") + .imm(&imm.floatcc) + .value() + .imm(&imm.trapcode) + .build(), + } + } } diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs index f6d181def2..1980730d95 100644 --- a/cranelift/codegen/meta/src/shared/instructions.rs +++ b/cranelift/codegen/meta/src/shared/instructions.rs @@ -1,6 +1,5 @@ #![allow(non_snake_case)] -use crate::cdsl::formats::FormatRegistry; use crate::cdsl::instructions::{ AllInstructions, InstructionBuilder as Inst, InstructionGroup, InstructionGroupBuilder, }; @@ -8,16 +7,17 @@ use crate::cdsl::operands::{create_operand as operand, create_operand_doc as ope use crate::cdsl::type_inference::Constraint::WiderOrEq; use crate::cdsl::types::{LaneType, ValueType}; use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar}; +use crate::shared::formats::Formats; use crate::shared::types; use crate::shared::{entities::EntityRefs, immediates::Immediates}; pub(crate) fn define( all_instructions: &mut AllInstructions, - format_registry: &FormatRegistry, + formats: &Formats, imm: &Immediates, entities: &EntityRefs, ) -> InstructionGroup { - let mut ig = InstructionGroupBuilder::new(all_instructions, format_registry); + let mut ig = InstructionGroupBuilder::new(all_instructions); // Operand kind shorthands. let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into(); @@ -130,6 +130,7 @@ pub(crate) fn define( EBB arguments. The number and types of arguments must match the destination EBB. "#, + &formats.jump, ) .operands_in(vec![EBB, args]) .is_terminator(true) @@ -149,6 +150,7 @@ pub(crate) fn define( relaxation pass. There is no reason to use this instruction outside that pass. "#, + &formats.jump, ) .operands_in(vec![EBB, args]) .is_terminator(true) @@ -164,6 +166,7 @@ pub(crate) fn define( If ``c`` is a `b1` value, take the branch when ``c`` is false. If ``c`` is an integer value, take the branch when ``c = 0``. "#, + &formats.branch, ) .operands_in(vec![c, EBB, args]) .is_branch(true), @@ -178,6 +181,7 @@ pub(crate) fn define( If ``c`` is a `b1` value, take the branch when ``c`` is true. If ``c`` is an integer value, take the branch when ``c != 0``. "#, + &formats.branch, ) .operands_in(vec![c, EBB, args]) .is_branch(true), @@ -207,6 +211,7 @@ pub(crate) fn define( implement all or some of the condition codes. The instruction can also be used to represent *macro-op fusion* on architectures like Intel's. "#, + &formats.branch_icmp, ) .operands_in(vec![Cond, x, y, EBB, args]) .is_branch(true), @@ -220,6 +225,7 @@ pub(crate) fn define( r#" Branch when condition is true in integer CPU flags. "#, + &formats.branch_int, ) .operands_in(vec![Cond, f, EBB, args]) .is_branch(true), @@ -234,6 +240,7 @@ pub(crate) fn define( r#" Branch when condition is true in floating point CPU flags. "#, + &formats.branch_float, ) .operands_in(vec![Cond, f, EBB, args]) .is_branch(true), @@ -263,6 +270,7 @@ pub(crate) fn define( function in a dynamic library, that will typically use ``call_indirect``. "#, + &formats.branch_table, ) .operands_in(vec![x, EBB, JT]) .is_terminator(true) @@ -287,6 +295,7 @@ pub(crate) fn define( Currently, the only type supported is entries which are relative to the base of the jump table. "#, + &formats.branch_table_entry, ) .operands_in(vec![x, addr, Size, JT]) .operands_out(vec![entry]) @@ -304,6 +313,7 @@ pub(crate) fn define( load an entry using ``jump_table_entry``, then use this instruction to add the relative base back to it. "#, + &formats.branch_table_base, ) .operands_in(vec![JT]) .operands_out(vec![addr]), @@ -318,6 +328,7 @@ pub(crate) fn define( Unconditionally jump via a jump table entry that was previously loaded with the ``jump_table_entry`` instruction. "#, + &formats.indirect_jump, ) .operands_in(vec![addr, JT]) .is_indirect_branch(true) @@ -331,6 +342,7 @@ pub(crate) fn define( r#" Encodes an assembly debug trap. "#, + &formats.nullary, ) .other_side_effects(true) .can_load(true) @@ -345,6 +357,7 @@ pub(crate) fn define( r#" Terminate execution unconditionally. "#, + &formats.trap, ) .operands_in(vec![code]) .can_trap(true) @@ -359,6 +372,7 @@ pub(crate) fn define( if ``c`` is non-zero, execution continues at the following instruction. "#, + &formats.cond_trap, ) .operands_in(vec![c, code]) .can_trap(true), @@ -372,6 +386,7 @@ pub(crate) fn define( This instruction allows non-conditional traps to be used as non-terminal instructions. "#, + &formats.trap, ) .operands_in(vec![code]) .can_trap(true), @@ -385,6 +400,7 @@ pub(crate) fn define( if ``c`` is zero, execution continues at the following instruction. "#, + &formats.cond_trap, ) .operands_in(vec![c, code]) .can_trap(true), @@ -399,6 +415,7 @@ pub(crate) fn define( r#" Trap when condition is true in integer CPU flags. "#, + &formats.int_cond_trap, ) .operands_in(vec![Cond, f, code]) .can_trap(true), @@ -413,6 +430,7 @@ pub(crate) fn define( r#" Trap when condition is true in floating point CPU flags. "#, + &formats.float_cond_trap, ) .operands_in(vec![Cond, f, code]) .can_trap(true), @@ -430,6 +448,7 @@ pub(crate) fn define( provided return values. The list of return values must match the function signature's return types. "#, + &formats.multiary, ) .operands_in(vec![rvals]) .is_return(true) @@ -446,6 +465,7 @@ pub(crate) fn define( a custom epilogue, which will then perform the real return. This instruction has no encoding. "#, + &formats.multiary, ) .operands_in(vec![rvals]) .is_return(true) @@ -468,6 +488,7 @@ pub(crate) fn define( Call a function which has been declared in the preamble. The argument types must match the function's signature. "#, + &formats.call, ) .operands_in(vec![FN, args]) .operands_out(vec![rvals]) @@ -491,6 +512,7 @@ pub(crate) fn define( `table_addr` and `load` are used to obtain a native address from a table. "#, + &formats.call_indirect, ) .operands_in(vec![SIG, callee, args]) .operands_out(vec![rvals]) @@ -509,6 +531,7 @@ pub(crate) fn define( are too far away to be addressable by a direct `call` instruction. "#, + &formats.func_addr, ) .operands_in(vec![FN]) .operands_out(vec![addr]), @@ -531,6 +554,7 @@ pub(crate) fn define( This is a polymorphic instruction that can load any value type which has a memory representation. "#, + &formats.load, ) .operands_in(vec![MemFlags, p, Offset]) .operands_out(vec![a]) @@ -546,6 +570,7 @@ pub(crate) fn define( This is a polymorphic instruction that can load any value type which has a memory representation. "#, + &formats.load_complex, ) .operands_in(vec![MemFlags, args, Offset]) .operands_out(vec![a]) @@ -561,6 +586,7 @@ pub(crate) fn define( This is a polymorphic instruction that can store any value type with a memory representation. "#, + &formats.store, ) .operands_in(vec![MemFlags, x, p, Offset]) .can_store(true), @@ -575,6 +601,7 @@ pub(crate) fn define( This is a polymorphic instruction that can store any value type with a memory representation. "#, + &formats.store_complex, ) .operands_in(vec![MemFlags, x, args, Offset]) .can_store(true), @@ -596,6 +623,7 @@ pub(crate) fn define( This is equivalent to ``load.i8`` followed by ``uextend``. "#, + &formats.load, ) .operands_in(vec![MemFlags, p, Offset]) .operands_out(vec![a]) @@ -610,6 +638,7 @@ pub(crate) fn define( This is equivalent to ``load.i8`` followed by ``uextend``. "#, + &formats.load_complex, ) .operands_in(vec![MemFlags, args, Offset]) .operands_out(vec![a]) @@ -624,6 +653,7 @@ pub(crate) fn define( This is equivalent to ``load.i8`` followed by ``sextend``. "#, + &formats.load, ) .operands_in(vec![MemFlags, p, Offset]) .operands_out(vec![a]) @@ -638,6 +668,7 @@ pub(crate) fn define( This is equivalent to ``load.i8`` followed by ``sextend``. "#, + &formats.load_complex, ) .operands_in(vec![MemFlags, args, Offset]) .operands_out(vec![a]) @@ -652,6 +683,7 @@ pub(crate) fn define( This is equivalent to ``ireduce.i8`` followed by ``store.i8``. "#, + &formats.store, ) .operands_in(vec![MemFlags, x, p, Offset]) .can_store(true), @@ -665,6 +697,7 @@ pub(crate) fn define( This is equivalent to ``ireduce.i8`` followed by ``store.i8``. "#, + &formats.store_complex, ) .operands_in(vec![MemFlags, x, args, Offset]) .can_store(true), @@ -686,6 +719,7 @@ pub(crate) fn define( This is equivalent to ``load.i16`` followed by ``uextend``. "#, + &formats.load, ) .operands_in(vec![MemFlags, p, Offset]) .operands_out(vec![a]) @@ -700,6 +734,7 @@ pub(crate) fn define( This is equivalent to ``load.i16`` followed by ``uextend``. "#, + &formats.load_complex, ) .operands_in(vec![MemFlags, args, Offset]) .operands_out(vec![a]) @@ -714,6 +749,7 @@ pub(crate) fn define( This is equivalent to ``load.i16`` followed by ``sextend``. "#, + &formats.load, ) .operands_in(vec![MemFlags, p, Offset]) .operands_out(vec![a]) @@ -728,6 +764,7 @@ pub(crate) fn define( This is equivalent to ``load.i16`` followed by ``sextend``. "#, + &formats.load_complex, ) .operands_in(vec![MemFlags, args, Offset]) .operands_out(vec![a]) @@ -742,6 +779,7 @@ pub(crate) fn define( This is equivalent to ``ireduce.i16`` followed by ``store.i16``. "#, + &formats.store, ) .operands_in(vec![MemFlags, x, p, Offset]) .can_store(true), @@ -755,6 +793,7 @@ pub(crate) fn define( This is equivalent to ``ireduce.i16`` followed by ``store.i16``. "#, + &formats.store_complex, ) .operands_in(vec![MemFlags, x, args, Offset]) .can_store(true), @@ -776,6 +815,7 @@ pub(crate) fn define( This is equivalent to ``load.i32`` followed by ``uextend``. "#, + &formats.load, ) .operands_in(vec![MemFlags, p, Offset]) .operands_out(vec![a]) @@ -790,6 +830,7 @@ pub(crate) fn define( This is equivalent to ``load.i32`` followed by ``uextend``. "#, + &formats.load_complex, ) .operands_in(vec![MemFlags, args, Offset]) .operands_out(vec![a]) @@ -804,6 +845,7 @@ pub(crate) fn define( This is equivalent to ``load.i32`` followed by ``sextend``. "#, + &formats.load, ) .operands_in(vec![MemFlags, p, Offset]) .operands_out(vec![a]) @@ -818,6 +860,7 @@ pub(crate) fn define( This is equivalent to ``load.i32`` followed by ``sextend``. "#, + &formats.load_complex, ) .operands_in(vec![MemFlags, args, Offset]) .operands_out(vec![a]) @@ -832,6 +875,7 @@ pub(crate) fn define( This is equivalent to ``ireduce.i32`` followed by ``store.i32``. "#, + &formats.store, ) .operands_in(vec![MemFlags, x, p, Offset]) .can_store(true), @@ -845,6 +889,7 @@ pub(crate) fn define( This is equivalent to ``ireduce.i32`` followed by ``store.i32``. "#, + &formats.store_complex, ) .operands_in(vec![MemFlags, x, args, Offset]) .can_store(true), @@ -867,6 +912,7 @@ pub(crate) fn define( access cannot go out of bounds, i.e. `sizeof(a) + Offset <= sizeof(SS)`. "#, + &formats.stack_load, ) .operands_in(vec![SS, Offset]) .operands_out(vec![a]) @@ -886,6 +932,7 @@ pub(crate) fn define( access cannot go out of bounds, i.e. `sizeof(a) + Offset <= sizeof(SS)`. "#, + &formats.stack_store, ) .operands_in(vec![x, SS, Offset]) .can_store(true), @@ -901,6 +948,7 @@ pub(crate) fn define( refer to a byte inside the stack slot: `0 <= Offset < sizeof(SS)`. "#, + &formats.stack_load, ) .operands_in(vec![SS, Offset]) .operands_out(vec![addr]), @@ -914,6 +962,7 @@ pub(crate) fn define( r#" Compute the value of global GV. "#, + &formats.unary_global_value, ) .operands_in(vec![GV]) .operands_out(vec![a]), @@ -925,6 +974,7 @@ pub(crate) fn define( r#" Compute the value of global GV, which is a symbolic value. "#, + &formats.unary_global_value, ) .operands_in(vec![GV]) .operands_out(vec![a]), @@ -954,6 +1004,7 @@ pub(crate) fn define( heap's base address. 2. If ``p + Size`` is greater than the heap bound, generate a trap. "#, + &formats.heap_addr, ) .operands_in(vec![H, p, Size]) .operands_out(vec![addr]), @@ -971,6 +1022,7 @@ pub(crate) fn define( r#" Gets the content of the pinned register, when it's enabled. "#, + &formats.nullary, ) .operands_out(vec![addr]) .other_side_effects(true), @@ -982,6 +1034,7 @@ pub(crate) fn define( r#" Sets the content of the pinned register, when it's enabled. "#, + &formats.unary, ) .operands_in(vec![addr]) .other_side_effects(true), @@ -1012,6 +1065,7 @@ pub(crate) fn define( base address. 2. If ``p`` is greater than the table bound, generate a trap. "#, + &formats.table_addr, ) .operands_in(vec![T, p, Offset]) .operands_out(vec![addr]), @@ -1029,6 +1083,7 @@ pub(crate) fn define( Create a scalar integer SSA value with an immediate constant value, or an integer vector where all the lanes have the same value. "#, + &formats.unary_imm, ) .operands_in(vec![N]) .operands_out(vec![a]), @@ -1045,6 +1100,7 @@ pub(crate) fn define( Create a `f32` SSA value with an immediate constant value. "#, + &formats.unary_ieee32, ) .operands_in(vec![N]) .operands_out(vec![a]), @@ -1061,6 +1117,7 @@ pub(crate) fn define( Create a `f64` SSA value with an immediate constant value. "#, + &formats.unary_ieee64, ) .operands_in(vec![N]) .operands_out(vec![a]), @@ -1078,6 +1135,7 @@ pub(crate) fn define( Create a scalar boolean SSA value with an immediate constant value, or a boolean vector where all the lanes have the same value. "#, + &formats.unary_bool, ) .operands_in(vec![N]) .operands_out(vec![a]), @@ -1098,6 +1156,7 @@ pub(crate) fn define( Construct a vector with the given immediate bytes. "#, + &formats.unary_const, ) .operands_in(vec![N]) .operands_out(vec![a]), @@ -1127,12 +1186,13 @@ pub(crate) fn define( "shuffle", r#" SIMD vector shuffle. - + Shuffle two vectors using the given immediate bytes. For each of the 16 bytes of the - immediate, a value i of 0-15 selects the i-th element of the first vector and a value i of - 16-31 selects the (i-16)th element of the second vector. Immediate values outside of the + immediate, a value i of 0-15 selects the i-th element of the first vector and a value i of + 16-31 selects the (i-16)th element of the second vector. Immediate values outside of the 0-31 range place a 0 in the resulting vector lane. "#, + &formats.shuffle, ) .operands_in(vec![a, b, mask]) .operands_out(vec![a]), @@ -1148,6 +1208,7 @@ pub(crate) fn define( Create a scalar reference SSA value with a constant null value. "#, + &formats.nullary, ) .operands_out(vec![a]), ); @@ -1155,10 +1216,11 @@ pub(crate) fn define( ig.push(Inst::new( "nop", r#" - Just a dummy instruction + Just a dummy instruction. - Note: this doesn't compile to a machine code nop + Note: this doesn't compile to a machine code nop. "#, + &formats.nullary, )); let c = &operand_doc("c", Testable, "Controlling value to test"); @@ -1175,6 +1237,7 @@ pub(crate) fn define( This instruction selects whole values. Use `vselect` for lane-wise selection. "#, + &formats.ternary, ) .operands_in(vec![c, x, y]) .operands_out(vec![a]), @@ -1189,6 +1252,7 @@ pub(crate) fn define( r#" Conditional select, dependent on integer condition codes. "#, + &formats.int_select, ) .operands_in(vec![cc, flags, x, y]) .operands_out(vec![a]), @@ -1201,10 +1265,11 @@ pub(crate) fn define( r#" Conditional select of bits. - For each bit in `c`, this instruction selects the corresponding bit from `x` if the bit - in `c` is 1 and the corresponding bit from `y` if the bit in `c` is 0. See also: + For each bit in `c`, this instruction selects the corresponding bit from `x` if the bit + in `c` is 1 and the corresponding bit from `y` if the bit in `c` is 0. See also: `select`, `vselect`. "#, + &formats.ternary, ) .operands_in(vec![c, x, y]) .operands_out(vec![a]), @@ -1225,6 +1290,7 @@ pub(crate) fn define( instruction transformations, and the register allocator needs a way of representing register copies. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -1239,6 +1305,7 @@ pub(crate) fn define( This instruction behaves exactly like `copy`, but the result value is assigned to a spill slot. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -1254,6 +1321,7 @@ pub(crate) fn define( This instruction behaves exactly like `copy`, but creates a new SSA value for the spilled input value. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -1270,6 +1338,7 @@ pub(crate) fn define( registers and stack slots have been assigned. It is used to replace `fill`s that have been identified as redundant. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -1294,6 +1363,7 @@ pub(crate) fn define( before the value leaves the EBB. At the entry to a new EBB, all live values must be in their originally assigned registers. "#, + &formats.reg_move, ) .operands_in(vec![x, src, dst]) .other_side_effects(true), @@ -1310,6 +1380,7 @@ pub(crate) fn define( special registers, e.g. copying the stack register to the frame register in a function prologue. "#, + &formats.copy_special, ) .operands_in(vec![src, dst]) .other_side_effects(true), @@ -1326,6 +1397,7 @@ pub(crate) fn define( of ''copy_special''. This instruction is internal and should not be created by Cranelift users. "#, + &formats.copy_to_ssa, ) .operands_in(vec![src]) .operands_out(vec![a]) @@ -1341,6 +1413,7 @@ pub(crate) fn define( This instruction copies its input, preserving the value type. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -1356,6 +1429,7 @@ pub(crate) fn define( This instruction is used to adjust the stack pointer by a dynamic amount. "#, + &formats.unary, ) .operands_in(vec![delta]) .other_side_effects(true), @@ -1373,6 +1447,7 @@ pub(crate) fn define( prologues and epilogues. ``Offset`` is constrained to the size of a signed 32-bit integer. "#, + &formats.unary_imm, ) .operands_in(vec![Offset]) .other_side_effects(true), @@ -1391,6 +1466,7 @@ pub(crate) fn define( prologues and epilogues. ``Offset`` is constrained to the size of a signed 32-bit integer. "#, + &formats.unary_imm, ) .operands_in(vec![Offset]) .other_side_effects(true), @@ -1407,6 +1483,7 @@ pub(crate) fn define( This is like `ifcmp` where ``addr`` is the LHS operand and the stack pointer is the RHS. "#, + &formats.unary, ) .operands_in(vec![addr]) .operands_out(vec![f]), @@ -1425,6 +1502,7 @@ pub(crate) fn define( See also `regmove`. "#, + &formats.reg_spill, ) .operands_in(vec![x, src, SS]) .other_side_effects(true), @@ -1443,6 +1521,7 @@ pub(crate) fn define( See also `regmove`. "#, + &formats.reg_fill, ) .operands_in(vec![x, SS, dst]) .other_side_effects(true), @@ -1461,6 +1540,7 @@ pub(crate) fn define( This instruction will provide live reference values at a point in the function. It can only be used by the compiler. "#, + &formats.multiary, ) .operands_in(vec![N]) .other_side_effects(true), @@ -1480,6 +1560,7 @@ pub(crate) fn define( the lanes from ``x``. The result may be two scalars if ``x`` only had two lanes. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![lo, hi]) @@ -1515,6 +1596,7 @@ pub(crate) fn define( It is possible to form a vector by concatenating two scalars. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]) @@ -1535,6 +1617,7 @@ pub(crate) fn define( Select lanes from ``x`` or ``y`` controlled by the lanes of the boolean vector ``c``. "#, + &formats.ternary, ) .operands_in(vec![c, x, y]) .operands_out(vec![a]), @@ -1550,6 +1633,7 @@ pub(crate) fn define( Return a vector whose lanes are all ``x``. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -1568,6 +1652,7 @@ pub(crate) fn define( The lane index, ``Idx``, is an immediate value, not an SSA value. It must indicate a valid lane index for the type of ``x``. "#, + &formats.insert_lane, ) .operands_in(vec![x, Idx, y]) .operands_out(vec![a]), @@ -1584,9 +1669,10 @@ pub(crate) fn define( The lane index, ``Idx``, is an immediate value, not an SSA value. It must indicate a valid lane index for the type of ``x``. Note that the upper bits of ``a`` - may or may not be zeroed depending on the ISA but the type system should prevent using + may or may not be zeroed depending on the ISA but the type system should prevent using ``a`` as anything other than the extracted value. "#, + &formats.extract_lane, ) .operands_in(vec![x, Idx]) .operands_out(vec![a]), @@ -1625,6 +1711,7 @@ pub(crate) fn define( When this instruction compares integer vectors, it returns a boolean vector of lane-wise comparisons. "#, + &formats.int_compare, ) .operands_in(vec![Cond, x, y]) .operands_out(vec![a]), @@ -1646,6 +1733,7 @@ pub(crate) fn define( This instruction can only compare scalars. Use `icmp` for lane-wise vector comparisons. "#, + &formats.int_compare_imm, ) .operands_in(vec![Cond, x, Y]) .operands_out(vec![a]), @@ -1664,6 +1752,7 @@ pub(crate) fn define( Compare two scalar integer values and return integer CPU flags representing the result. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![f]), @@ -1678,6 +1767,7 @@ pub(crate) fn define( Like `icmp_imm`, but returns integer CPU flags instead of testing a specific condition code. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![f]), @@ -1696,6 +1786,7 @@ pub(crate) fn define( This instruction does not depend on the signed/unsigned interpretation of the operands. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1707,10 +1798,11 @@ pub(crate) fn define( r#" Add with unsigned saturation. - This is similar to `iadd` but the operands are interpreted as unsigned integers and their + This is similar to `iadd` but the operands are interpreted as unsigned integers and their summed result, instead of wrapping, will be saturated to the highest unsigned integer for the controlling type (e.g. `0xFF` for i8). "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1722,12 +1814,13 @@ pub(crate) fn define( r#" Add with signed saturation. - This is similar to `iadd` but the operands are interpreted as signed integers and their - summed result, instead of wrapping, will be saturated to the lowest or highest - signed integer for the controlling type (e.g. `0x80` or `0x7F` for i8). For example, - since an `iadd_ssat.i8` of `0x70` and `0x70` is greater than `0x7F`, the result will be + This is similar to `iadd` but the operands are interpreted as signed integers and their + summed result, instead of wrapping, will be saturated to the lowest or highest + signed integer for the controlling type (e.g. `0x80` or `0x7F` for i8). For example, + since an `iadd_ssat.i8` of `0x70` and `0x70` is greater than `0x7F`, the result will be clamped to `0x7F`. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1742,6 +1835,7 @@ pub(crate) fn define( This instruction does not depend on the signed/unsigned interpretation of the operands. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1753,10 +1847,11 @@ pub(crate) fn define( r#" Subtract with unsigned saturation. - This is similar to `isub` but the operands are interpreted as unsigned integers and their + This is similar to `isub` but the operands are interpreted as unsigned integers and their difference, instead of wrapping, will be saturated to the lowest unsigned integer for the controlling type (e.g. `0x00` for i8). "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1768,10 +1863,11 @@ pub(crate) fn define( r#" Subtract with signed saturation. - This is similar to `isub` but the operands are interpreted as signed integers and their - difference, instead of wrapping, will be saturated to the lowest or highest + This is similar to `isub` but the operands are interpreted as signed integers and their + difference, instead of wrapping, will be saturated to the lowest or highest signed integer for the controlling type (e.g. `0x80` or `0x7F` for i8). "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1783,6 +1879,7 @@ pub(crate) fn define( r#" Integer negation: `a := -x \pmod{2^B}`. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -1799,6 +1896,7 @@ pub(crate) fn define( Polymorphic over all integer types (vector and scalar). "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1814,6 +1912,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1829,6 +1928,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -1842,6 +1942,7 @@ pub(crate) fn define( This operation traps if the divisor is zero. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]) @@ -1859,6 +1960,7 @@ pub(crate) fn define( representable in `B` bits two's complement. This only happens when `x = -2^{B-1}, y = -1`. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]) @@ -1873,6 +1975,7 @@ pub(crate) fn define( This operation traps if the divisor is zero. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]) @@ -1887,6 +1990,7 @@ pub(crate) fn define( This operation traps if the divisor is zero. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]) @@ -1908,6 +2012,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -1922,6 +2027,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -1935,6 +2041,7 @@ pub(crate) fn define( This operation traps if the divisor is zero. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -1950,6 +2057,7 @@ pub(crate) fn define( representable in `B` bits two's complement. This only happens when `x = -2^{B-1}, Y = -1`. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -1963,6 +2071,7 @@ pub(crate) fn define( This operation traps if the divisor is zero. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -1976,6 +2085,7 @@ pub(crate) fn define( This operation traps if the divisor is zero. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -1994,6 +2104,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2028,6 +2139,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.ternary, ) .operands_in(vec![x, y, c_in]) .operands_out(vec![a]), @@ -2048,6 +2160,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.ternary, ) .operands_in(vec![x, y, c_if_in]) .operands_out(vec![a]), @@ -2069,6 +2182,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a, c_out]), @@ -2090,6 +2204,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a, c_if_out]), @@ -2111,6 +2226,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.ternary, ) .operands_in(vec![x, y, c_in]) .operands_out(vec![a, c_out]), @@ -2132,6 +2248,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.ternary, ) .operands_in(vec![x, y, c_if_in]) .operands_out(vec![a, c_if_out]), @@ -2152,6 +2269,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.ternary, ) .operands_in(vec![x, y, b_in]) .operands_out(vec![a]), @@ -2172,6 +2290,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.ternary, ) .operands_in(vec![x, y, b_if_in]) .operands_out(vec![a]), @@ -2193,6 +2312,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a, b_out]), @@ -2214,6 +2334,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a, b_if_out]), @@ -2235,6 +2356,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.ternary, ) .operands_in(vec![x, y, b_in]) .operands_out(vec![a, b_out]), @@ -2256,6 +2378,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.ternary, ) .operands_in(vec![x, y, b_if_in]) .operands_out(vec![a, b_if_out]), @@ -2282,6 +2405,7 @@ pub(crate) fn define( r#" Bitwise and. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2293,6 +2417,7 @@ pub(crate) fn define( r#" Bitwise or. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2304,6 +2429,7 @@ pub(crate) fn define( r#" Bitwise xor. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2315,6 +2441,7 @@ pub(crate) fn define( r#" Bitwise not. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2328,6 +2455,7 @@ pub(crate) fn define( Computes `x & ~y`. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2341,6 +2469,7 @@ pub(crate) fn define( Computes `x | ~y`. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2354,6 +2483,7 @@ pub(crate) fn define( Computes `x ^ ~y`. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2374,6 +2504,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2390,6 +2521,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2406,6 +2538,7 @@ pub(crate) fn define( Polymorphic over all scalar integer types, but does not support vector types. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2424,6 +2557,7 @@ pub(crate) fn define( Rotate the bits in ``x`` by ``y`` places. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2437,6 +2571,7 @@ pub(crate) fn define( Rotate the bits in ``x`` by ``y`` places. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2448,6 +2583,7 @@ pub(crate) fn define( r#" Rotate left by immediate. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2459,6 +2595,7 @@ pub(crate) fn define( r#" Rotate right by immediate. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2480,6 +2617,7 @@ pub(crate) fn define( a &:= x \cdot 2^s \pmod{2^B}. ``` "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2502,6 +2640,7 @@ pub(crate) fn define( a &:= \lfloor x \cdot 2^{-s} \rfloor. ``` "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2517,6 +2656,7 @@ pub(crate) fn define( The shift amount is masked to the size of the register. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2530,6 +2670,7 @@ pub(crate) fn define( The shift amount is masked to the size of ``x``. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2543,6 +2684,7 @@ pub(crate) fn define( The shift amount is masked to the size of the register. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2556,6 +2698,7 @@ pub(crate) fn define( The shift amount is masked to the size of the register. "#, + &formats.binary_imm, ) .operands_in(vec![x, Y]) .operands_out(vec![a]), @@ -2572,6 +2715,7 @@ pub(crate) fn define( Reverses the bits in ``x``. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2587,6 +2731,7 @@ pub(crate) fn define( reaching the first one bit. When ``x`` is zero, returns the size of x in bits. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2602,6 +2747,7 @@ pub(crate) fn define( consecutive bits identical to the sign bit. When ``x`` is 0 or -1, returns one less than the size of x in bits. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2617,6 +2763,7 @@ pub(crate) fn define( reaching the first one bit. When ``x`` is zero, returns the size of x in bits. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2630,6 +2777,7 @@ pub(crate) fn define( Count the number of one bits in ``x``. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2712,6 +2860,7 @@ pub(crate) fn define( When this instruction compares floating point vectors, it returns a boolean vector with the results of lane-wise comparisons. "#, + &formats.float_compare, ) .operands_in(vec![Cond, x, y]) .operands_out(vec![a]), @@ -2728,6 +2877,7 @@ pub(crate) fn define( Compares two numbers like `fcmp`, but returns floating point CPU flags instead of testing a specific condition. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![f]), @@ -2744,6 +2894,7 @@ pub(crate) fn define( r#" Floating point addition. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2755,6 +2906,7 @@ pub(crate) fn define( r#" Floating point subtraction. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2766,6 +2918,7 @@ pub(crate) fn define( r#" Floating point multiplication. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2781,6 +2934,7 @@ pub(crate) fn define( `udiv`, this can't trap. Division by zero is infinity or NaN, depending on the dividend. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2792,6 +2946,7 @@ pub(crate) fn define( r#" Floating point square root. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2806,6 +2961,7 @@ pub(crate) fn define( Computes `a := xy+z` without any intermediate rounding of the product. "#, + &formats.ternary, ) .operands_in(vec![x, y, z]) .operands_out(vec![a]), @@ -2821,6 +2977,7 @@ pub(crate) fn define( Note that this is a pure bitwise operation. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2836,6 +2993,7 @@ pub(crate) fn define( Note that this is a pure bitwise operation. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2856,6 +3014,7 @@ pub(crate) fn define( Note that this is a pure bitwise operation. The sign bit from ``y`` is copied to the sign bit of ``x``. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2871,6 +3030,7 @@ pub(crate) fn define( If either operand is NaN, this returns a NaN. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2886,6 +3046,7 @@ pub(crate) fn define( If either operand is NaN, this returns a NaN. "#, + &formats.binary, ) .operands_in(vec![x, y]) .operands_out(vec![a]), @@ -2899,6 +3060,7 @@ pub(crate) fn define( r#" Round floating point round to integral, towards positive infinity. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2910,6 +3072,7 @@ pub(crate) fn define( r#" Round floating point round to integral, towards negative infinity. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2921,6 +3084,7 @@ pub(crate) fn define( r#" Round floating point round to integral, towards zero. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2933,6 +3097,7 @@ pub(crate) fn define( Round floating point round to integral, towards nearest with ties to even. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2950,6 +3115,7 @@ pub(crate) fn define( The condition code determines if the reference type in question is null or not. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -2968,6 +3134,7 @@ pub(crate) fn define( Check the CPU flags in ``f`` against the ``Cond`` condition code and return true when the condition code is satisfied. "#, + &formats.int_cond, ) .operands_in(vec![Cond, f]) .operands_out(vec![a]), @@ -2985,6 +3152,7 @@ pub(crate) fn define( Check the CPU flags in ``f`` against the ``Cond`` condition code and return true when the condition code is satisfied. "#, + &formats.float_cond, ) .operands_in(vec![Cond, f]) .operands_out(vec![a]), @@ -3003,6 +3171,7 @@ pub(crate) fn define( size. A bitcast is equivalent to storing one type and loading the other type from the same address. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -3025,6 +3194,7 @@ pub(crate) fn define( lane is a raw_bitcast of the corresponding operand lane. TODO there is currently no mechanism for enforcing the bit width constraint. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -3037,12 +3207,13 @@ pub(crate) fn define( Inst::new( "scalar_to_vector", r#" - Scalar To Vector -- move a value out of a scalar register and into a vector register; the - scalar will be moved to the lowest-order bits of the vector register. Note that this - instruction is intended as a low-level legalization instruction and frontends should prefer + Scalar To Vector -- move a value out of a scalar register and into a vector register; the + scalar will be moved to the lowest-order bits of the vector register. Note that this + instruction is intended as a low-level legalization instruction and frontends should prefer insertlane; on certain architectures, scalar_to_vector may zero the highest-order bits for some types (e.g. integers) but not for others (e.g. floats). "#, + &formats.unary, ) .operands_in(vec![s]) .operands_out(vec![a]), @@ -3079,6 +3250,7 @@ pub(crate) fn define( and each lane must not have more bits that the input lanes. If the input and output types are the same, this is a no-op. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3106,6 +3278,7 @@ pub(crate) fn define( and each lane must not have fewer bits that the input lanes. If the input and output types are the same, this is a no-op. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3132,6 +3305,7 @@ pub(crate) fn define( True maps to 1 and false maps to 0. The result type must have the same number of vector lanes as the input. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -3146,6 +3320,7 @@ pub(crate) fn define( True maps to all 1s and false maps to all 0s. The result type must have the same number of vector lanes as the input. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -3185,6 +3360,7 @@ pub(crate) fn define( and each lane must not have more bits that the input lanes. If the input and output types are the same, this is a no-op. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3216,6 +3392,7 @@ pub(crate) fn define( and each lane must not have fewer bits that the input lanes. If the input and output types are the same, this is a no-op. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3236,6 +3413,7 @@ pub(crate) fn define( and each lane must not have fewer bits that the input lanes. If the input and output types are the same, this is a no-op. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3269,6 +3447,7 @@ pub(crate) fn define( and the result lanes must not have fewer bits than the input lanes. If the input and output types are the same, this is a no-op. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3291,6 +3470,7 @@ pub(crate) fn define( and the result lanes must not have more bits than the input lanes. If the input and output types are the same, this is a no-op. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3312,6 +3492,7 @@ pub(crate) fn define( The result type must have the same number of vector lanes as the input. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3326,6 +3507,7 @@ pub(crate) fn define( saturates the input instead of trapping. NaN and negative values are converted to 0. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -3343,6 +3525,7 @@ pub(crate) fn define( The result type must have the same number of vector lanes as the input. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]) @@ -3356,6 +3539,7 @@ pub(crate) fn define( Convert floating point to signed integer as fcvt_to_sint does, but saturates the input instead of trapping. NaN values are converted to 0. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -3375,6 +3559,7 @@ pub(crate) fn define( The result type must have the same number of vector lanes as the input. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -3391,6 +3576,7 @@ pub(crate) fn define( The result type must have the same number of vector lanes as the input. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![a]), @@ -3420,6 +3606,7 @@ pub(crate) fn define( Returns the low half of `x` and the high half of `x` as two independent values. "#, + &formats.unary, ) .operands_in(vec![x]) .operands_out(vec![lo, hi]) @@ -3453,6 +3640,7 @@ pub(crate) fn define( the same number of lanes as the inputs, but the lanes are twice the size. "#, + &formats.binary, ) .operands_in(vec![lo, hi]) .operands_out(vec![a]) diff --git a/cranelift/codegen/meta/src/shared/mod.rs b/cranelift/codegen/meta/src/shared/mod.rs index 73cdb4254d..808b6bbc49 100644 --- a/cranelift/codegen/meta/src/shared/mod.rs +++ b/cranelift/codegen/meta/src/shared/mod.rs @@ -8,20 +8,25 @@ pub mod legalize; pub mod settings; pub mod types; -use crate::cdsl::formats::FormatRegistry; +use crate::cdsl::formats::{FormatStructure, InstructionFormat}; use crate::cdsl::instructions::{AllInstructions, InstructionGroup}; use crate::cdsl::settings::SettingGroup; use crate::cdsl::xform::TransformGroups; use crate::shared::entities::EntityRefs; +use crate::shared::formats::Formats; use crate::shared::immediates::Immediates; +use std::collections::HashMap; +use std::iter::FromIterator; +use std::rc::Rc; + pub(crate) struct Definitions { pub settings: SettingGroup, pub all_instructions: AllInstructions, pub instructions: InstructionGroup, pub imm: Immediates, - pub format_registry: FormatRegistry, + pub formats: Formats, pub transform_groups: TransformGroups, } @@ -30,13 +35,9 @@ pub(crate) fn define() -> Definitions { let immediates = Immediates::new(); let entities = EntityRefs::new(); - let format_registry = formats::define(&immediates, &entities); - let instructions = instructions::define( - &mut all_instructions, - &format_registry, - &immediates, - &entities, - ); + let formats = Formats::new(&immediates, &entities); + let instructions = + instructions::define(&mut all_instructions, &formats, &immediates, &entities); let transform_groups = legalize::define(&instructions, &immediates); Definitions { @@ -44,7 +45,53 @@ pub(crate) fn define() -> Definitions { all_instructions, instructions, imm: immediates, - format_registry, + formats, 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> = 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 = 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)) + } +}