1 //! Test command for testing the binary machine code emission.
2 //!
3 //! The `binemit` test command generates binary machine code for every instruction in the input
4 //! functions and compares the results to the expected output.
5 
6 use crate::match_directive::match_directive;
7 use crate::subtest::{Context, SubTest, SubtestResult};
8 use cranelift_codegen::binemit::{self, CodeInfo, CodeSink, RegDiversions};
9 use cranelift_codegen::dbg::DisplayList;
10 use cranelift_codegen::dominator_tree::DominatorTree;
11 use cranelift_codegen::flowgraph::ControlFlowGraph;
12 use cranelift_codegen::ir;
13 use cranelift_codegen::ir::entities::AnyEntity;
14 use cranelift_codegen::isa;
15 use cranelift_codegen::print_errors::pretty_error;
16 use cranelift_codegen::settings::OptLevel;
17 use cranelift_reader::TestCommand;
18 use std::borrow::Cow;
19 use std::collections::HashMap;
20 use std::fmt::Write;
21 
22 struct TestBinEmit;
23 
subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>>24 pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
25     assert_eq!(parsed.command, "binemit");
26     if !parsed.options.is_empty() {
27         Err(format!("No options allowed on {}", parsed))
28     } else {
29         Ok(Box::new(TestBinEmit))
30     }
31 }
32 
33 /// Code sink that generates text.
34 struct TextSink {
35     code_size: binemit::CodeOffset,
36     offset: binemit::CodeOffset,
37     text: String,
38 }
39 
40 impl TextSink {
41     /// Create a new empty TextSink.
new() -> Self42     pub fn new() -> Self {
43         Self {
44             code_size: 0,
45             offset: 0,
46             text: String::new(),
47         }
48     }
49 }
50 
51 impl binemit::CodeSink for TextSink {
offset(&self) -> binemit::CodeOffset52     fn offset(&self) -> binemit::CodeOffset {
53         self.offset
54     }
55 
put1(&mut self, x: u8)56     fn put1(&mut self, x: u8) {
57         write!(self.text, "{:02x} ", x).unwrap();
58         self.offset += 1;
59     }
60 
put2(&mut self, x: u16)61     fn put2(&mut self, x: u16) {
62         write!(self.text, "{:04x} ", x).unwrap();
63         self.offset += 2;
64     }
65 
put4(&mut self, x: u32)66     fn put4(&mut self, x: u32) {
67         write!(self.text, "{:08x} ", x).unwrap();
68         self.offset += 4;
69     }
70 
put8(&mut self, x: u64)71     fn put8(&mut self, x: u64) {
72         write!(self.text, "{:016x} ", x).unwrap();
73         self.offset += 8;
74     }
75 
reloc_block(&mut self, reloc: binemit::Reloc, block_offset: binemit::CodeOffset)76     fn reloc_block(&mut self, reloc: binemit::Reloc, block_offset: binemit::CodeOffset) {
77         write!(self.text, "{}({}) ", reloc, block_offset).unwrap();
78     }
79 
reloc_external( &mut self, _srcloc: ir::SourceLoc, reloc: binemit::Reloc, name: &ir::ExternalName, addend: binemit::Addend, )80     fn reloc_external(
81         &mut self,
82         _srcloc: ir::SourceLoc,
83         reloc: binemit::Reloc,
84         name: &ir::ExternalName,
85         addend: binemit::Addend,
86     ) {
87         write!(self.text, "{}({}", reloc, name).unwrap();
88         if addend != 0 {
89             write!(self.text, "{:+}", addend).unwrap();
90         }
91         write!(self.text, ") ").unwrap();
92     }
93 
reloc_constant(&mut self, reloc: binemit::Reloc, constant: ir::ConstantOffset)94     fn reloc_constant(&mut self, reloc: binemit::Reloc, constant: ir::ConstantOffset) {
95         write!(self.text, "{}({}) ", reloc, constant).unwrap();
96     }
97 
reloc_jt(&mut self, reloc: binemit::Reloc, jt: ir::JumpTable)98     fn reloc_jt(&mut self, reloc: binemit::Reloc, jt: ir::JumpTable) {
99         write!(self.text, "{}({}) ", reloc, jt).unwrap();
100     }
101 
trap(&mut self, code: ir::TrapCode, _srcloc: ir::SourceLoc)102     fn trap(&mut self, code: ir::TrapCode, _srcloc: ir::SourceLoc) {
103         write!(self.text, "{} ", code).unwrap();
104     }
105 
begin_jumptables(&mut self)106     fn begin_jumptables(&mut self) {
107         self.code_size = self.offset
108     }
begin_rodata(&mut self)109     fn begin_rodata(&mut self) {}
end_codegen(&mut self)110     fn end_codegen(&mut self) {}
add_stackmap( &mut self, _: &[ir::entities::Value], _: &ir::Function, _: &dyn isa::TargetIsa, )111     fn add_stackmap(
112         &mut self,
113         _: &[ir::entities::Value],
114         _: &ir::Function,
115         _: &dyn isa::TargetIsa,
116     ) {
117     }
118 }
119 
120 impl SubTest for TestBinEmit {
name(&self) -> &'static str121     fn name(&self) -> &'static str {
122         "binemit"
123     }
124 
is_mutating(&self) -> bool125     fn is_mutating(&self) -> bool {
126         true
127     }
128 
needs_isa(&self) -> bool129     fn needs_isa(&self) -> bool {
130         true
131     }
132 
run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()>133     fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
134         let isa = context.isa.expect("binemit needs an ISA");
135         let encinfo = isa.encoding_info();
136         // TODO: Run a verifier pass over the code first to detect any bad encodings or missing/bad
137         // value locations. The current error reporting is just crashing...
138         let mut func = func.into_owned();
139 
140         // Fix the stack frame layout so we can test spill/fill encodings.
141         let min_offset = func
142             .stack_slots
143             .values()
144             .map(|slot| slot.offset.unwrap())
145             .min();
146         func.stack_slots.layout_info = min_offset.map(|off| ir::StackLayoutInfo {
147             frame_size: (-off) as u32,
148             inbound_args_size: 0,
149         });
150 
151         let opt_level = isa.flags().opt_level();
152 
153         // Give an encoding to any instruction that doesn't already have one.
154         let mut divert = RegDiversions::new();
155         for block in func.layout.blocks() {
156             divert.clear();
157             for inst in func.layout.block_insts(block) {
158                 if !func.encodings[inst].is_legal() {
159                     // Find an encoding that satisfies both immediate field and register
160                     // constraints.
161                     if let Some(enc) = {
162                         let mut legal_encodings = isa
163                             .legal_encodings(&func, &func.dfg[inst], func.dfg.ctrl_typevar(inst))
164                             .filter(|e| {
165                                 let recipe_constraints = &encinfo.constraints[e.recipe()];
166                                 recipe_constraints.satisfied(inst, &divert, &func)
167                             });
168 
169                         if opt_level == OptLevel::SpeedAndSize {
170                             // Get the smallest legal encoding
171                             legal_encodings
172                                 .min_by_key(|&e| encinfo.byte_size(e, inst, &divert, &func))
173                         } else {
174                             // If not optimizing, just use the first encoding.
175                             legal_encodings.next()
176                         }
177                     } {
178                         func.encodings[inst] = enc;
179                     }
180                 }
181                 divert.apply(&func.dfg[inst]);
182             }
183         }
184 
185         // Relax branches and compute block offsets based on the encodings.
186         let mut cfg = ControlFlowGraph::with_function(&func);
187         let mut domtree = DominatorTree::with_function(&func, &cfg);
188         let CodeInfo { total_size, .. } =
189             binemit::relax_branches(&mut func, &mut cfg, &mut domtree, isa)
190                 .map_err(|e| pretty_error(&func, context.isa, e))?;
191 
192         // Collect all of the 'bin:' directives on instructions.
193         let mut bins = HashMap::new();
194         for comment in &context.details.comments {
195             if let Some(want) = match_directive(comment.text, "bin:") {
196                 match comment.entity {
197                     AnyEntity::Inst(inst) => {
198                         if let Some(prev) = bins.insert(inst, want) {
199                             return Err(format!(
200                                 "multiple 'bin:' directives on {}: '{}' and '{}'",
201                                 func.dfg.display_inst(inst, isa),
202                                 prev,
203                                 want
204                             ));
205                         }
206                     }
207                     _ => {
208                         return Err(format!(
209                             "'bin:' directive on non-inst {}: {}",
210                             comment.entity, comment.text
211                         ));
212                     }
213                 }
214             }
215         }
216         if bins.is_empty() {
217             return Err("No 'bin:' directives found".to_string());
218         }
219 
220         // Now emit all instructions.
221         let mut sink = TextSink::new();
222         for block in func.layout.blocks() {
223             divert.clear();
224             // Correct header offsets should have been computed by `relax_branches()`.
225             assert_eq!(
226                 sink.offset, func.offsets[block],
227                 "Inconsistent {} header offset",
228                 block
229             );
230             for (offset, inst, enc_bytes) in func.inst_offsets(block, &encinfo) {
231                 assert_eq!(sink.offset, offset);
232                 sink.text.clear();
233                 let enc = func.encodings[inst];
234 
235                 // Send legal encodings into the emitter.
236                 if enc.is_legal() {
237                     // Generate a better error message if output locations are not specified.
238                     validate_location_annotations(&func, inst, isa, false)?;
239 
240                     let before = sink.offset;
241                     isa.emit_inst(&func, inst, &mut divert, &mut sink);
242                     let emitted = sink.offset - before;
243                     // Verify the encoding recipe sizes against the ISAs emit_inst implementation.
244                     assert_eq!(
245                         emitted,
246                         enc_bytes,
247                         "Inconsistent size for [{}] {}",
248                         encinfo.display(enc),
249                         func.dfg.display_inst(inst, isa)
250                     );
251                 }
252 
253                 // Check against bin: directives.
254                 if let Some(want) = bins.remove(&inst) {
255                     if !enc.is_legal() {
256                         // A possible cause of an unencoded instruction is a missing location for
257                         // one of the input/output operands.
258                         validate_location_annotations(&func, inst, isa, true)?;
259                         validate_location_annotations(&func, inst, isa, false)?;
260 
261                         // Do any encodings exist?
262                         let encodings = isa
263                             .legal_encodings(&func, &func.dfg[inst], func.dfg.ctrl_typevar(inst))
264                             .map(|e| encinfo.display(e))
265                             .collect::<Vec<_>>();
266 
267                         if encodings.is_empty() {
268                             return Err(format!(
269                                 "No encodings found for: {}",
270                                 func.dfg.display_inst(inst, isa)
271                             ));
272                         }
273                         return Err(format!(
274                             "No matching encodings for {} in {}",
275                             func.dfg.display_inst(inst, isa),
276                             DisplayList(&encodings),
277                         ));
278                     }
279                     let have = sink.text.trim();
280                     if have != want {
281                         return Err(format!(
282                             "Bad machine code for {}: {}\nWant: {}\nGot:  {}",
283                             inst,
284                             func.dfg.display_inst(inst, isa),
285                             want,
286                             have
287                         ));
288                     }
289                 }
290             }
291         }
292 
293         sink.begin_jumptables();
294 
295         for (jt, jt_data) in func.jump_tables.iter() {
296             let jt_offset = func.jt_offsets[jt];
297             for block in jt_data.iter() {
298                 let rel_offset: i32 = func.offsets[*block] as i32 - jt_offset as i32;
299                 sink.put4(rel_offset as u32)
300             }
301         }
302 
303         sink.begin_rodata();
304 
305         // output constants
306         for (_, constant_data) in func.dfg.constants.iter() {
307             for byte in constant_data.iter() {
308                 sink.put1(*byte)
309             }
310         }
311 
312         sink.end_codegen();
313 
314         if sink.offset != total_size {
315             return Err(format!(
316                 "Expected code size {}, got {}",
317                 total_size, sink.offset
318             ));
319         }
320 
321         Ok(())
322     }
323 }
324 
325 /// Validate registers/stack slots are correctly annotated.
validate_location_annotations( func: &ir::Function, inst: ir::Inst, isa: &dyn isa::TargetIsa, validate_inputs: bool, ) -> SubtestResult<()>326 fn validate_location_annotations(
327     func: &ir::Function,
328     inst: ir::Inst,
329     isa: &dyn isa::TargetIsa,
330     validate_inputs: bool,
331 ) -> SubtestResult<()> {
332     let values = if validate_inputs {
333         func.dfg.inst_args(inst)
334     } else {
335         func.dfg.inst_results(inst)
336     };
337 
338     if let Some(&v) = values.iter().find(|&&v| !func.locations[v].is_assigned()) {
339         Err(format!(
340             "Need register/stack slot annotation for {} in {}",
341             v,
342             func.dfg.display_inst(inst, isa)
343         ))
344     } else {
345         Ok(())
346     }
347 }
348