Print value aliases at referrent definition (#492)
* Print value aliases at referrent definition Closes #488.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! Data flow graph tracking Instructions, Values, and EBBs.
|
||||
|
||||
use entity::{EntityMap, PrimaryMap};
|
||||
use entity::{self, EntityMap, PrimaryMap};
|
||||
use ir;
|
||||
use ir::builder::ReplaceBuilder;
|
||||
use ir::extfunc::ExtFuncData;
|
||||
@@ -149,6 +149,38 @@ fn resolve_aliases(values: &PrimaryMap<Value, ValueData>, value: Value) -> Value
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over all Values in a DFG
|
||||
pub struct Values<'a> {
|
||||
inner: entity::Iter<'a, Value, ValueData>,
|
||||
}
|
||||
|
||||
/// Check for non-values
|
||||
fn valid_valuedata(data: &ValueData) -> bool {
|
||||
if let &ValueData::Alias {
|
||||
ty: types::VOID,
|
||||
original,
|
||||
} = data
|
||||
{
|
||||
if original == Value::reserved_value() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Values<'a> {
|
||||
type Item = Value;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
return self
|
||||
.inner
|
||||
.by_ref()
|
||||
.filter(|kv| valid_valuedata(kv.1))
|
||||
.next()
|
||||
.map(|kv| kv.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Handling values.
|
||||
///
|
||||
/// Values are either EBB parameters or instruction results.
|
||||
@@ -158,6 +190,13 @@ impl DataFlowGraph {
|
||||
self.values.push(data)
|
||||
}
|
||||
|
||||
/// Get an iterator over all values.
|
||||
pub fn values<'a>(&'a self) -> Values {
|
||||
Values {
|
||||
inner: self.values.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a value reference is valid.
|
||||
pub fn value_is_valid(&self, v: Value) -> bool {
|
||||
self.values.is_valid(v)
|
||||
@@ -932,9 +971,9 @@ impl DataFlowGraph {
|
||||
}
|
||||
|
||||
/// Create a new value alias. This is only for use by the parser to create
|
||||
/// aliases with specific values.
|
||||
/// aliases with specific values, and the printer for testing.
|
||||
#[cold]
|
||||
pub fn make_value_alias_for_parser(&mut self, src: Value, dest: Value) {
|
||||
pub fn make_value_alias_for_serialization(&mut self, src: Value, dest: Value) {
|
||||
assert_ne!(src, Value::reserved_value());
|
||||
assert_ne!(dest, Value::reserved_value());
|
||||
|
||||
@@ -951,9 +990,9 @@ impl DataFlowGraph {
|
||||
|
||||
/// If `v` is already defined as an alias, return its destination value.
|
||||
/// Otherwise return None. This allows the parser to coalesce identical
|
||||
/// alias definitions.
|
||||
/// alias definitions, and the printer to identify an alias's immediate target.
|
||||
#[cold]
|
||||
pub fn value_alias_dest_for_parser(&self, v: Value) -> Option<Value> {
|
||||
pub fn value_alias_dest_for_serialization(&self, v: Value) -> Option<Value> {
|
||||
if let ValueData::Alias { original, .. } = self.values[v] {
|
||||
Some(original)
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! Utility routines for pretty-printing error messages.
|
||||
|
||||
use entity::EntityMap;
|
||||
use ir;
|
||||
use ir::entities::{AnyEntity, Inst};
|
||||
use ir::entities::{AnyEntity, Inst, Value};
|
||||
use ir::function::Function;
|
||||
use isa::TargetIsa;
|
||||
use result::CodegenError;
|
||||
@@ -39,11 +40,12 @@ impl<'a> FuncWriter for PrettyVerifierError<'a> {
|
||||
&mut self,
|
||||
w: &mut Write,
|
||||
func: &Function,
|
||||
aliases: &EntityMap<Value, Vec<Value>>,
|
||||
isa: Option<&TargetIsa>,
|
||||
inst: Inst,
|
||||
indent: usize,
|
||||
) -> fmt::Result {
|
||||
pretty_instruction_error(w, func, isa, inst, indent, &mut *self.0, self.1)
|
||||
pretty_instruction_error(w, func, aliases, isa, inst, indent, &mut *self.0, self.1)
|
||||
}
|
||||
|
||||
fn write_entity_definition(
|
||||
@@ -61,6 +63,7 @@ impl<'a> FuncWriter for PrettyVerifierError<'a> {
|
||||
fn pretty_instruction_error(
|
||||
w: &mut Write,
|
||||
func: &Function,
|
||||
aliases: &EntityMap<Value, Vec<Value>>,
|
||||
isa: Option<&TargetIsa>,
|
||||
cur_inst: Inst,
|
||||
indent: usize,
|
||||
@@ -77,7 +80,7 @@ fn pretty_instruction_error(
|
||||
let err = errors.remove(i);
|
||||
|
||||
if !printed_instr {
|
||||
func_w.write_instruction(w, func, isa, cur_inst, indent)?;
|
||||
func_w.write_instruction(w, func, aliases, isa, cur_inst, indent)?;
|
||||
printed_instr = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//! The `write` module provides the `write_function` function which converts an IR `Function` to an
|
||||
//! equivalent textual form. This textual form can be read back by the `cranelift-reader` crate.
|
||||
|
||||
use entity::EntityMap;
|
||||
use ir::entities::AnyEntity;
|
||||
use ir::{DataFlowGraph, Ebb, Function, Inst, SigRef, Type, Value, ValueDef};
|
||||
use isa::{RegInfo, TargetIsa};
|
||||
@@ -17,6 +18,7 @@ pub trait FuncWriter {
|
||||
&mut self,
|
||||
w: &mut Write,
|
||||
func: &Function,
|
||||
aliases: &EntityMap<Value, Vec<Value>>,
|
||||
isa: Option<&TargetIsa>,
|
||||
inst: Inst,
|
||||
ident: usize,
|
||||
@@ -94,11 +96,12 @@ impl FuncWriter for PlainWriter {
|
||||
&mut self,
|
||||
w: &mut Write,
|
||||
func: &Function,
|
||||
aliases: &EntityMap<Value, Vec<Value>>,
|
||||
isa: Option<&TargetIsa>,
|
||||
inst: Inst,
|
||||
indent: usize,
|
||||
) -> fmt::Result {
|
||||
write_instruction(w, func, isa, inst, indent)
|
||||
write_instruction(w, func, aliases, isa, inst, indent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +111,18 @@ pub fn write_function(w: &mut Write, func: &Function, isa: Option<&TargetIsa>) -
|
||||
decorate_function(&mut PlainWriter, w, func, isa)
|
||||
}
|
||||
|
||||
/// Create a reverse-alias map from a value to all aliases having that value as a direct target
|
||||
fn alias_map(func: &Function) -> EntityMap<Value, Vec<Value>> {
|
||||
let mut aliases = EntityMap::<_, Vec<_>>::new();
|
||||
for v in func.dfg.values() {
|
||||
// VADFS returns the immediate target of an alias
|
||||
if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
|
||||
aliases[k].push(v);
|
||||
}
|
||||
}
|
||||
aliases
|
||||
}
|
||||
|
||||
/// Writes `func` to `w` as text.
|
||||
/// write_function_plain is passed as 'closure' to print instructions as text.
|
||||
/// pretty_function_error is passed as 'closure' to add error decoration.
|
||||
@@ -123,12 +138,13 @@ pub fn decorate_function<FW: FuncWriter>(
|
||||
write!(w, "function ")?;
|
||||
write_spec(w, func, regs)?;
|
||||
writeln!(w, " {{")?;
|
||||
let aliases = alias_map(func);
|
||||
let mut any = func_w.write_preamble(w, func, regs)?;
|
||||
for ebb in &func.layout {
|
||||
if any {
|
||||
writeln!(w)?;
|
||||
}
|
||||
decorate_ebb(func_w, w, func, isa, ebb)?;
|
||||
decorate_ebb(func_w, w, func, &aliases, isa, ebb)?;
|
||||
any = true;
|
||||
}
|
||||
writeln!(w, "}}")
|
||||
@@ -195,6 +211,7 @@ fn decorate_ebb<FW: FuncWriter>(
|
||||
func_w: &mut FW,
|
||||
w: &mut Write,
|
||||
func: &Function,
|
||||
aliases: &EntityMap<Value, Vec<Value>>,
|
||||
isa: Option<&TargetIsa>,
|
||||
ebb: Ebb,
|
||||
) -> fmt::Result {
|
||||
@@ -206,8 +223,11 @@ fn decorate_ebb<FW: FuncWriter>(
|
||||
};
|
||||
|
||||
write_ebb_header(w, func, isa, ebb, indent)?;
|
||||
for a in func.dfg.ebb_params(ebb).iter().cloned() {
|
||||
write_value_aliases(w, aliases, a, indent)?;
|
||||
}
|
||||
for inst in func.layout.ebb_insts(ebb) {
|
||||
func_w.write_instruction(w, func, isa, inst, indent)?;
|
||||
func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -251,13 +271,16 @@ fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
|
||||
Some(rtype)
|
||||
}
|
||||
|
||||
// Write out any value aliases appearing in `inst`.
|
||||
fn write_value_aliases(w: &mut Write, func: &Function, inst: Inst, indent: usize) -> fmt::Result {
|
||||
for &arg in func.dfg.inst_args(inst) {
|
||||
let resolved = func.dfg.resolve_aliases(arg);
|
||||
if resolved != arg {
|
||||
writeln!(w, "{1:0$}{2} -> {3}", indent, "", arg, resolved)?;
|
||||
}
|
||||
/// Write out any aliases to the given target, including indirect aliases
|
||||
fn write_value_aliases(
|
||||
w: &mut Write,
|
||||
aliases: &EntityMap<Value, Vec<Value>>,
|
||||
target: Value,
|
||||
indent: usize,
|
||||
) -> fmt::Result {
|
||||
for &a in &aliases[target] {
|
||||
writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
|
||||
write_value_aliases(w, aliases, a, indent)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -265,13 +288,11 @@ fn write_value_aliases(w: &mut Write, func: &Function, inst: Inst, indent: usize
|
||||
fn write_instruction(
|
||||
w: &mut Write,
|
||||
func: &Function,
|
||||
aliases: &EntityMap<Value, Vec<Value>>,
|
||||
isa: Option<&TargetIsa>,
|
||||
inst: Inst,
|
||||
indent: usize,
|
||||
) -> fmt::Result {
|
||||
// Value aliases come out on lines before the instruction using them.
|
||||
write_value_aliases(w, func, inst, indent)?;
|
||||
|
||||
// Prefix containing source location, encoding, and value locations.
|
||||
let mut s = String::with_capacity(16);
|
||||
|
||||
@@ -324,7 +345,13 @@ fn write_instruction(
|
||||
}
|
||||
|
||||
write_operands(w, &func.dfg, isa, inst)?;
|
||||
writeln!(w)
|
||||
writeln!(w)?;
|
||||
|
||||
// Value aliases come out on lines after the instruction defining the referrent.
|
||||
for r in func.dfg.inst_results(inst) {
|
||||
write_value_aliases(w, aliases, *r, indent)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the operands of `inst` to `w` with a prepended space.
|
||||
@@ -623,4 +650,40 @@ mod tests {
|
||||
"function %foo() fast {\n ss0 = explicit_slot 4\n\nebb0(v0: i8, v1: f32x4):\n return\n}\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliases() {
|
||||
use ir::InstBuilder;
|
||||
|
||||
let mut func = Function::new();
|
||||
{
|
||||
let ebb0 = func.dfg.make_ebb();
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_ebb(ebb0);
|
||||
|
||||
// make some detached values for change_to_alias
|
||||
let v0 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
|
||||
let v1 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
|
||||
let v2 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
|
||||
pos.func.dfg.detach_ebb_params(ebb0);
|
||||
|
||||
// alias to a param--will be printed at beginning of ebb defining param
|
||||
let v3 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
|
||||
pos.func.dfg.change_to_alias(v0, v3);
|
||||
|
||||
// alias to an alias--should print attached to alias, not ultimate target
|
||||
pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
|
||||
|
||||
// alias to a result--will be printed after instruction producing result
|
||||
let _dummy0 = pos.ins().iconst(types::I32, 42);
|
||||
let v4 = pos.ins().iadd(v0, v0);
|
||||
pos.func.dfg.change_to_alias(v1, v4);
|
||||
let _dummy1 = pos.ins().iconst(types::I32, 23);
|
||||
let _v7 = pos.ins().iadd(v1, v1);
|
||||
}
|
||||
assert_eq!(
|
||||
func.to_string(),
|
||||
"function u0:0() fast {\nebb0(v3: i32):\n v0 -> v3\n v2 -> v0\n v4 = iconst.i32 42\n v5 = iadd v0, v0\n v1 -> v5\n v6 = iconst.i32 23\n v7 = iadd v1, v1\n}\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1688,7 +1688,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
// Allow duplicate definitions of aliases, as long as they are identical.
|
||||
if ctx.map.contains_value(result) {
|
||||
if let Some(old) = ctx.function.dfg.value_alias_dest_for_parser(result) {
|
||||
if let Some(old) = ctx.function.dfg.value_alias_dest_for_serialization(result) {
|
||||
if old != dest {
|
||||
return err!(
|
||||
self.loc,
|
||||
@@ -1708,7 +1708,9 @@ impl<'a> Parser<'a> {
|
||||
return err!(self.loc, "value {} is not yet defined", dest);
|
||||
}
|
||||
|
||||
ctx.function.dfg.make_value_alias_for_parser(dest, result);
|
||||
ctx.function
|
||||
.dfg
|
||||
.make_value_alias_for_serialization(dest, result);
|
||||
|
||||
ctx.aliases.push(result);
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user