egraph-based midend: draw the rest of the owl (productionized). (#4953)
* egraph-based midend: draw the rest of the owl. * Rename `egg` submodule of cranelift-codegen to `egraph`. * Apply some feedback from @jsharp during code walkthrough. * Remove recursion from find_best_node by doing a single pass. Rather than recursively computing the lowest-cost node for a given eclass and memoizing the answer at each eclass node, we can do a single forward pass; because every eclass node refers only to earlier nodes, this is sufficient. The behavior may slightly differ from the earlier behavior because we cannot short-circuit costs to zero once a node is elaborated; but in practice this should not matter. * Make elaboration non-recursive. Use an explicit stack instead (with `ElabStackEntry` entries, alongside a result stack). * Make elaboration traversal of the domtree non-recursive/stack-safe. * Work analysis logic in Cranelift-side egraph glue into a general analysis framework in cranelift-egraph. * Apply static recursion limit to rule application. * Fix aarch64 wrt dynamic-vector support -- broken rebase. * Topo-sort cranelift-egraph before cranelift-codegen in publish script, like the comment instructs me to! * Fix multi-result call testcase. * Include `cranelift-egraph` in `PUBLISHED_CRATES`. * Fix atomic_rmw: not really a load. * Remove now-unnecessary PartialOrd/Ord derivations. * Address some code-review comments. * Review feedback. * Review feedback. * No overlap in mid-end rules, because we are defining a multi-constructor. * rustfmt * Review feedback. * Review feedback. * Review feedback. * Review feedback. * Remove redundant `mut`. * Add comment noting what rules can do. * Review feedback. * Clarify comment wording. * Update `has_memory_fence_semantics`. * Apply @jameysharp's improved loop-level computation. Co-authored-by: Jamey Sharp <jamey@minilop.net> * Fix suggestion commit. * Fix off-by-one in new loop-nest analysis. * Review feedback. * Review feedback. * Review feedback. * Use `Default`, not `std::default::Default`, as per @fitzgen Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com> * Apply @fitzgen's comment elaboration to a doc-comment. Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com> * Add stat for hitting the rewrite-depth limit. * Some code motion in split prelude to make the diff a little clearer wrt `main`. * Take @jameysharp's suggested `try_into()` usage for blockparam indices. Co-authored-by: Jamey Sharp <jamey@minilop.net> * Take @jameysharp's suggestion to avoid double-match on load op. Co-authored-by: Jamey Sharp <jamey@minilop.net> * Fix suggestion (add import). * Review feedback. * Fix stack_load handling. * Remove redundant can_store case. * Take @jameysharp's suggested improvement to FuncEGraph::build() logic Co-authored-by: Jamey Sharp <jamey@minilop.net> * Tweaks to FuncEGraph::build() on top of suggestion. * Take @jameysharp's suggested clarified condition Co-authored-by: Jamey Sharp <jamey@minilop.net> * Clean up after suggestion (unused variable). * Fix loop analysis. * loop level asserts * Revert constant-space loop analysis -- edge cases were incorrect, so let's go with the simple thing for now. * Take @jameysharp's suggestion re: result_tys Co-authored-by: Jamey Sharp <jamey@minilop.net> * Fix up after suggestion * Take @jameysharp's suggestion to use fold rather than reduce Co-authored-by: Jamey Sharp <jamey@minilop.net> * Fixup after suggestion * Take @jameysharp's suggestion to remove elaborate_eclass_use's return value. * Clarifying comment in terminator insts. Co-authored-by: Jamey Sharp <jamey@minilop.net> Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com>
This commit is contained in:
@@ -60,36 +60,52 @@ fn gen_formats(formats: &[&InstructionFormat], fmt: &mut Formatter) {
|
||||
fmt.empty_line();
|
||||
}
|
||||
|
||||
/// Generate the InstructionData enum.
|
||||
/// Generate the InstructionData and InstructionImms enums.
|
||||
///
|
||||
/// Every variant must contain an `opcode` field. The size of `InstructionData` should be kept at
|
||||
/// 16 bytes on 64-bit architectures. If more space is needed to represent an instruction, use a
|
||||
/// `ValueList` to store the additional information out of line.
|
||||
///
|
||||
/// `InstructionImms` stores everything about an instruction except for the arguments: in other
|
||||
/// words, the `Opcode` and any immediates or other parameters. `InstructionData` stores this, plus
|
||||
/// the SSA `Value` arguments.
|
||||
fn gen_instruction_data(formats: &[&InstructionFormat], fmt: &mut Formatter) {
|
||||
fmt.line("#[derive(Clone, Debug, PartialEq, Hash)]");
|
||||
fmt.line(r#"#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]"#);
|
||||
fmt.line("#[allow(missing_docs)]");
|
||||
fmt.line("pub enum InstructionData {");
|
||||
fmt.indent(|fmt| {
|
||||
for format in formats {
|
||||
fmtln!(fmt, "{} {{", format.name);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("opcode: Opcode,");
|
||||
if format.has_value_list {
|
||||
fmt.line("args: ValueList,");
|
||||
} else if format.num_value_operands == 1 {
|
||||
fmt.line("arg: Value,");
|
||||
} else if format.num_value_operands > 0 {
|
||||
fmtln!(fmt, "args: [Value; {}],", format.num_value_operands);
|
||||
}
|
||||
for field in &format.imm_fields {
|
||||
fmtln!(fmt, "{}: {},", field.member, field.kind.rust_type);
|
||||
}
|
||||
});
|
||||
fmtln!(fmt, "},");
|
||||
for (name, include_args) in &[("InstructionData", true), ("InstructionImms", false)] {
|
||||
fmt.line("#[derive(Clone, Debug, PartialEq, Hash)]");
|
||||
if !include_args {
|
||||
// `InstructionImms` gets some extra derives: it acts like
|
||||
// a sort of extended opcode and we want to allow for
|
||||
// hashconsing via Eq. `Copy` also turns out to be useful.
|
||||
fmt.line("#[derive(Copy, Eq)]");
|
||||
}
|
||||
});
|
||||
fmt.line("}");
|
||||
fmt.line(r#"#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]"#);
|
||||
fmt.line("#[allow(missing_docs)]");
|
||||
// generate `enum InstructionData` or `enum InstructionImms`.
|
||||
// (This comment exists so one can grep for `enum InstructionData`!)
|
||||
fmtln!(fmt, "pub enum {} {{", name);
|
||||
fmt.indent(|fmt| {
|
||||
for format in formats {
|
||||
fmtln!(fmt, "{} {{", format.name);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("opcode: Opcode,");
|
||||
if *include_args {
|
||||
if format.has_value_list {
|
||||
fmt.line("args: ValueList,");
|
||||
} else if format.num_value_operands == 1 {
|
||||
fmt.line("arg: Value,");
|
||||
} else if format.num_value_operands > 0 {
|
||||
fmtln!(fmt, "args: [Value; {}],", format.num_value_operands);
|
||||
}
|
||||
}
|
||||
for field in &format.imm_fields {
|
||||
fmtln!(fmt, "{}: {},", field.member, field.kind.rust_type);
|
||||
}
|
||||
});
|
||||
fmtln!(fmt, "},");
|
||||
}
|
||||
});
|
||||
fmt.line("}");
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_arguments_method(formats: &[&InstructionFormat], fmt: &mut Formatter, is_mut: bool) {
|
||||
@@ -150,6 +166,122 @@ fn gen_arguments_method(formats: &[&InstructionFormat], fmt: &mut Formatter, is_
|
||||
fmtln!(fmt, "}");
|
||||
}
|
||||
|
||||
/// Generate the conversion from `InstructionData` to `InstructionImms`, stripping out the
|
||||
/// `Value`s.
|
||||
fn gen_instruction_data_to_instruction_imms(formats: &[&InstructionFormat], fmt: &mut Formatter) {
|
||||
fmt.line("impl std::convert::From<&InstructionData> for InstructionImms {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.doc_comment("Convert an `InstructionData` into an `InstructionImms`.");
|
||||
fmt.line("fn from(data: &InstructionData) -> InstructionImms {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("match data {");
|
||||
fmt.indent(|fmt| {
|
||||
for format in formats {
|
||||
fmtln!(fmt, "InstructionData::{} {{", format.name);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("opcode,");
|
||||
for field in &format.imm_fields {
|
||||
fmtln!(fmt, "{},", field.member);
|
||||
}
|
||||
fmt.line("..");
|
||||
});
|
||||
fmtln!(fmt, "}} => InstructionImms::{} {{", format.name);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("opcode: *opcode,");
|
||||
for field in &format.imm_fields {
|
||||
fmtln!(fmt, "{}: {}.clone(),", field.member, field.member);
|
||||
}
|
||||
});
|
||||
fmt.line("},");
|
||||
}
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
fmt.empty_line();
|
||||
}
|
||||
|
||||
/// Generate the conversion from `InstructionImms` to `InstructionData`, adding the
|
||||
/// `Value`s.
|
||||
fn gen_instruction_imms_to_instruction_data(formats: &[&InstructionFormat], fmt: &mut Formatter) {
|
||||
fmt.line("impl InstructionImms {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.doc_comment("Convert an `InstructionImms` into an `InstructionData` by adding args.");
|
||||
fmt.line(
|
||||
"pub fn with_args(&self, values: &[Value], value_list: &mut ValueListPool) -> InstructionData {",
|
||||
);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("match self {");
|
||||
fmt.indent(|fmt| {
|
||||
for format in formats {
|
||||
fmtln!(fmt, "InstructionImms::{} {{", format.name);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("opcode,");
|
||||
for field in &format.imm_fields {
|
||||
fmtln!(fmt, "{},", field.member);
|
||||
}
|
||||
});
|
||||
fmt.line("} => {");
|
||||
if format.has_value_list {
|
||||
fmtln!(fmt, "let args = ValueList::from_slice(values, value_list);");
|
||||
}
|
||||
fmt.indent(|fmt| {
|
||||
fmtln!(fmt, "InstructionData::{} {{", format.name);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("opcode: *opcode,");
|
||||
for field in &format.imm_fields {
|
||||
fmtln!(fmt, "{}: {}.clone(),", field.member, field.member);
|
||||
}
|
||||
if format.has_value_list {
|
||||
fmtln!(fmt, "args,");
|
||||
} else if format.num_value_operands == 1 {
|
||||
fmtln!(fmt, "arg: values[0],");
|
||||
} else if format.num_value_operands > 0 {
|
||||
let mut args = vec![];
|
||||
for i in 0..format.num_value_operands {
|
||||
args.push(format!("values[{}]", i));
|
||||
}
|
||||
fmtln!(fmt, "args: [{}],", args.join(", "));
|
||||
}
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("},");
|
||||
}
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
fmt.empty_line();
|
||||
}
|
||||
|
||||
/// Generate the `opcode` method on InstructionImms.
|
||||
fn gen_instruction_imms_impl(formats: &[&InstructionFormat], fmt: &mut Formatter) {
|
||||
fmt.line("impl InstructionImms {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.doc_comment("Get the opcode of this instruction.");
|
||||
fmt.line("pub fn opcode(&self) -> Opcode {");
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new("*self");
|
||||
for format in formats {
|
||||
m.arm(
|
||||
format!("Self::{}", format.name),
|
||||
vec!["opcode", ".."],
|
||||
"opcode".to_string(),
|
||||
);
|
||||
}
|
||||
fmt.add_match(m);
|
||||
});
|
||||
fmt.line("}");
|
||||
});
|
||||
fmt.line("}");
|
||||
fmt.empty_line();
|
||||
}
|
||||
|
||||
/// Generate the boring parts of the InstructionData implementation.
|
||||
///
|
||||
/// These methods in `impl InstructionData` can be generated automatically from the instruction
|
||||
@@ -1070,7 +1202,12 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo
|
||||
fmtln!(fmt, "}")
|
||||
}
|
||||
|
||||
fn gen_isle(formats: &[&InstructionFormat], instructions: &AllInstructions, fmt: &mut Formatter) {
|
||||
fn gen_common_isle(
|
||||
formats: &[&InstructionFormat],
|
||||
instructions: &AllInstructions,
|
||||
fmt: &mut Formatter,
|
||||
is_lower: bool,
|
||||
) {
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Write;
|
||||
|
||||
@@ -1123,40 +1260,46 @@ fn gen_isle(formats: &[&InstructionFormat], instructions: &AllInstructions, fmt:
|
||||
gen_isle_enum(name, variants, fmt)
|
||||
}
|
||||
|
||||
// Generate all of the value arrays we need for `InstructionData` as well as
|
||||
// the constructors and extractors for them.
|
||||
fmt.line(";;;; Value Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
|
||||
fmt.empty_line();
|
||||
let value_array_arities: BTreeSet<_> = formats
|
||||
.iter()
|
||||
.filter(|f| f.typevar_operand.is_some() && !f.has_value_list && f.num_value_operands != 1)
|
||||
.map(|f| f.num_value_operands)
|
||||
.collect();
|
||||
for n in value_array_arities {
|
||||
fmtln!(fmt, ";; ISLE representation of `[Value; {}]`.", n);
|
||||
fmtln!(fmt, "(type ValueArray{} extern (enum))", n);
|
||||
if is_lower {
|
||||
// Generate all of the value arrays we need for `InstructionData` as well as
|
||||
// the constructors and extractors for them.
|
||||
fmt.line(
|
||||
";;;; Value Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
|
||||
);
|
||||
fmt.empty_line();
|
||||
let value_array_arities: BTreeSet<_> = formats
|
||||
.iter()
|
||||
.filter(|f| {
|
||||
f.typevar_operand.is_some() && !f.has_value_list && f.num_value_operands != 1
|
||||
})
|
||||
.map(|f| f.num_value_operands)
|
||||
.collect();
|
||||
for n in value_array_arities {
|
||||
fmtln!(fmt, ";; ISLE representation of `[Value; {}]`.", n);
|
||||
fmtln!(fmt, "(type ValueArray{} extern (enum))", n);
|
||||
fmt.empty_line();
|
||||
|
||||
fmtln!(
|
||||
fmt,
|
||||
"(decl value_array_{} ({}) ValueArray{})",
|
||||
n,
|
||||
(0..n).map(|_| "Value").collect::<Vec<_>>().join(" "),
|
||||
n
|
||||
);
|
||||
fmtln!(
|
||||
fmt,
|
||||
"(extern constructor value_array_{} pack_value_array_{})",
|
||||
n,
|
||||
n
|
||||
);
|
||||
fmtln!(
|
||||
fmt,
|
||||
"(extern extractor infallible value_array_{} unpack_value_array_{})",
|
||||
n,
|
||||
n
|
||||
);
|
||||
fmt.empty_line();
|
||||
fmtln!(
|
||||
fmt,
|
||||
"(decl value_array_{} ({}) ValueArray{})",
|
||||
n,
|
||||
(0..n).map(|_| "Value").collect::<Vec<_>>().join(" "),
|
||||
n
|
||||
);
|
||||
fmtln!(
|
||||
fmt,
|
||||
"(extern constructor value_array_{} pack_value_array_{})",
|
||||
n,
|
||||
n
|
||||
);
|
||||
fmtln!(
|
||||
fmt,
|
||||
"(extern extractor infallible value_array_{} unpack_value_array_{})",
|
||||
n,
|
||||
n
|
||||
);
|
||||
fmt.empty_line();
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the extern type declaration for `Opcode`.
|
||||
@@ -1175,21 +1318,33 @@ fn gen_isle(formats: &[&InstructionFormat], instructions: &AllInstructions, fmt:
|
||||
fmt.line(")");
|
||||
fmt.empty_line();
|
||||
|
||||
// Generate the extern type declaration for `InstructionData`.
|
||||
fmt.line(";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
|
||||
// Generate the extern type declaration for `InstructionData`
|
||||
// (lowering) or `InstructionImms` (opt).
|
||||
let inst_data_name = if is_lower {
|
||||
"InstructionData"
|
||||
} else {
|
||||
"InstructionImms"
|
||||
};
|
||||
fmtln!(
|
||||
fmt,
|
||||
";;;; `{}` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
|
||||
inst_data_name
|
||||
);
|
||||
fmt.empty_line();
|
||||
fmt.line("(type InstructionData extern");
|
||||
fmtln!(fmt, "(type {} extern", inst_data_name);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("(enum");
|
||||
fmt.indent(|fmt| {
|
||||
for format in formats {
|
||||
let mut s = format!("({} (opcode Opcode)", format.name);
|
||||
if format.has_value_list {
|
||||
s.push_str(" (args ValueList)");
|
||||
} else if format.num_value_operands == 1 {
|
||||
s.push_str(" (arg Value)");
|
||||
} else if format.num_value_operands > 1 {
|
||||
write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap();
|
||||
if is_lower {
|
||||
if format.has_value_list {
|
||||
s.push_str(" (args ValueList)");
|
||||
} else if format.num_value_operands == 1 {
|
||||
s.push_str(" (arg Value)");
|
||||
} else if format.num_value_operands > 1 {
|
||||
write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap();
|
||||
}
|
||||
}
|
||||
for field in &format.imm_fields {
|
||||
write!(
|
||||
@@ -1210,85 +1365,157 @@ fn gen_isle(formats: &[&InstructionFormat], instructions: &AllInstructions, fmt:
|
||||
fmt.empty_line();
|
||||
|
||||
// Generate the helper extractors for each opcode's full instruction.
|
||||
//
|
||||
// TODO: if/when we port our peephole optimization passes to ISLE we will
|
||||
// want helper constructors as well.
|
||||
fmt.line(";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;");
|
||||
fmtln!(
|
||||
fmt,
|
||||
";;;; Extracting Opcode, Operands, and Immediates from `{}` ;;;;;;;;",
|
||||
inst_data_name
|
||||
);
|
||||
fmt.empty_line();
|
||||
let ret_ty = if is_lower { "Inst" } else { "Id" };
|
||||
for inst in instructions {
|
||||
if !is_lower && inst.format.has_value_list {
|
||||
continue;
|
||||
}
|
||||
|
||||
fmtln!(
|
||||
fmt,
|
||||
"(decl {} ({}) Inst)",
|
||||
"(decl {} ({}{}) {})",
|
||||
inst.name,
|
||||
if is_lower { "" } else { "Type " },
|
||||
inst.operands_in
|
||||
.iter()
|
||||
.map(|o| {
|
||||
let ty = o.kind.rust_type;
|
||||
if ty == "&[Value]" {
|
||||
"ValueSlice"
|
||||
if is_lower {
|
||||
if ty == "&[Value]" {
|
||||
"ValueSlice"
|
||||
} else {
|
||||
ty.rsplit("::").next().unwrap()
|
||||
}
|
||||
} else {
|
||||
ty.rsplit("::").next().unwrap()
|
||||
if ty == "&[Value]" {
|
||||
panic!("value slice in mid-end extractor");
|
||||
} else if ty == "Value" || ty == "ir::Value" {
|
||||
"Id"
|
||||
} else {
|
||||
ty.rsplit("::").next().unwrap()
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
.join(" "),
|
||||
ret_ty
|
||||
);
|
||||
fmtln!(fmt, "(extractor");
|
||||
fmt.indent(|fmt| {
|
||||
fmtln!(
|
||||
fmt,
|
||||
"({} {})",
|
||||
"({} {}{})",
|
||||
inst.name,
|
||||
if is_lower { "" } else { "ty " },
|
||||
inst.operands_in
|
||||
.iter()
|
||||
.map(|o| { o.name })
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
let mut s = format!(
|
||||
"(inst_data (InstructionData.{} (Opcode.{})",
|
||||
inst.format.name, inst.camel_name
|
||||
);
|
||||
|
||||
// Value and varargs operands.
|
||||
if inst.format.has_value_list {
|
||||
// The instruction format uses a value list, but the
|
||||
// instruction itself might have not only a `&[Value]`
|
||||
// varargs operand, but also one or more `Value` operands as
|
||||
// well. If this is the case, then we need to read them off
|
||||
// the front of the `ValueList`.
|
||||
let values: Vec<_> = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.filter(|o| o.is_value())
|
||||
.map(|o| o.name)
|
||||
.collect();
|
||||
let varargs = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.find(|o| o.is_varargs())
|
||||
.unwrap()
|
||||
.name;
|
||||
if values.is_empty() {
|
||||
write!(&mut s, " (value_list_slice {})", varargs).unwrap();
|
||||
} else {
|
||||
if is_lower {
|
||||
let mut s = format!(
|
||||
"(inst_data (InstructionData.{} (Opcode.{})",
|
||||
inst.format.name, inst.camel_name
|
||||
);
|
||||
|
||||
// Value and varargs operands.
|
||||
if inst.format.has_value_list {
|
||||
// The instruction format uses a value list, but the
|
||||
// instruction itself might have not only a `&[Value]`
|
||||
// varargs operand, but also one or more `Value` operands as
|
||||
// well. If this is the case, then we need to read them off
|
||||
// the front of the `ValueList`.
|
||||
let values: Vec<_> = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.filter(|o| o.is_value())
|
||||
.map(|o| o.name)
|
||||
.collect();
|
||||
let varargs = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.find(|o| o.is_varargs())
|
||||
.unwrap()
|
||||
.name;
|
||||
if values.is_empty() {
|
||||
write!(&mut s, " (value_list_slice {})", varargs).unwrap();
|
||||
} else {
|
||||
write!(
|
||||
&mut s,
|
||||
" (unwrap_head_value_list_{} {} {})",
|
||||
values.len(),
|
||||
values.join(" "),
|
||||
varargs
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
} else if inst.format.num_value_operands == 1 {
|
||||
write!(
|
||||
&mut s,
|
||||
" (unwrap_head_value_list_{} {} {})",
|
||||
values.len(),
|
||||
values.join(" "),
|
||||
varargs
|
||||
" {}",
|
||||
inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
|
||||
)
|
||||
.unwrap();
|
||||
} else if inst.format.num_value_operands > 1 {
|
||||
let values = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.filter(|o| o.is_value())
|
||||
.map(|o| o.name)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(values.len(), inst.format.num_value_operands);
|
||||
let values = values.join(" ");
|
||||
write!(
|
||||
&mut s,
|
||||
" (value_array_{} {})",
|
||||
inst.format.num_value_operands, values,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
} else if inst.format.num_value_operands == 1 {
|
||||
write!(
|
||||
&mut s,
|
||||
" {}",
|
||||
inst.operands_in.iter().find(|o| o.is_value()).unwrap().name
|
||||
)
|
||||
.unwrap();
|
||||
} else if inst.format.num_value_operands > 1 {
|
||||
|
||||
// Immediates.
|
||||
let imm_operands: Vec<_> = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.filter(|o| !o.is_value() && !o.is_varargs())
|
||||
.collect();
|
||||
assert_eq!(imm_operands.len(), inst.format.imm_fields.len());
|
||||
for op in imm_operands {
|
||||
write!(&mut s, " {}", op.name).unwrap();
|
||||
}
|
||||
|
||||
s.push_str("))");
|
||||
fmt.line(&s);
|
||||
} else {
|
||||
// Mid-end case.
|
||||
let mut s = format!(
|
||||
"(enodes ty (InstructionImms.{} (Opcode.{})",
|
||||
inst.format.name, inst.camel_name
|
||||
);
|
||||
|
||||
// Immediates.
|
||||
let imm_operands: Vec<_> = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.filter(|o| !o.is_value() && !o.is_varargs())
|
||||
.collect();
|
||||
assert_eq!(imm_operands.len(), inst.format.imm_fields.len());
|
||||
for op in imm_operands {
|
||||
write!(&mut s, " {}", op.name).unwrap();
|
||||
}
|
||||
// End of `InstructionImms`.
|
||||
s.push_str(")");
|
||||
|
||||
// Second arg to `enode`: value args.
|
||||
assert!(!inst.operands_in.iter().any(|op| op.is_varargs()));
|
||||
let values = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
@@ -1299,31 +1526,83 @@ fn gen_isle(formats: &[&InstructionFormat], instructions: &AllInstructions, fmt:
|
||||
let values = values.join(" ");
|
||||
write!(
|
||||
&mut s,
|
||||
" (value_array_{} {})",
|
||||
" (id_array_{} {})",
|
||||
inst.format.num_value_operands, values,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Immediates.
|
||||
let imm_operands: Vec<_> = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.filter(|o| !o.is_value() && !o.is_varargs())
|
||||
.collect();
|
||||
assert_eq!(imm_operands.len(), inst.format.imm_fields.len());
|
||||
for op in imm_operands {
|
||||
write!(&mut s, " {}", op.name).unwrap();
|
||||
s.push_str(")");
|
||||
fmt.line(&s);
|
||||
}
|
||||
|
||||
s.push_str("))");
|
||||
fmt.line(&s);
|
||||
});
|
||||
fmt.line(")");
|
||||
|
||||
// Generate a constructor if this is the mid-end prelude.
|
||||
if !is_lower {
|
||||
fmtln!(
|
||||
fmt,
|
||||
"(rule ({} ty {})",
|
||||
inst.name,
|
||||
inst.operands_in
|
||||
.iter()
|
||||
.map(|o| o.name)
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
fmt.indent(|fmt| {
|
||||
let mut s = format!(
|
||||
"(pure_enode ty (InstructionImms.{} (Opcode.{})",
|
||||
inst.format.name, inst.camel_name
|
||||
);
|
||||
|
||||
for o in inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.filter(|o| !o.is_value() && !o.is_varargs())
|
||||
{
|
||||
write!(&mut s, " {}", o.name).unwrap();
|
||||
}
|
||||
s.push_str(")");
|
||||
|
||||
let values = inst
|
||||
.operands_in
|
||||
.iter()
|
||||
.filter(|o| o.is_value())
|
||||
.map(|o| o.name)
|
||||
.collect::<Vec<_>>();
|
||||
let values = values.join(" ");
|
||||
write!(
|
||||
&mut s,
|
||||
" (id_array_{} {})",
|
||||
inst.format.num_value_operands, values
|
||||
)
|
||||
.unwrap();
|
||||
s.push_str(")");
|
||||
fmt.line(&s);
|
||||
});
|
||||
fmt.line(")");
|
||||
}
|
||||
|
||||
fmt.empty_line();
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_opt_isle(
|
||||
formats: &[&InstructionFormat],
|
||||
instructions: &AllInstructions,
|
||||
fmt: &mut Formatter,
|
||||
) {
|
||||
gen_common_isle(formats, instructions, fmt, /* is_lower = */ false);
|
||||
}
|
||||
|
||||
fn gen_lower_isle(
|
||||
formats: &[&InstructionFormat],
|
||||
instructions: &AllInstructions,
|
||||
fmt: &mut Formatter,
|
||||
) {
|
||||
gen_common_isle(formats, instructions, fmt, /* is_lower = */ true);
|
||||
}
|
||||
|
||||
/// Generate an `enum` immediate in ISLE.
|
||||
fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) {
|
||||
variants.sort();
|
||||
@@ -1388,7 +1667,8 @@ pub(crate) fn generate(
|
||||
all_inst: &AllInstructions,
|
||||
opcode_filename: &str,
|
||||
inst_builder_filename: &str,
|
||||
isle_filename: &str,
|
||||
isle_opt_filename: &str,
|
||||
isle_lower_filename: &str,
|
||||
out_dir: &str,
|
||||
isle_dir: &str,
|
||||
) -> Result<(), error::Error> {
|
||||
@@ -1398,16 +1678,24 @@ pub(crate) fn generate(
|
||||
gen_instruction_data(&formats, &mut fmt);
|
||||
fmt.empty_line();
|
||||
gen_instruction_data_impl(&formats, &mut fmt);
|
||||
gen_instruction_data_to_instruction_imms(&formats, &mut fmt);
|
||||
gen_instruction_imms_impl(&formats, &mut fmt);
|
||||
gen_instruction_imms_to_instruction_data(&formats, &mut fmt);
|
||||
fmt.empty_line();
|
||||
gen_opcodes(all_inst, &mut fmt);
|
||||
fmt.empty_line();
|
||||
gen_type_constraints(all_inst, &mut fmt);
|
||||
fmt.update_file(opcode_filename, out_dir)?;
|
||||
|
||||
// ISLE DSL.
|
||||
// ISLE DSL: mid-end ("opt") generated bindings.
|
||||
let mut fmt = Formatter::new();
|
||||
gen_isle(&formats, all_inst, &mut fmt);
|
||||
fmt.update_file(isle_filename, isle_dir)?;
|
||||
gen_opt_isle(&formats, all_inst, &mut fmt);
|
||||
fmt.update_file(isle_opt_filename, isle_dir)?;
|
||||
|
||||
// ISLE DSL: lowering generated bindings.
|
||||
let mut fmt = Formatter::new();
|
||||
gen_lower_isle(&formats, all_inst, &mut fmt);
|
||||
fmt.update_file(isle_lower_filename, isle_dir)?;
|
||||
|
||||
// Instruction builder.
|
||||
let mut fmt = Formatter::new();
|
||||
|
||||
Reference in New Issue
Block a user