Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit d0e6c39

Browse files
bnjbvrbjorn3
andcommitted
[meta] Generate the binemits files;
Co-authored-by: Benjamin Bouvier <[email protected]> Co-authored-by: bjorn3 <[email protected]>
1 parent 2b1cfea commit d0e6c39

File tree

2 files changed

+234
-0
lines changed

2 files changed

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

cranelift-codegen/meta/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod srcgen;
55
pub mod error;
66
pub mod isa;
77

8+
mod gen_binemit;
89
mod gen_inst;
910
mod gen_legalizer;
1011
mod gen_registers;
@@ -48,12 +49,21 @@ pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error>
4849

4950
for isa in isas {
5051
gen_registers::generate(&isa, &format!("registers-{}.rs", isa.name), &out_dir)?;
52+
5153
gen_settings::generate(
5254
&isa.settings,
5355
gen_settings::ParentGroup::Shared,
5456
&format!("settings-{}.rs", isa.name),
5557
&out_dir,
5658
)?;
59+
60+
gen_binemit::generate(
61+
&shared_defs.format_registry,
62+
&isa.name,
63+
&isa.recipes,
64+
&format!("binemit-{}.rs", isa.name),
65+
&out_dir,
66+
)?;
5767
}
5868

5969
Ok(())

0 commit comments

Comments
 (0)