diff --git a/cranelift/codegen/meta/src/cdsl/cpu_modes.rs b/cranelift/codegen/meta/src/cdsl/cpu_modes.rs index 873cfe3d95..e42a27da2a 100644 --- a/cranelift/codegen/meta/src/cdsl/cpu_modes.rs +++ b/cranelift/codegen/meta/src/cdsl/cpu_modes.rs @@ -1,7 +1,6 @@ -use std::collections::{hash_map, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::iter::FromIterator; -use crate::cdsl::encodings::Encoding; use crate::cdsl::types::ValueType; use crate::cdsl::xform::TransformGroupIndex; @@ -10,30 +9,9 @@ pub(crate) struct CpuMode { default_legalize: Option, monomorphic_legalize: Option, typed_legalize: HashMap, - pub encodings: Vec, } impl CpuMode { - pub fn get_default_legalize_code(&self) -> TransformGroupIndex { - self.default_legalize - .expect("a finished CpuMode must have a default legalize code") - } - pub fn get_legalize_code_for(&self, typ: &Option) -> TransformGroupIndex { - match typ { - Some(typ) => self - .typed_legalize - .get(typ) - .copied() - .unwrap_or_else(|| self.get_default_legalize_code()), - None => self - .monomorphic_legalize - .unwrap_or_else(|| self.get_default_legalize_code()), - } - } - pub fn get_legalized_types(&self) -> hash_map::Keys { - self.typed_legalize.keys() - } - /// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the directly /// reachable set of TransformGroup this TargetIsa uses. pub fn direct_transform_groups(&self) -> Vec { diff --git a/cranelift/codegen/meta/src/cdsl/encodings.rs b/cranelift/codegen/meta/src/cdsl/encodings.rs deleted file mode 100644 index 3474e3dda0..0000000000 --- a/cranelift/codegen/meta/src/cdsl/encodings.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::cdsl::instructions::{ - InstSpec, Instruction, - InstructionPredicateNumber -}; -use crate::cdsl::recipes::{EncodingRecipeNumber, Recipes}; -use crate::cdsl::settings::SettingPredicateNumber; -use crate::cdsl::types::ValueType; -use std::rc::Rc; - -/// Encoding for a concrete instruction. -/// -/// An `Encoding` object ties an instruction opcode with concrete type variables together with an -/// encoding recipe and encoding encbits. -/// -/// The concrete instruction can be in three different forms: -/// -/// 1. A naked opcode: `trap` for non-polymorphic instructions. -/// 2. With bound type variables: `iadd.i32` for polymorphic instructions. -/// 3. With operands providing constraints: `icmp.i32(intcc.eq, x, y)`. -/// -/// If the instruction is polymorphic, all type variables must be provided. -pub(crate) struct EncodingContent { - /// The `Instruction` or `BoundInstruction` being encoded. - inst: InstSpec, - - /// The `EncodingRecipe` to use. - pub recipe: EncodingRecipeNumber, - - /// Additional encoding bits to be interpreted by `recipe`. - pub encbits: u16, - - /// An instruction predicate that must be true to allow selecting this encoding. - pub inst_predicate: Option, - - /// An ISA predicate that must be true to allow selecting this encoding. - pub isa_predicate: Option, - - /// The value type this encoding has been bound to, for encodings of polymorphic instructions. - pub bound_type: Option, -} - -impl EncodingContent { - pub fn inst(&self) -> &Instruction { - self.inst.inst() - } - pub fn to_rust_comment(&self, recipes: &Recipes) -> String { - format!("[{}#{:02x}]", recipes[self.recipe].name, self.encbits) - } -} - -pub(crate) type Encoding = Rc; - diff --git a/cranelift/codegen/meta/src/cdsl/instructions.rs b/cranelift/codegen/meta/src/cdsl/instructions.rs index db8f4b1e4e..f7258ea300 100644 --- a/cranelift/codegen/meta/src/cdsl/instructions.rs +++ b/cranelift/codegen/meta/src/cdsl/instructions.rs @@ -745,12 +745,6 @@ impl FormatPredicateNode { } } - fn destructuring_member_name(&self) -> &'static str { - match &self.kind { - _ => self.member_name, - } - } - fn rust_predicate(&self) -> String { match &self.kind { FormatPredicateKind::IsEqual(arg) => { @@ -808,44 +802,6 @@ impl InstructionPredicateNode { .join(" && "), } } - - pub fn format_destructuring_member_name(&self) -> &str { - match self { - InstructionPredicateNode::FormatPredicate(format_pred) => { - format_pred.destructuring_member_name() - } - _ => panic!("Only for leaf format predicates"), - } - } - - pub fn format_name(&self) -> &str { - match self { - InstructionPredicateNode::FormatPredicate(format_pred) => format_pred.format_name, - _ => panic!("Only for leaf format predicates"), - } - } - - pub fn is_type_predicate(&self) -> bool { - match self { - InstructionPredicateNode::FormatPredicate(_) | InstructionPredicateNode::And(_) => { - false - } - InstructionPredicateNode::TypePredicate(_) => true, - } - } - - fn collect_leaves(&self) -> Vec<&InstructionPredicateNode> { - let mut ret = Vec::new(); - match self { - InstructionPredicateNode::And(nodes) => { - for node in nodes { - ret.extend(node.collect_leaves()); - } - } - _ => ret.push(self), - } - ret - } } #[derive(Clone, Hash, PartialEq, Eq)] @@ -918,22 +874,6 @@ impl InstructionPredicate { pub fn rust_predicate(&self, func_str: &str) -> Option { self.node.as_ref().map(|root| root.rust_predicate(func_str)) } - - /// Returns the type predicate if this is one, or None otherwise. - pub fn type_predicate(&self, func_str: &str) -> Option { - let node = self.node.as_ref().unwrap(); - if node.is_type_predicate() { - Some(node.rust_predicate(func_str)) - } else { - None - } - } - - /// Returns references to all the nodes that are leaves in the condition (i.e. by flattening - /// AND/OR). - pub fn collect_leaves(&self) -> Vec<&InstructionPredicateNode> { - self.node.as_ref().unwrap().collect_leaves() - } } #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/cranelift/codegen/meta/src/cdsl/isa.rs b/cranelift/codegen/meta/src/cdsl/isa.rs index f6ac9f2493..28844c97fb 100644 --- a/cranelift/codegen/meta/src/cdsl/isa.rs +++ b/cranelift/codegen/meta/src/cdsl/isa.rs @@ -86,11 +86,4 @@ impl TargetIsa { pub fn direct_transform_groups(&self) -> &Vec { &self.local_transform_groups } - - pub fn translate_group_index(&self, group_index: TransformGroupIndex) -> usize { - self.local_transform_groups - .iter() - .position(|&val| val == group_index) - .expect("TransformGroup unused by this TargetIsa!") - } } diff --git a/cranelift/codegen/meta/src/cdsl/mod.rs b/cranelift/codegen/meta/src/cdsl/mod.rs index 698b64dff3..bd08ebfe41 100644 --- a/cranelift/codegen/meta/src/cdsl/mod.rs +++ b/cranelift/codegen/meta/src/cdsl/mod.rs @@ -6,7 +6,6 @@ #[macro_use] pub mod ast; pub mod cpu_modes; -pub mod encodings; pub mod formats; pub mod instructions; pub mod isa; diff --git a/cranelift/codegen/meta/src/cdsl/recipes.rs b/cranelift/codegen/meta/src/cdsl/recipes.rs index 36036fc085..ea9ce5f20b 100644 --- a/cranelift/codegen/meta/src/cdsl/recipes.rs +++ b/cranelift/codegen/meta/src/cdsl/recipes.rs @@ -32,13 +32,6 @@ pub(crate) struct Stack { pub regclass: RegClassIndex, } -impl Stack { - pub fn stack_base_mask(self) -> &'static str { - // TODO: Make this configurable instead of just using the SP. - "StackBaseMask(1)" - } -} - #[derive(Clone, Hash, PartialEq)] pub(crate) struct BranchRange { pub inst_size: u64, diff --git a/cranelift/codegen/meta/src/gen_binemit.rs b/cranelift/codegen/meta/src/gen_binemit.rs deleted file mode 100644 index f67aa9b5a9..0000000000 --- a/cranelift/codegen/meta/src/gen_binemit.rs +++ /dev/null @@ -1,224 +0,0 @@ -//! Generate binary emission code for each ISA. - -use cranelift_entity::EntityRef; - -use crate::error; -use crate::srcgen::Formatter; - -use crate::cdsl::recipes::{EncodingRecipe, OperandConstraint, Recipes}; - -/// Generate code to handle a single recipe. -/// -/// - Unpack the instruction data, knowing the format. -/// - Determine register locations for operands with register constraints. -/// - Determine stack slot locations for operands with stack constraints. -/// - Call hand-written code for the actual emission. -fn gen_recipe(recipe: &EncodingRecipe, fmt: &mut Formatter) { - let inst_format = &recipe.format; - let num_value_ops = inst_format.num_value_operands; - - // TODO: Set want_args to true for only MultiAry instructions instead of all formats with value - // list. - let want_args = inst_format.has_value_list - || recipe.operands_in.iter().any(|c| match c { - OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true, - OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false, - }); - assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list); - - let want_outs = recipe.operands_out.iter().any(|c| match c { - OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true, - OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false, - }); - - let is_regmove = ["RegMove", "RegSpill", "RegFill"].contains(&inst_format.name); - - // Unpack the instruction data. - fmtln!(fmt, "if let InstructionData::{} {{", inst_format.name); - fmt.indent(|fmt| { - fmt.line("opcode,"); - for f in &inst_format.imm_fields { - fmtln!(fmt, "{},", f.member); - } - if want_args { - if inst_format.has_value_list || num_value_ops > 1 { - fmt.line("ref args,"); - } else { - fmt.line("arg,"); - } - } - fmt.line(".."); - - fmt.outdented_line("} = *inst_data {"); - - // Pass recipe arguments in this order: inputs, imm_fields, outputs. - let mut args = String::new(); - - if want_args && !is_regmove { - if inst_format.has_value_list { - fmt.line("let args = args.as_slice(&func.dfg.value_lists);"); - } else if num_value_ops == 1 { - fmt.line("let args = [arg];"); - } - args += &unwrap_values(&recipe.operands_in, "in", "args", fmt); - } - - for f in &inst_format.imm_fields { - args += &format!(", {}", f.member); - } - - // Unwrap interesting output arguments. - if want_outs { - if recipe.operands_out.len() == 1 { - fmt.line("let results = [func.dfg.first_result(inst)];") - } else { - fmt.line("let results = func.dfg.inst_results(inst);"); - } - args += &unwrap_values(&recipe.operands_out, "out", "results", fmt); - } - - // Optimization: Only update the register diversion tracker for regmove instructions. - if is_regmove { - fmt.line("divert.apply(inst_data);") - } - - match &recipe.emit { - Some(emit) => { - fmt.multi_line(emit); - fmt.line("return;"); - } - None => { - fmtln!( - fmt, - "return recipe_{}(func, inst, sink, bits{});", - recipe.name.to_lowercase(), - args - ); - } - } - }); - fmt.line("}"); -} - -/// Emit code that unwraps values living in registers or stack slots. -/// -/// :param args: Input or output constraints. -/// :param prefix: Prefix to be used for the generated local variables. -/// :param values: Name of slice containing the values to be unwrapped. -/// :returns: Comma separated list of the generated variables -fn unwrap_values( - args: &[OperandConstraint], - prefix: &str, - values_slice: &str, - fmt: &mut Formatter, -) -> String { - let mut varlist = String::new(); - for (i, cst) in args.iter().enumerate() { - match cst { - OperandConstraint::RegClass(_reg_class) => { - let v = format!("{}_reg{}", prefix, i); - varlist += &format!(", {}", v); - fmtln!( - fmt, - "let {} = divert.reg({}[{}], &func.locations);", - v, - values_slice, - i - ); - } - OperandConstraint::Stack(stack) => { - let v = format!("{}_stk{}", prefix, i); - varlist += &format!(", {}", v); - fmtln!(fmt, "let {} = StackRef::masked(", v); - fmt.indent(|fmt| { - fmtln!( - fmt, - "divert.stack({}[{}], &func.locations),", - values_slice, - i - ); - fmt.line(format!("{},", stack.stack_base_mask())); - fmt.line("&func.stack_slots,"); - }); - fmt.line(").unwrap();"); - } - _ => {} - } - } - varlist -} - -fn gen_isa(isa_name: &str, recipes: &Recipes, fmt: &mut Formatter) { - fmt.doc_comment(format!( - "Emit binary machine code for `inst` for the {} ISA.", - isa_name - )); - - if recipes.is_empty() { - fmt.line("pub fn emit_inst("); - fmt.indent(|fmt| { - fmt.line("func: &Function,"); - fmt.line("inst: Inst,"); - fmt.line("_divert: &mut RegDiversions,"); - fmt.line("_sink: &mut CS,"); - fmt.line("_isa: &dyn TargetIsa,"); - }); - fmt.line(") {"); - fmt.indent(|fmt| { - // No encoding recipes: Emit a stub. - fmt.line("bad_encoding(func, inst)"); - }); - fmt.line("}"); - return; - } - - fmt.line("#[allow(unused_variables, unreachable_code)]"); - fmt.line("pub fn emit_inst("); - fmt.indent(|fmt| { - fmt.line("func: &Function,"); - fmt.line("inst: Inst,"); - fmt.line("divert: &mut RegDiversions,"); - fmt.line("sink: &mut CS,"); - fmt.line("isa: &dyn TargetIsa,") - }); - - fmt.line(") {"); - fmt.indent(|fmt| { - fmt.line("let encoding = func.encodings[inst];"); - fmt.line("let bits = encoding.bits();"); - fmt.line("let inst_data = &func.dfg[inst];"); - fmt.line("match encoding.recipe() {"); - fmt.indent(|fmt| { - for (i, recipe) in recipes.iter() { - fmt.comment(format!("Recipe {}", recipe.name)); - fmtln!(fmt, "{} => {{", i.index()); - fmt.indent(|fmt| { - gen_recipe(recipe, fmt); - }); - fmt.line("}"); - } - fmt.line("_ => {},"); - }); - fmt.line("}"); - - // Allow for unencoded ghost instructions. The verifier will check details. - fmt.line("if encoding.is_legal() {"); - fmt.indent(|fmt| { - fmt.line("bad_encoding(func, inst);"); - }); - fmt.line("}"); - }); - fmt.line("}"); -} - -pub(crate) fn generate( - isa_name: &str, - recipes: &Recipes, - binemit_filename: &str, - out_dir: &str, -) -> Result<(), error::Error> { - let mut fmt = Formatter::new(); - gen_isa(isa_name, recipes, &mut fmt); - fmt.update_file(binemit_filename, out_dir)?; - Ok(()) -} diff --git a/cranelift/codegen/meta/src/gen_encodings.rs b/cranelift/codegen/meta/src/gen_encodings.rs deleted file mode 100644 index d7bb289bd2..0000000000 --- a/cranelift/codegen/meta/src/gen_encodings.rs +++ /dev/null @@ -1,1139 +0,0 @@ -//! Generate sources for instruction encoding. -//! -//! The tables and functions generated here support the `TargetISA::encode()` function which -//! determines if a given instruction is legal, and if so, its `Encoding` data which consists of a -//! *recipe* and some *encoding* bits. -//! -//! The `encode` function doesn't actually generate the binary machine bits. Each recipe has a -//! corresponding hand-written function to do that after registers are allocated. -//! -//! This is the information available to us: -//! -//! - The instruction to be encoded as an `InstructionData` reference. -//! - The controlling type variable. -//! - The data-flow graph giving us access to the types of all values involved. This is needed for -//! testing any secondary type variables. -//! - A `PredicateView` reference for the ISA-specific settings for evaluating ISA predicates. -//! - The currently active CPU mode is determined by the ISA. -//! -//! ## Level 1 table lookup -//! -//! The CPU mode provides the first table. The key is the instruction's controlling type variable. -//! If the instruction is not polymorphic, use `INVALID` for the type variable. The table values -//! are level 2 tables. -//! -//! ## Level 2 table lookup -//! -//! The level 2 table is keyed by the instruction's opcode. The table values are *encoding lists*. -//! -//! The two-level table lookup allows the level 2 tables to be much smaller with good locality. -//! Code in any given function usually only uses a few different types, so many of the level 2 -//! tables will be cold. -//! -//! ## Encoding lists -//! -//! An encoding list is a non-empty sequence of list entries. Each entry has one of these forms: -//! -//! 1. Recipe + bits. Use this encoding if the recipe predicate is satisfied. -//! 2. Recipe + bits, final entry. Use this encoding if the recipe predicate is satisfied. -//! Otherwise, stop with the default legalization code. -//! 3. Stop with legalization code. -//! 4. Predicate + skip count. Test predicate and skip N entries if it is false. -//! 5. Predicate + stop. Test predicate and stop with the default legalization code if it is false. -//! -//! The instruction predicate is also used to distinguish between polymorphic instructions with -//! different types for secondary type variables. - -use std::collections::btree_map; -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::convert::TryFrom; -use std::iter::FromIterator; - -use cranelift_codegen_shared::constant_hash::generate_table; -use cranelift_entity::EntityRef; - -use crate::error; -use crate::srcgen::Formatter; - -use crate::cdsl::cpu_modes::CpuMode; -use crate::cdsl::encodings::Encoding; -use crate::cdsl::instructions::{Instruction, InstructionPredicate, InstructionPredicateNumber}; -use crate::cdsl::isa::TargetIsa; -use crate::cdsl::recipes::{EncodingRecipe, OperandConstraint, Recipes, Register}; -use crate::cdsl::regs::IsaRegs; -use crate::cdsl::settings::SettingPredicateNumber; -use crate::cdsl::types::ValueType; -use crate::cdsl::xform::TransformGroupIndex; - -use crate::shared::Definitions as SharedDefinitions; - -use crate::default_map::MapWithDefault; -use crate::unique_table::UniqueSeqTable; - -/// Emit code for matching an instruction predicate against an `InstructionData` reference called -/// `inst`. -/// -/// The generated code is an `if let` pattern match that falls through if the instruction has an -/// unexpected format. This should lead to a panic. -fn emit_instp(instp: &InstructionPredicate, has_func: bool, fmt: &mut Formatter) { - if let Some(type_predicate) = instp.type_predicate("func") { - fmt.line("let args = inst.arguments(&func.dfg.value_lists);"); - fmt.line(type_predicate); - return; - } - - let leaves = instp.collect_leaves(); - - let mut has_type_check = false; - let mut format_name = None; - let mut field_names = HashSet::new(); - - for leaf in leaves { - if leaf.is_type_predicate() { - has_type_check = true; - } else { - field_names.insert(leaf.format_destructuring_member_name()); - let leaf_format_name = leaf.format_name(); - match format_name { - None => format_name = Some(leaf_format_name), - Some(previous_format_name) => { - assert!( - previous_format_name == leaf_format_name, - "Format predicate can only operate on a single InstructionFormat; trying to use both {} and {}", previous_format_name, leaf_format_name - ); - } - } - } - } - - let mut fields = Vec::from_iter(field_names); - fields.sort(); - let fields = fields.join(", "); - - let format_name = format_name.expect("There should be a format name!"); - - fmtln!( - fmt, - "if let crate::ir::InstructionData::{} {{ {}, .. }} = *inst {{", - format_name, - fields - ); - fmt.indent(|fmt| { - if has_type_check { - // We could implement this. - assert!(has_func, "recipe predicates can't check type variables."); - fmt.line("let args = inst.arguments(&func.dfg.value_lists);"); - } else if has_func { - // Silence dead argument. - fmt.line("let _ = func;"); - } - fmtln!(fmt, "return {};", instp.rust_predicate("func").unwrap()); - }); - fmtln!(fmt, "}"); - - fmt.line("unreachable!();"); -} - -/// Emit private functions for checking recipe predicates as well as a static `RECIPE_PREDICATES` -/// array indexed by recipe number. -/// -/// A recipe predicate is a combination of an ISA predicate and an instruction predicate. Many -/// recipes have identical predicates. -fn emit_recipe_predicates(isa: &TargetIsa, fmt: &mut Formatter) { - let mut predicate_names = HashMap::new(); - - fmt.comment(format!("{} recipe predicates.", isa.name)); - for recipe in isa.recipes.values() { - let (isap, instp) = match (&recipe.isa_predicate, &recipe.inst_predicate) { - (None, None) => continue, - (isap, instp) if predicate_names.contains_key(&(isap, instp)) => continue, - (isap, instp) => (isap, instp), - }; - - let func_name = format!("recipe_predicate_{}", recipe.name.to_lowercase()); - predicate_names.insert((isap, instp), func_name.clone()); - - // Generate the predicate function. - fmtln!( - fmt, - "fn {}({}: crate::settings::PredicateView, {}: &ir::InstructionData) -> bool {{", - func_name, - if isap.is_some() { "isap" } else { "_" }, - if instp.is_some() { "inst" } else { "_" } - ); - fmt.indent(|fmt| { - match (isap, instp) { - (Some(isap), None) => { - fmtln!(fmt, "isap.test({})", isap); - } - (None, Some(instp)) => { - emit_instp(instp, /* has func */ false, fmt); - } - (Some(isap), Some(instp)) => { - fmtln!(fmt, "isap.test({}) &&", isap); - emit_instp(instp, /* has func */ false, fmt); - } - _ => panic!("skipped above"), - } - }); - fmtln!(fmt, "}"); - } - fmt.empty_line(); - - // Generate the static table. - fmt.doc_comment(format!( - r#"{} recipe predicate table. - - One entry per recipe, set to Some only when the recipe is guarded by a predicate."#, - isa.name - )); - fmtln!( - fmt, - "pub static RECIPE_PREDICATES: [RecipePredicate; {}] = [", - isa.recipes.len() - ); - fmt.indent(|fmt| { - for recipe in isa.recipes.values() { - match (&recipe.isa_predicate, &recipe.inst_predicate) { - (None, None) => fmt.line("None,"), - key => fmtln!(fmt, "Some({}),", predicate_names.get(&key).unwrap()), - } - } - }); - fmtln!(fmt, "];"); - fmt.empty_line(); -} - -/// Emit private functions for matching instruction predicates as well as a static -/// `INST_PREDICATES` array indexed by predicate number. -fn emit_inst_predicates(isa: &TargetIsa, fmt: &mut Formatter) { - fmt.comment(format!("{} instruction predicates.", isa.name)); - for (id, instp) in isa.encodings_predicates.iter() { - fmtln!(fmt, "fn inst_predicate_{}(func: &crate::ir::Function, inst: &crate::ir::InstructionData) -> bool {{", id.index()); - fmt.indent(|fmt| { - emit_instp(instp, /* has func */ true, fmt); - }); - fmtln!(fmt, "}"); - } - fmt.empty_line(); - - // Generate the static table. - fmt.doc_comment(format!( - r#"{} instruction predicate table. - - One entry per instruction predicate, so the encoding bytecode can embed indexes into this - table."#, - isa.name - )); - fmtln!( - fmt, - "pub static INST_PREDICATES: [InstPredicate; {}] = [", - isa.encodings_predicates.len() - ); - fmt.indent(|fmt| { - for id in isa.encodings_predicates.keys() { - fmtln!(fmt, "inst_predicate_{},", id.index()); - } - }); - fmtln!(fmt, "];"); - fmt.empty_line(); -} - -/// Emit a table of encoding recipe names keyed by recipe number. -/// -/// This is used for pretty-printing encodings. -fn emit_recipe_names(isa: &TargetIsa, fmt: &mut Formatter) { - fmt.doc_comment(format!( - r#"{} recipe names, using the same recipe index spaces as the one specified by the - corresponding binemit file."#, - isa.name - )); - fmtln!( - fmt, - "static RECIPE_NAMES: [&str; {}] = [", - isa.recipes.len() - ); - fmt.indent(|fmt| { - for recipe in isa.recipes.values() { - fmtln!(fmt, r#""{}","#, recipe.name); - } - }); - fmtln!(fmt, "];"); - fmt.empty_line(); -} - -/// Returns a set of all the registers involved in fixed register constraints. -fn get_fixed_registers(operands_in: &[OperandConstraint]) -> HashSet { - HashSet::from_iter( - operands_in - .iter() - .map(|constraint| { - if let OperandConstraint::FixedReg(reg) = &constraint { - Some(*reg) - } else { - None - } - }) - .filter(|opt| opt.is_some()) - .map(|opt| opt.unwrap()), - ) -} - -/// Emit a struct field initializer for an array of operand constraints. -/// -/// Note "fixed_registers" must refer to the other kind of operands (i.e. if we're operating on -/// inputs, fixed_registers must contain the fixed output registers). -fn emit_operand_constraints( - registers: &IsaRegs, - recipe: &EncodingRecipe, - constraints: &[OperandConstraint], - field_name: &'static str, - tied_operands: &HashMap, - fixed_registers: &HashSet, - fmt: &mut Formatter, -) { - if constraints.is_empty() { - fmtln!(fmt, "{}: &[],", field_name); - return; - } - - fmtln!(fmt, "{}: &[", field_name); - fmt.indent(|fmt| { - for (n, constraint) in constraints.iter().enumerate() { - fmt.line("OperandConstraint {"); - fmt.indent(|fmt| { - match constraint { - OperandConstraint::RegClass(reg_class) => { - if let Some(tied_input) = tied_operands.get(&n) { - fmtln!(fmt, "kind: ConstraintKind::Tied({}),", tied_input); - } else { - fmt.line("kind: ConstraintKind::Reg,"); - } - fmtln!( - fmt, - "regclass: &{}_DATA,", - registers.classes[*reg_class].name - ); - } - OperandConstraint::FixedReg(reg) => { - assert!(!tied_operands.contains_key(&n), "can't tie fixed registers"); - let constraint_kind = if fixed_registers.contains(®) { - "FixedTied" - } else { - "FixedReg" - }; - fmtln!( - fmt, - "kind: ConstraintKind::{}({}),", - constraint_kind, - reg.unit - ); - fmtln!( - fmt, - "regclass: &{}_DATA,", - registers.classes[reg.regclass].name - ); - } - OperandConstraint::TiedInput(tied_input) => { - // This is a tied output constraint. It should never happen - // for input constraints. - assert!( - tied_input == tied_operands.get(&n).unwrap(), - "invalid tied constraint" - ); - fmtln!(fmt, "kind: ConstraintKind::Tied({}),", tied_input); - - let tied_class = if let OperandConstraint::RegClass(tied_class) = - recipe.operands_in[*tied_input] - { - tied_class - } else { - panic!("tied constraints relate only to register inputs"); - }; - - fmtln!( - fmt, - "regclass: &{}_DATA,", - registers.classes[tied_class].name - ); - } - OperandConstraint::Stack(stack) => { - assert!(!tied_operands.contains_key(&n), "can't tie stack operand"); - fmt.line("kind: ConstraintKind::Stack,"); - fmtln!( - fmt, - "regclass: &{}_DATA,", - registers.classes[stack.regclass].name - ); - } - } - }); - fmt.line("},"); - } - }); - fmtln!(fmt, "],"); -} - -/// Emit a table of encoding recipe operand constraints keyed by recipe number. -/// -/// These are used by the register allocator to pick registers that can be properly encoded. -fn emit_recipe_constraints(isa: &TargetIsa, fmt: &mut Formatter) { - fmt.doc_comment(format!( - r#"{} recipe constraints list, using the same recipe index spaces as the one - specified by the corresponding binemit file. These constraints are used by register - allocation to select the right location to use for input and output values."#, - isa.name - )); - fmtln!( - fmt, - "static RECIPE_CONSTRAINTS: [RecipeConstraints; {}] = [", - isa.recipes.len() - ); - fmt.indent(|fmt| { - for recipe in isa.recipes.values() { - // Compute a mapping of tied operands in both directions (input tied to outputs and - // conversely). - let mut tied_in_to_out = HashMap::new(); - let mut tied_out_to_in = HashMap::new(); - for (out_index, constraint) in recipe.operands_out.iter().enumerate() { - if let OperandConstraint::TiedInput(in_index) = &constraint { - tied_in_to_out.insert(*in_index, out_index); - tied_out_to_in.insert(out_index, *in_index); - } - } - - // Find the sets of registers involved in fixed register constraints. - let fixed_inputs = get_fixed_registers(&recipe.operands_in); - let fixed_outputs = get_fixed_registers(&recipe.operands_out); - - fmt.comment(format!("Constraints for recipe {}:", recipe.name)); - fmt.line("RecipeConstraints {"); - fmt.indent(|fmt| { - emit_operand_constraints( - &isa.regs, - recipe, - &recipe.operands_in, - "ins", - &tied_in_to_out, - &fixed_outputs, - fmt, - ); - emit_operand_constraints( - &isa.regs, - recipe, - &recipe.operands_out, - "outs", - &tied_out_to_in, - &fixed_inputs, - fmt, - ); - fmtln!( - fmt, - "fixed_ins: {},", - if !fixed_inputs.is_empty() { - "true" - } else { - "false" - } - ); - fmtln!( - fmt, - "fixed_outs: {},", - if !fixed_outputs.is_empty() { - "true" - } else { - "false" - } - ); - fmtln!( - fmt, - "tied_ops: {},", - if !tied_in_to_out.is_empty() { - "true" - } else { - "false" - } - ); - fmtln!( - fmt, - "clobbers_flags: {},", - if recipe.clobbers_flags { - "true" - } else { - "false" - } - ); - }); - fmt.line("},"); - } - }); - fmtln!(fmt, "];"); - fmt.empty_line(); -} - -/// Emit a table of encoding recipe code size information. -fn emit_recipe_sizing(isa: &TargetIsa, fmt: &mut Formatter) { - fmt.doc_comment(format!( - r#"{} recipe sizing descriptors, using the same recipe index spaces as the one - specified by the corresponding binemit file. These are used to compute the final size of an - instruction, as well as to compute the range of branches."#, - isa.name - )); - fmtln!( - fmt, - "static RECIPE_SIZING: [RecipeSizing; {}] = [", - isa.recipes.len() - ); - fmt.indent(|fmt| { - for recipe in isa.recipes.values() { - fmt.comment(format!("Code size information for recipe {}:", recipe.name)); - fmt.line("RecipeSizing {"); - fmt.indent(|fmt| { - fmtln!(fmt, "base_size: {},", recipe.base_size); - fmtln!(fmt, "compute_size: {},", recipe.compute_size); - if let Some(range) = &recipe.branch_range { - fmtln!( - fmt, - "branch_range: Some(BranchRange {{ origin: {}, bits: {} }}),", - range.inst_size, - range.range - ); - } else { - fmt.line("branch_range: None,"); - } - }); - fmt.line("},"); - } - }); - fmtln!(fmt, "];"); - fmt.empty_line(); -} - -/// Level 1 table mapping types to `Level2` objects. -struct Level1Table<'cpu_mode> { - cpu_mode: &'cpu_mode CpuMode, - legalize_code: TransformGroupIndex, - - table_map: HashMap, usize>, - table_vec: Vec, -} - -impl<'cpu_mode> Level1Table<'cpu_mode> { - fn new(cpu_mode: &'cpu_mode CpuMode) -> Self { - Self { - cpu_mode, - legalize_code: cpu_mode.get_default_legalize_code(), - table_map: HashMap::new(), - table_vec: Vec::new(), - } - } - - /// Returns the level2 table for the given type; None means monomorphic, in this context. - fn l2table_for(&mut self, typ: Option) -> &mut Level2Table { - let cpu_mode = &self.cpu_mode; - let index = match self.table_map.get(&typ) { - Some(&index) => index, - None => { - let legalize_code = cpu_mode.get_legalize_code_for(&typ); - let table = Level2Table::new(typ.clone(), legalize_code); - let index = self.table_vec.len(); - self.table_map.insert(typ, index); - self.table_vec.push(table); - index - } - }; - self.table_vec.get_mut(index).unwrap() - } - - fn l2tables(&mut self) -> Vec<&mut Level2Table> { - self.table_vec - .iter_mut() - .filter(|table| !table.is_empty()) - .collect::>() - } -} - -struct Level2HashTableEntry { - inst_name: String, - offset: usize, -} - -/// Level 2 table mapping instruction opcodes to `EncList` objects. -/// -/// A level 2 table can be completely empty if it only holds a custom legalization action for `ty`. -struct Level2Table { - typ: Option, - legalize_code: TransformGroupIndex, - inst_to_encodings: BTreeMap, - hash_table_offset: Option, - hash_table_len: Option, -} - -impl Level2Table { - fn new(typ: Option, legalize_code: TransformGroupIndex) -> Self { - Self { - typ, - legalize_code, - inst_to_encodings: BTreeMap::new(), - hash_table_offset: None, - hash_table_len: None, - } - } - - fn enclist_for(&mut self, inst: &Instruction) -> &mut EncodingList { - let copied_typ = self.typ.clone(); - self.inst_to_encodings - .entry(inst.name.clone()) - .or_insert_with(|| EncodingList::new(inst, copied_typ)) - } - - fn enclists(&mut self) -> btree_map::ValuesMut<'_, String, EncodingList> { - self.inst_to_encodings.values_mut() - } - - fn is_empty(&self) -> bool { - self.inst_to_encodings.is_empty() - } - - fn layout_hashtable( - &mut self, - level2_hashtables: &mut Vec>, - level2_doc: &mut HashMap>, - ) { - let hash_table = generate_table( - self.inst_to_encodings.values(), - self.inst_to_encodings.len(), - // TODO the Python code wanted opcode numbers to start from 1. - |enc_list| enc_list.inst.opcode_number.index() + 1, - ); - - let hash_table_offset = level2_hashtables.len(); - let hash_table_len = hash_table.len(); - - assert!(self.hash_table_offset.is_none()); - assert!(self.hash_table_len.is_none()); - self.hash_table_offset = Some(hash_table_offset); - self.hash_table_len = Some(hash_table_len); - - level2_hashtables.extend(hash_table.iter().map(|opt_enc_list| { - opt_enc_list.map(|enc_list| Level2HashTableEntry { - inst_name: enc_list.inst.camel_name.clone(), - offset: enc_list.offset.unwrap(), - }) - })); - - let typ_comment = match &self.typ { - Some(ty) => ty.to_string(), - None => "typeless".into(), - }; - - level2_doc.get_or_default(hash_table_offset).push(format!( - "{:06x}: {}, {} entries", - hash_table_offset, typ_comment, hash_table_len - )); - } -} - -/// The u16 values in an encoding list entry are interpreted as follows: -/// -/// NR = len(all_recipes) -/// -/// entry < 2*NR -/// Try Encoding(entry/2, next_entry) if the recipe predicate is satisfied. -/// If bit 0 is set, stop with the default legalization code. -/// If bit 0 is clear, keep going down the list. -/// entry < PRED_START -/// Stop with legalization code `entry - 2*NR`. -/// -/// Remaining entries are interpreted as (skip, pred) pairs, where: -/// -/// skip = (entry - PRED_START) >> PRED_BITS -/// pred = (entry - PRED_START) & PRED_MASK -/// -/// If the predicate is satisfied, keep going. Otherwise skip over the next -/// `skip` entries. If skip == 0, stop with the default legalization code. -/// -/// The `pred` predicate number is interpreted as an instruction predicate if it -/// is in range, otherwise an ISA predicate. - -/// Encoding lists are represented as u16 arrays. -const CODE_BITS: usize = 16; - -/// Beginning of the predicate code words. -const PRED_START: u16 = 0x1000; - -/// Number of bits used to hold a predicate number (instruction + ISA predicates). -const PRED_BITS: usize = 12; - -/// Mask for extracting the predicate number. -const PRED_MASK: usize = (1 << PRED_BITS) - 1; - -/// Encoder for the list format above. -struct Encoder { - num_instruction_predicates: usize, - - /// u16 encoding list words. - words: Vec, - - /// Documentation comments: Index into `words` + comment. - docs: Vec<(usize, String)>, -} - -impl Encoder { - fn new(num_instruction_predicates: usize) -> Self { - Self { - num_instruction_predicates, - words: Vec::new(), - docs: Vec::new(), - } - } - - /// Add a recipe+bits entry to the list. - fn recipe(&mut self, recipes: &Recipes, enc: &Encoding, is_final: bool) { - let code = (2 * enc.recipe.index() + if is_final { 1 } else { 0 }) as u16; - assert!(code < PRED_START); - - let doc = format!( - "--> {}{}", - enc.to_rust_comment(recipes), - if is_final { " and stop" } else { "" } - ); - self.docs.push((self.words.len(), doc)); - - self.words.push(code); - self.words.push(enc.encbits); - } - - /// Add a predicate entry. - fn pred(&mut self, pred_comment: String, skip: usize, n: usize) { - assert!(n <= PRED_MASK); - let entry = (PRED_START as usize) + (n | (skip << PRED_BITS)); - assert!(entry < (1 << CODE_BITS)); - let entry = entry as u16; - - let doc = if skip == 0 { - "stop".to_string() - } else { - format!("skip {}", skip) - }; - let doc = format!("{} unless {}", doc, pred_comment); - - self.docs.push((self.words.len(), doc)); - self.words.push(entry); - } - - /// Add an instruction predicate entry. - fn inst_predicate(&mut self, pred: InstructionPredicateNumber, skip: usize) { - let number = pred.index(); - let pred_comment = format!("inst_predicate_{}", number); - self.pred(pred_comment, skip, number); - } - - /// Add an ISA predicate entry. - fn isa_predicate(&mut self, pred: SettingPredicateNumber, skip: usize) { - // ISA predicates follow the instruction predicates. - let n = self.num_instruction_predicates + (pred as usize); - let pred_comment = format!("PredicateView({})", pred); - self.pred(pred_comment, skip, n); - } -} - -/// List of instructions for encoding a given type + opcode pair. -/// -/// An encoding list contains a sequence of predicates and encoding recipes, all encoded as u16 -/// values. -struct EncodingList { - inst: Instruction, - typ: Option, - encodings: Vec, - offset: Option, -} - -impl EncodingList { - fn new(inst: &Instruction, typ: Option) -> Self { - Self { - inst: inst.clone(), - typ, - encodings: Default::default(), - offset: None, - } - } - - /// Encode this list as a sequence of u16 numbers. - /// - /// Adds the sequence to `enc_lists` and records the returned offset as - /// `self.offset`. - /// - /// Adds comment lines to `enc_lists_doc` keyed by enc_lists offsets. - fn encode( - &mut self, - isa: &TargetIsa, - cpu_mode: &CpuMode, - enc_lists: &mut UniqueSeqTable, - enc_lists_doc: &mut HashMap>, - ) { - assert!(!self.encodings.is_empty()); - - let mut encoder = Encoder::new(isa.encodings_predicates.len()); - - let mut index = 0; - while index < self.encodings.len() { - let encoding = &self.encodings[index]; - - // Try to see how many encodings are following and have the same ISA predicate and - // instruction predicate, so as to reduce the number of tests carried out by the - // encoding list interpreter.. - // - // Encodings with similar tests are hereby called a group. The group includes the - // current encoding we're looking at. - let (isa_predicate, inst_predicate) = - (&encoding.isa_predicate, &encoding.inst_predicate); - - let group_size = { - let mut group_size = 1; - while index + group_size < self.encodings.len() { - let next_encoding = &self.encodings[index + group_size]; - if &next_encoding.inst_predicate != inst_predicate - || &next_encoding.isa_predicate != isa_predicate - { - break; - } - group_size += 1; - } - group_size - }; - - let is_last_group = index + group_size == self.encodings.len(); - - // The number of entries to skip when a predicate isn't satisfied is the size of both - // predicates + the size of the group, minus one (for this predicate). Each recipe - // entry has a size of two u16 (recipe index + bits). - let mut skip = if is_last_group { - 0 - } else { - let isap_size = match isa_predicate { - Some(_) => 1, - None => 0, - }; - let instp_size = match inst_predicate { - Some(_) => 1, - None => 0, - }; - isap_size + instp_size + group_size * 2 - 1 - }; - - if let Some(pred) = isa_predicate { - encoder.isa_predicate(*pred, skip); - if !is_last_group { - skip -= 1; - } - } - - if let Some(pred) = inst_predicate { - encoder.inst_predicate(*pred, skip); - // No need to update skip, it's dead after this point. - } - - for i in 0..group_size { - let encoding = &self.encodings[index + i]; - let is_last_encoding = index + i == self.encodings.len() - 1; - encoder.recipe(&isa.recipes, encoding, is_last_encoding); - } - - index += group_size; - } - - assert!(self.offset.is_none()); - let offset = enc_lists.add(&encoder.words); - self.offset = Some(offset); - - // Doc comments. - let recipe_typ_mode_name = format!( - "{}{} ({})", - self.inst.name, - if let Some(typ) = &self.typ { - format!(".{}", typ.to_string()) - } else { - "".into() - }, - cpu_mode.name - ); - - enc_lists_doc - .get_or_default(offset) - .push(format!("{:06x}: {}", offset, recipe_typ_mode_name)); - for (pos, doc) in encoder.docs { - enc_lists_doc.get_or_default(offset + pos).push(doc); - } - enc_lists_doc - .get_or_default(offset + encoder.words.len()) - .insert(0, format!("end of {}", recipe_typ_mode_name)); - } -} - -fn make_tables(cpu_mode: &CpuMode) -> Level1Table { - let mut table = Level1Table::new(cpu_mode); - - for encoding in &cpu_mode.encodings { - table - .l2table_for(encoding.bound_type.clone()) - .enclist_for(encoding.inst()) - .encodings - .push(encoding.clone()); - } - - // Ensure there are level 1 table entries for all types with a custom legalize action. - for value_type in cpu_mode.get_legalized_types() { - table.l2table_for(Some(value_type.clone())); - } - // ... and also for monomorphic instructions. - table.l2table_for(None); - - table -} - -/// Compute encodings and doc comments for encoding lists in `level1`. -fn encode_enclists( - isa: &TargetIsa, - cpu_mode: &CpuMode, - level1: &mut Level1Table, - enc_lists: &mut UniqueSeqTable, - enc_lists_doc: &mut HashMap>, -) { - for level2 in level1.l2tables() { - for enclist in level2.enclists() { - enclist.encode(isa, cpu_mode, enc_lists, enc_lists_doc); - } - } -} - -fn encode_level2_hashtables<'a>( - level1: &'a mut Level1Table, - level2_hashtables: &mut Vec>, - level2_doc: &mut HashMap>, -) { - for level2 in level1.l2tables() { - level2.layout_hashtable(level2_hashtables, level2_doc); - } -} - -fn emit_encoding_tables(defs: &SharedDefinitions, isa: &TargetIsa, fmt: &mut Formatter) { - // Level 1 tables, one per CPU mode. - let mut level1_tables: HashMap<&'static str, Level1Table> = HashMap::new(); - - // Single table containing all the level2 hash tables. - let mut level2_hashtables = Vec::new(); - let mut level2_doc: HashMap> = HashMap::new(); - - // Tables for encoding lists with comments. - let mut enc_lists = UniqueSeqTable::new(); - let mut enc_lists_doc = HashMap::new(); - - for cpu_mode in &isa.cpu_modes { - level2_doc - .get_or_default(level2_hashtables.len()) - .push(cpu_mode.name.into()); - - let mut level1 = make_tables(cpu_mode); - - encode_enclists( - isa, - cpu_mode, - &mut level1, - &mut enc_lists, - &mut enc_lists_doc, - ); - encode_level2_hashtables(&mut level1, &mut level2_hashtables, &mut level2_doc); - - level1_tables.insert(cpu_mode.name, level1); - } - - // Compute an appropriate Rust integer type to use for offsets into a table of the given length. - let offset_type = |length: usize| { - if length <= 0x10000 { - "u16" - } else { - assert!(u32::try_from(length).is_ok(), "table too big!"); - "u32" - } - }; - - let level1_offset_type = offset_type(level2_hashtables.len()); - let level2_offset_type = offset_type(enc_lists.len()); - - // Emit encoding lists. - fmt.doc_comment( - format!(r#"{} encoding lists. - - This contains the entire encodings bytecode for every single instruction; the encodings - interpreter knows where to start from thanks to the initial lookup in the level 1 and level 2 - table entries below."#, isa.name) - ); - fmtln!(fmt, "pub static ENCLISTS: [u16; {}] = [", enc_lists.len()); - fmt.indent(|fmt| { - let mut line = Vec::new(); - for (index, entry) in enc_lists.iter().enumerate() { - if let Some(comments) = enc_lists_doc.get(&index) { - if !line.is_empty() { - fmtln!(fmt, "{},", line.join(", ")); - line.clear(); - } - for comment in comments { - fmt.comment(comment); - } - } - line.push(format!("{:#06x}", entry)); - } - if !line.is_empty() { - fmtln!(fmt, "{},", line.join(", ")); - } - }); - fmtln!(fmt, "];"); - fmt.empty_line(); - - // Emit the full concatenation of level 2 hash tables. - fmt.doc_comment(format!( - r#"{} level 2 hash tables. - - This hash table, keyed by instruction opcode, contains all the starting offsets for the - encodings interpreter, for all the CPU modes. It is jumped to after a lookup on the - instruction's controlling type in the level 1 hash table."#, - isa.name - )); - fmtln!( - fmt, - "pub static LEVEL2: [Level2Entry<{}>; {}] = [", - level2_offset_type, - level2_hashtables.len() - ); - fmt.indent(|fmt| { - for (offset, entry) in level2_hashtables.iter().enumerate() { - if let Some(comments) = level2_doc.get(&offset) { - for comment in comments { - fmt.comment(comment); - } - } - if let Some(entry) = entry { - fmtln!( - fmt, - "Level2Entry {{ opcode: Some(crate::ir::Opcode::{}), offset: {:#08x} }},", - entry.inst_name, - entry.offset - ); - } else { - fmt.line("Level2Entry { opcode: None, offset: 0 },"); - } - } - }); - fmtln!(fmt, "];"); - fmt.empty_line(); - - // Emit a level 1 hash table for each CPU mode. - for cpu_mode in &isa.cpu_modes { - let level1 = &level1_tables.get(cpu_mode.name).unwrap(); - let hash_table = generate_table( - level1.table_vec.iter(), - level1.table_vec.len(), - |level2_table| { - if let Some(typ) = &level2_table.typ { - typ.number().expect("type without a number") as usize - } else { - 0 - } - }, - ); - - fmt.doc_comment(format!( - r#"{} level 1 hash table for the CPU mode {}. - - This hash table, keyed by instruction controlling type, contains all the level 2 - hash-tables offsets for the given CPU mode, as well as a legalization identifier indicating - which legalization scheme to apply when the instruction doesn't have any valid encoding for - this CPU mode. - "#, - isa.name, cpu_mode.name - )); - fmtln!( - fmt, - "pub static LEVEL1_{}: [Level1Entry<{}>; {}] = [", - cpu_mode.name.to_uppercase(), - level1_offset_type, - hash_table.len() - ); - fmt.indent(|fmt| { - for opt_level2 in hash_table { - let level2 = match opt_level2 { - None => { - // Empty hash table entry. Include the default legalization action. - fmtln!(fmt, "Level1Entry {{ ty: ir::types::INVALID, log2len: !0, offset: 0, legalize: {} }},", - isa.translate_group_index(level1.legalize_code)); - continue; - } - Some(level2) => level2, - }; - - let legalize_comment = defs.transform_groups.get(level2.legalize_code).name; - let legalize_code = isa.translate_group_index(level2.legalize_code); - - let typ_name = if let Some(typ) = &level2.typ { - typ.rust_name() - } else { - "ir::types::INVALID".into() - }; - - if level2.is_empty() { - // Empty level 2 table: Only a specialized legalization action, no actual - // table. - // Set an offset that is out of bounds, but make sure it doesn't overflow its - // type when adding `1< 0, "Level2 hash table was too small."); - fmtln!(fmt, "Level1Entry {{ ty: {}, log2len: {}, offset: {:#08x}, legalize: {} }}, // {}", - typ_name, l2l, level2.hash_table_offset.unwrap(), legalize_code, legalize_comment); - } - }); - fmtln!(fmt, "];"); - fmt.empty_line(); - } -} - -fn gen_isa(defs: &SharedDefinitions, isa: &TargetIsa, fmt: &mut Formatter) { - // Make the `RECIPE_PREDICATES` table. - emit_recipe_predicates(isa, fmt); - - // Make the `INST_PREDICATES` table. - emit_inst_predicates(isa, fmt); - - emit_encoding_tables(defs, isa, fmt); - - emit_recipe_names(isa, fmt); - emit_recipe_constraints(isa, fmt); - emit_recipe_sizing(isa, fmt); - - // Finally, tie it all together in an `EncInfo`. - fmt.line("pub static INFO: isa::EncInfo = isa::EncInfo {"); - fmt.indent(|fmt| { - fmt.line("constraints: &RECIPE_CONSTRAINTS,"); - fmt.line("sizing: &RECIPE_SIZING,"); - fmt.line("names: &RECIPE_NAMES,"); - }); - fmt.line("};"); -} - -pub(crate) fn generate( - defs: &SharedDefinitions, - isa: &TargetIsa, - filename: &str, - out_dir: &str, -) -> Result<(), error::Error> { - let mut fmt = Formatter::new(); - gen_isa(defs, isa, &mut fmt); - fmt.update_file(filename, out_dir)?; - Ok(()) -} diff --git a/cranelift/codegen/meta/src/lib.rs b/cranelift/codegen/meta/src/lib.rs index 88f7ed6b09..bfa1cd0056 100644 --- a/cranelift/codegen/meta/src/lib.rs +++ b/cranelift/codegen/meta/src/lib.rs @@ -7,8 +7,6 @@ mod srcgen; pub mod error; pub mod isa; -mod gen_binemit; -mod gen_encodings; mod gen_inst; mod gen_legalizer; mod gen_registers; @@ -81,20 +79,6 @@ pub fn generate( &format!("settings-{}.rs", isa.name), &out_dir, )?; - - gen_encodings::generate( - &shared_defs, - &isa, - &format!("encoding-{}.rs", isa.name), - &out_dir, - )?; - - gen_binemit::generate( - &isa.name, - &isa.recipes, - &format!("binemit-{}.rs", isa.name), - &out_dir, - )?; } for isa in new_backend_isas {