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