Give better error messages in "test binemit".

When an instruction can't be encoded, provide a bit more help:

- Detect missing register assignments for input and output operands.
- List encodings that where considered and rejected.
This commit is contained in:
Jakob Stoklund Olesen
2018-01-29 09:16:33 -08:00
parent 3f69581d03
commit 584a33bca7

View File

@@ -7,6 +7,7 @@ use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
use cretonne::binemit; use cretonne::binemit;
use cretonne::dbg::DisplayList;
use cretonne::ir; use cretonne::ir;
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
use cretonne::regalloc::RegDiversions; use cretonne::regalloc::RegDiversions;
@@ -137,26 +138,27 @@ impl SubTest for TestBinEmit {
divert.clear(); divert.clear();
for inst in func.layout.ebb_insts(ebb) { for inst in func.layout.ebb_insts(ebb) {
if !func.encodings[inst].is_legal() { if !func.encodings[inst].is_legal() {
let mut legal_encodings = isa.legal_encodings( // Find an encoding that satisfies both immediate field and register
&func.dfg, // constraints.
&func.dfg[inst], if let Some(enc) = {
func.dfg.ctrl_typevar(inst), let mut legal_encodings = isa.legal_encodings(
); &func.dfg,
&func.dfg[inst],
let enc = if is_compressed { func.dfg.ctrl_typevar(inst),
// Get the smallest legal encoding ).filter(|e| {
legal_encodings
.filter(|e| {
let recipe_constraints = &encinfo.constraints[e.recipe()]; let recipe_constraints = &encinfo.constraints[e.recipe()];
recipe_constraints.satisfied(inst, &divert, &func) recipe_constraints.satisfied(inst, &divert, &func)
}) });
.min_by_key(|&e| encinfo.bytes(e))
} else {
// If not using compressed, just use the first encoding.
legal_encodings.next()
};
if let Some(enc) = enc { if is_compressed {
// Get the smallest legal encoding
legal_encodings.min_by_key(|&e| encinfo.bytes(e))
} else {
// If not using compressed, just use the first encoding.
legal_encodings.next()
}
}
{
func.encodings[inst] = enc; func.encodings[inst] = enc;
} }
} }
@@ -215,6 +217,17 @@ impl SubTest for TestBinEmit {
// Send legal encodings into the emitter. // Send legal encodings into the emitter.
if enc.is_legal() { if enc.is_legal() {
// Generate a better error message if output locations are not specified.
if let Some(&v) = func.dfg.inst_results(inst).iter().find(|&&v| {
!func.locations[v].is_assigned()
})
{
return Err(format!(
"Missing register/stack slot for {} in {}",
v,
func.dfg.display_inst(inst, isa)
));
}
let before = sink.offset; let before = sink.offset;
isa.emit_inst(&func, inst, &mut divert, &mut sink); isa.emit_inst(&func, inst, &mut divert, &mut sink);
let emitted = sink.offset - before; let emitted = sink.offset - before;
@@ -231,11 +244,39 @@ impl SubTest for TestBinEmit {
// Check against bin: directives. // Check against bin: directives.
if let Some(want) = bins.remove(&inst) { if let Some(want) = bins.remove(&inst) {
if !enc.is_legal() { if !enc.is_legal() {
return Err(format!( // A possible cause of an unencoded instruction is a missing location for
"{} can't be encoded: {}", // one of the input operands.
inst, if let Some(&v) = func.dfg.inst_args(inst).iter().find(|&&v| {
func.dfg.display_inst(inst, isa) !func.locations[v].is_assigned()
)); })
{
return Err(format!(
"Missing register/stack slot for {} in {}",
v,
func.dfg.display_inst(inst, isa)
));
}
// Do any encodings exist?
let encodings = isa.legal_encodings(
&func.dfg,
&func.dfg[inst],
func.dfg.ctrl_typevar(inst),
).map(|e| encinfo.display(e))
.collect::<Vec<_>>();
if encodings.is_empty() {
return Err(format!(
"No encodings found for: {}",
func.dfg.display_inst(inst, isa)
));
} else {
return Err(format!(
"No matching encodings for {} in {}",
func.dfg.display_inst(inst, isa),
DisplayList(&encodings),
));
}
} }
let have = sink.text.trim(); let have = sink.text.trim();
if have != want { if have != want {