Remove encoding generation from cranelift-codegen-meta
This commit is contained in:
@@ -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<TransformGroupIndex>,
|
||||
monomorphic_legalize: Option<TransformGroupIndex>,
|
||||
typed_legalize: HashMap<ValueType, TransformGroupIndex>,
|
||||
pub encodings: Vec<Encoding>,
|
||||
}
|
||||
|
||||
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
|
||||
/// reachable set of TransformGroup this TargetIsa uses.
|
||||
pub fn direct_transform_groups(&self) -> Vec<TransformGroupIndex> {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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<String> {
|
||||
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)]
|
||||
|
||||
@@ -86,11 +86,4 @@ impl TargetIsa {
|
||||
pub fn direct_transform_groups(&self) -> &Vec<TransformGroupIndex> {
|
||||
&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!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user