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 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> {
|
||||||
|
|||||||
@@ -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 {
|
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)]
|
||||||
|
|||||||
@@ -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!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user