summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cranelift-codegen-meta/src/gen_binemit.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/cranelift-codegen-meta/src/gen_binemit.rs
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cranelift-codegen-meta/src/gen_binemit.rs')
-rw-r--r--third_party/rust/cranelift-codegen-meta/src/gen_binemit.rs224
1 files changed, 224 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen-meta/src/gen_binemit.rs b/third_party/rust/cranelift-codegen-meta/src/gen_binemit.rs
new file mode 100644
index 0000000000..f67aa9b5a9
--- /dev/null
+++ b/third_party/rust/cranelift-codegen-meta/src/gen_binemit.rs
@@ -0,0 +1,224 @@
+//! 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(())
+}