Remove encoding generation from cranelift-codegen-meta

This commit is contained in:
bjorn3
2021-06-21 12:13:20 +02:00
parent d8818c967e
commit d499933612
9 changed files with 1 additions and 1529 deletions

View File

@@ -1,7 +1,6 @@
use std::collections::{hash_map, HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::iter::FromIterator; use std::iter::FromIterator;
use crate::cdsl::encodings::Encoding;
use crate::cdsl::types::ValueType; use crate::cdsl::types::ValueType;
use crate::cdsl::xform::TransformGroupIndex; use crate::cdsl::xform::TransformGroupIndex;
@@ -10,30 +9,9 @@ pub(crate) struct CpuMode {
default_legalize: Option<TransformGroupIndex>, default_legalize: Option<TransformGroupIndex>,
monomorphic_legalize: Option<TransformGroupIndex>, monomorphic_legalize: Option<TransformGroupIndex>,
typed_legalize: HashMap<ValueType, TransformGroupIndex>, typed_legalize: HashMap<ValueType, TransformGroupIndex>,
pub encodings: Vec<Encoding>,
} }
impl CpuMode { 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<ValueType>) -> 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<ValueType, TransformGroupIndex> {
self.typed_legalize.keys()
}
/// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the directly /// Returns a deterministically ordered, deduplicated list of TransformGroupIndex for the directly
/// reachable set of TransformGroup this TargetIsa uses. /// reachable set of TransformGroup this TargetIsa uses.
pub fn direct_transform_groups(&self) -> Vec<TransformGroupIndex> { pub fn direct_transform_groups(&self) -> Vec<TransformGroupIndex> {

View File

@@ -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<InstructionPredicateNumber>,
/// An ISA predicate that must be true to allow selecting this encoding.
pub isa_predicate: Option<SettingPredicateNumber>,
/// The value type this encoding has been bound to, for encodings of polymorphic instructions.
pub bound_type: Option<ValueType>,
}
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<EncodingContent>;

View File

@@ -745,12 +745,6 @@ impl FormatPredicateNode {
} }
} }
fn destructuring_member_name(&self) -> &'static str {
match &self.kind {
_ => self.member_name,
}
}
fn rust_predicate(&self) -> String { fn rust_predicate(&self) -> String {
match &self.kind { match &self.kind {
FormatPredicateKind::IsEqual(arg) => { FormatPredicateKind::IsEqual(arg) => {
@@ -808,44 +802,6 @@ impl InstructionPredicateNode {
.join(" && "), .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)] #[derive(Clone, Hash, PartialEq, Eq)]
@@ -918,22 +874,6 @@ impl InstructionPredicate {
pub fn rust_predicate(&self, func_str: &str) -> Option<String> { pub fn rust_predicate(&self, func_str: &str) -> Option<String> {
self.node.as_ref().map(|root| root.rust_predicate(func_str)) 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<String> {
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)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]

View File

@@ -86,11 +86,4 @@ impl TargetIsa {
pub fn direct_transform_groups(&self) -> &Vec<TransformGroupIndex> { pub fn direct_transform_groups(&self) -> &Vec<TransformGroupIndex> {
&self.local_transform_groups &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!")
}
} }

View File

@@ -6,7 +6,6 @@
#[macro_use] #[macro_use]
pub mod ast; pub mod ast;
pub mod cpu_modes; pub mod cpu_modes;
pub mod encodings;
pub mod formats; pub mod formats;
pub mod instructions; pub mod instructions;
pub mod isa; pub mod isa;

View File

@@ -32,13 +32,6 @@ pub(crate) struct Stack {
pub regclass: RegClassIndex, 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)] #[derive(Clone, Hash, PartialEq)]
pub(crate) struct BranchRange { pub(crate) struct BranchRange {
pub inst_size: u64, pub inst_size: u64,

View File

@@ -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<CS: CodeSink + ?Sized>(");
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<CS: CodeSink + ?Sized>(");
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(())
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,6 @@ mod srcgen;
pub mod error; pub mod error;
pub mod isa; pub mod isa;
mod gen_binemit;
mod gen_encodings;
mod gen_inst; mod gen_inst;
mod gen_legalizer; mod gen_legalizer;
mod gen_registers; mod gen_registers;
@@ -81,20 +79,6 @@ pub fn generate(
&format!("settings-{}.rs", isa.name), &format!("settings-{}.rs", isa.name),
&out_dir, &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 { for isa in new_backend_isas {