1 //! Generate binary emission code for each ISA.
2 
3 use cranelift_entity::EntityRef;
4 
5 use crate::error;
6 use crate::srcgen::Formatter;
7 
8 use crate::cdsl::recipes::{EncodingRecipe, OperandConstraint, Recipes};
9 
10 /// Generate code to handle a single recipe.
11 ///
12 /// - Unpack the instruction data, knowing the format.
13 /// - Determine register locations for operands with register constraints.
14 /// - Determine stack slot locations for operands with stack constraints.
15 /// - Call hand-written code for the actual emission.
gen_recipe(recipe: &EncodingRecipe, fmt: &mut Formatter)16 fn gen_recipe(recipe: &EncodingRecipe, fmt: &mut Formatter) {
17     let inst_format = &recipe.format;
18     let num_value_ops = inst_format.num_value_operands;
19 
20     // TODO: Set want_args to true for only MultiAry instructions instead of all formats with value
21     // list.
22     let want_args = inst_format.has_value_list
23         || recipe.operands_in.iter().any(|c| match c {
24             OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
25             OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
26         });
27     assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list);
28 
29     let want_outs = recipe.operands_out.iter().any(|c| match c {
30         OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
31         OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
32     });
33 
34     let is_regmove = ["RegMove", "RegSpill", "RegFill"].contains(&inst_format.name);
35 
36     // Unpack the instruction data.
37     fmtln!(fmt, "if let InstructionData::{} {{", inst_format.name);
38     fmt.indent(|fmt| {
39         fmt.line("opcode,");
40         for f in &inst_format.imm_fields {
41             fmtln!(fmt, "{},", f.member);
42         }
43         if want_args {
44             if inst_format.has_value_list || num_value_ops > 1 {
45                 fmt.line("ref args,");
46             } else {
47                 fmt.line("arg,");
48             }
49         }
50         fmt.line("..");
51 
52         fmt.outdented_line("} = *inst_data {");
53 
54         // Pass recipe arguments in this order: inputs, imm_fields, outputs.
55         let mut args = String::new();
56 
57         if want_args && !is_regmove {
58             if inst_format.has_value_list {
59                 fmt.line("let args = args.as_slice(&func.dfg.value_lists);");
60             } else if num_value_ops == 1 {
61                 fmt.line("let args = [arg];");
62             }
63             args += &unwrap_values(&recipe.operands_in, "in", "args", fmt);
64         }
65 
66         for f in &inst_format.imm_fields {
67             args += &format!(", {}", f.member);
68         }
69 
70         // Unwrap interesting output arguments.
71         if want_outs {
72             if recipe.operands_out.len() == 1 {
73                 fmt.line("let results = [func.dfg.first_result(inst)];")
74             } else {
75                 fmt.line("let results = func.dfg.inst_results(inst);");
76             }
77             args += &unwrap_values(&recipe.operands_out, "out", "results", fmt);
78         }
79 
80         // Optimization: Only update the register diversion tracker for regmove instructions.
81         if is_regmove {
82             fmt.line("divert.apply(inst_data);")
83         }
84 
85         match &recipe.emit {
86             Some(emit) => {
87                 fmt.multi_line(emit);
88                 fmt.line("return;");
89             }
90             None => {
91                 fmtln!(
92                     fmt,
93                     "return recipe_{}(func, inst, sink, bits{});",
94                     recipe.name.to_lowercase(),
95                     args
96                 );
97             }
98         }
99     });
100     fmt.line("}");
101 }
102 
103 /// Emit code that unwraps values living in registers or stack slots.
104 ///
105 /// :param args: Input or output constraints.
106 /// :param prefix: Prefix to be used for the generated local variables.
107 /// :param values: Name of slice containing the values to be unwrapped.
108 /// :returns: Comma separated list of the generated variables
unwrap_values( args: &[OperandConstraint], prefix: &str, values_slice: &str, fmt: &mut Formatter, ) -> String109 fn unwrap_values(
110     args: &[OperandConstraint],
111     prefix: &str,
112     values_slice: &str,
113     fmt: &mut Formatter,
114 ) -> String {
115     let mut varlist = String::new();
116     for (i, cst) in args.iter().enumerate() {
117         match cst {
118             OperandConstraint::RegClass(_reg_class) => {
119                 let v = format!("{}_reg{}", prefix, i);
120                 varlist += &format!(", {}", v);
121                 fmtln!(
122                     fmt,
123                     "let {} = divert.reg({}[{}], &func.locations);",
124                     v,
125                     values_slice,
126                     i
127                 );
128             }
129             OperandConstraint::Stack(stack) => {
130                 let v = format!("{}_stk{}", prefix, i);
131                 varlist += &format!(", {}", v);
132                 fmtln!(fmt, "let {} = StackRef::masked(", v);
133                 fmt.indent(|fmt| {
134                     fmtln!(
135                         fmt,
136                         "divert.stack({}[{}], &func.locations),",
137                         values_slice,
138                         i
139                     );
140                     fmt.line(format!("{},", stack.stack_base_mask()));
141                     fmt.line("&func.stack_slots,");
142                 });
143                 fmt.line(").unwrap();");
144             }
145             _ => {}
146         }
147     }
148     varlist
149 }
150 
gen_isa(isa_name: &str, recipes: &Recipes, fmt: &mut Formatter)151 fn gen_isa(isa_name: &str, recipes: &Recipes, fmt: &mut Formatter) {
152     fmt.doc_comment(format!(
153         "Emit binary machine code for `inst` for the {} ISA.",
154         isa_name
155     ));
156 
157     if recipes.is_empty() {
158         fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
159         fmt.indent(|fmt| {
160             fmt.line("func: &Function,");
161             fmt.line("inst: Inst,");
162             fmt.line("_divert: &mut RegDiversions,");
163             fmt.line("_sink: &mut CS,");
164             fmt.line("_isa: &dyn TargetIsa,");
165         });
166         fmt.line(") {");
167         fmt.indent(|fmt| {
168             // No encoding recipes: Emit a stub.
169             fmt.line("bad_encoding(func, inst)");
170         });
171         fmt.line("}");
172         return;
173     }
174 
175     fmt.line("#[allow(unused_variables, unreachable_code)]");
176     fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
177     fmt.indent(|fmt| {
178         fmt.line("func: &Function,");
179         fmt.line("inst: Inst,");
180         fmt.line("divert: &mut RegDiversions,");
181         fmt.line("sink: &mut CS,");
182         fmt.line("isa: &dyn TargetIsa,")
183     });
184 
185     fmt.line(") {");
186     fmt.indent(|fmt| {
187         fmt.line("let encoding = func.encodings[inst];");
188         fmt.line("let bits = encoding.bits();");
189         fmt.line("let inst_data = &func.dfg[inst];");
190         fmt.line("match encoding.recipe() {");
191         fmt.indent(|fmt| {
192             for (i, recipe) in recipes.iter() {
193                 fmt.comment(format!("Recipe {}", recipe.name));
194                 fmtln!(fmt, "{} => {{", i.index());
195                 fmt.indent(|fmt| {
196                     gen_recipe(recipe, fmt);
197                 });
198                 fmt.line("}");
199             }
200             fmt.line("_ => {},");
201         });
202         fmt.line("}");
203 
204         // Allow for unencoded ghost instructions. The verifier will check details.
205         fmt.line("if encoding.is_legal() {");
206         fmt.indent(|fmt| {
207             fmt.line("bad_encoding(func, inst);");
208         });
209         fmt.line("}");
210     });
211     fmt.line("}");
212 }
213 
generate( isa_name: &str, recipes: &Recipes, binemit_filename: &str, out_dir: &str, ) -> Result<(), error::Error>214 pub(crate) fn generate(
215     isa_name: &str,
216     recipes: &Recipes,
217     binemit_filename: &str,
218     out_dir: &str,
219 ) -> Result<(), error::Error> {
220     let mut fmt = Formatter::new();
221     gen_isa(isa_name, recipes, &mut fmt);
222     fmt.update_file(binemit_filename, out_dir)?;
223     Ok(())
224 }
225