Rewrite MIR graphviz printing and improve MIR debug printing.

This commit is contained in:
Scott Olson 2015-12-18 19:29:03 -06:00
parent 27a1834ce5
commit 9000ecf761
5 changed files with 236 additions and 200 deletions

View file

@ -11,14 +11,15 @@
use middle::const_eval::ConstVal;
use middle::def_id::DefId;
use middle::subst::Substs;
use middle::ty::{AdtDef, ClosureSubsts, FnOutput, Region, Ty};
use middle::ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
use rustc_back::slice;
use rustc_data_structures::tuple_slice::TupleSlice;
use rustc_front::hir::InlineAsm;
use syntax::ast::Name;
use syntax::codemap::Span;
use std::fmt::{Debug, Formatter, Error};
use std::u32;
use std::borrow::{Cow, IntoCow};
use std::fmt::{Debug, Formatter, Error, Write};
use std::{iter, u32};
/// Lowered representation of a single function.
#[derive(RustcEncodable, RustcDecodable)]
@ -317,23 +318,43 @@ impl<'tcx> BasicBlockData<'tcx> {
impl<'tcx> Debug for Terminator<'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
try!(self.fmt_head(fmt));
let successors = self.successors();
let labels = self.fmt_successor_labels();
assert_eq!(successors.len(), labels.len());
match successors.len() {
0 => Ok(()),
1 => write!(fmt, " -> {:?}", successors[0]),
_ => {
try!(write!(fmt, " -> ["));
for (i, target) in successors.iter().enumerate() {
if i > 0 {
try!(write!(fmt, ", "));
}
try!(write!(fmt, "{}: {:?}", labels[i], target));
}
write!(fmt, "]")
}
}
}
}
impl<'tcx> Terminator<'tcx> {
pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> Result<(), Error> {
use self::Terminator::*;
match *self {
Goto { target } =>
write!(fmt, "goto -> {:?}", target),
Panic { target } =>
write!(fmt, "panic -> {:?}", target),
If { cond: ref lv, ref targets } =>
write!(fmt, "if({:?}) -> {:?}", lv, targets),
Switch { discr: ref lv, adt_def: _, ref targets } =>
write!(fmt, "switch({:?}) -> {:?}", lv, targets),
SwitchInt { discr: ref lv, switch_ty: _, ref values, ref targets } =>
write!(fmt, "switchInt({:?}, {:?}) -> {:?}", lv, values, targets),
Diverge =>
write!(fmt, "diverge"),
Return =>
write!(fmt, "return"),
Call { data: ref c, targets } => {
Goto { .. } => write!(fmt, "goto"),
Panic { .. } => write!(fmt, "panic"),
If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv),
Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv),
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
Diverge => write!(fmt, "diverge"),
Return => write!(fmt, "return"),
Call { data: ref c, .. } => {
try!(write!(fmt, "{:?} = {:?}(", c.destination, c.func));
for (index, arg) in c.args.iter().enumerate() {
if index > 0 {
@ -341,7 +362,33 @@ impl<'tcx> Debug for Terminator<'tcx> {
}
try!(write!(fmt, "{:?}", arg));
}
write!(fmt, ") -> {:?}", targets)
write!(fmt, ")")
}
}
}
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
use self::Terminator::*;
match *self {
Diverge | Return => vec![],
Goto { .. } | Panic { .. } => vec!["".into_cow()],
If { .. } => vec!["true".into_cow(), "false".into_cow()],
Call { .. } => vec!["return".into_cow(), "unwind".into_cow()],
Switch { ref adt_def, .. } => {
adt_def.variants
.iter()
.map(|variant| variant.name.to_string().into_cow())
.collect()
}
SwitchInt { ref values, .. } => {
values.iter()
.map(|const_val| {
let mut buf = String::new();
fmt_const_val(&mut buf, const_val).unwrap();
buf.into_cow()
})
.chain(iter::once(String::from("otherwise").into_cow()))
.collect()
}
}
}
@ -495,19 +542,19 @@ impl<'tcx> Debug for Lvalue<'tcx> {
match *self {
Var(id) =>
write!(fmt,"Var({:?})", id),
write!(fmt,"v{:?}", id),
Arg(id) =>
write!(fmt,"Arg({:?})", id),
write!(fmt,"a{:?}", id),
Temp(id) =>
write!(fmt,"Temp({:?})", id),
write!(fmt,"t{:?}", id),
Static(id) =>
write!(fmt,"Static({:?})", id),
ReturnPointer =>
write!(fmt,"ReturnPointer"),
Projection(ref data) =>
match data.elem {
ProjectionElem::Downcast(_, variant_index) =>
write!(fmt,"({:?} as {:?})", data.base, variant_index),
ProjectionElem::Downcast(ref adt_def, index) =>
write!(fmt,"({:?} as {})", data.base, adt_def.variants[index].name),
ProjectionElem::Deref =>
write!(fmt,"(*{:?})", data.base),
ProjectionElem::Field(field) =>
@ -671,12 +718,12 @@ impl<'tcx> Debug for Rvalue<'tcx> {
Use(ref lvalue) => write!(fmt, "{:?}", lvalue),
Repeat(ref a, ref b) => write!(fmt, "[{:?}; {:?}]", a, b),
Ref(ref a, bk, ref b) => write!(fmt, "&{:?} {:?} {:?}", a, bk, b),
Len(ref a) => write!(fmt, "LEN({:?})", a),
Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?}", lv, ty, kind),
BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?},{:?})", op, a, b),
Len(ref a) => write!(fmt, "Len({:?})", a),
Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?})", lv, ty, kind),
BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b),
UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
Box(ref t) => write!(fmt, "Box {:?}", t),
Aggregate(ref kind, ref lvs) => write!(fmt, "Aggregate<{:?}>({:?})", kind, lvs),
Box(ref t) => write!(fmt, "Box({:?})", t),
Aggregate(ref kind, ref lvs) => write!(fmt, "Aggregate<{:?}>{:?}", kind, lvs),
InlineAsm(ref asm) => write!(fmt, "InlineAsm({:?})", asm),
Slice { ref input, from_start, from_end } =>
write!(fmt, "{:?}[{:?}..-{:?}]", input, from_start, from_end),
@ -691,7 +738,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
// this does not necessarily mean that they are "==" in Rust -- in
// particular one must be wary of `NaN`!
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Constant<'tcx> {
pub span: Span,
pub ty: Ty<'tcx>,
@ -707,7 +754,7 @@ pub enum ItemKind {
Method,
}
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum Literal<'tcx> {
Item {
def_id: DefId,
@ -718,3 +765,37 @@ pub enum Literal<'tcx> {
value: ConstVal,
},
}
impl<'tcx> Debug for Constant<'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
write!(fmt, "{:?}", self.literal)
}
}
impl<'tcx> Debug for Literal<'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
use self::Literal::*;
match *self {
Item { def_id, .. } =>
write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))),
Value { ref value } => fmt_const_val(fmt, value),
}
}
}
pub fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> Result<(), Error> {
use middle::const_eval::ConstVal::*;
match *const_val {
Float(f) => write!(fmt, "{:?}", f),
Int(n) => write!(fmt, "{:?}", n),
Uint(n) => write!(fmt, "{:?}", n),
Str(ref s) => write!(fmt, "Str({:?})", s),
ByteStr(ref bytes) => write!(fmt, "ByteStr{:?}", bytes),
Bool(b) => write!(fmt, "{:?}", b),
Struct(id) => write!(fmt, "Struct({:?})", id),
Tuple(id) => write!(fmt, "Tuple({:?})", id),
Function(def_id) => write!(fmt, "Function({:?})", def_id),
Array(id, n) => write!(fmt, "Array({:?}, {:?})", id, n),
Repeat(id, n) => write!(fmt, "Repeat({:?}, {:?})", id, n),
}
}

View file

@ -0,0 +1,120 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use dot;
use rustc::mir::repr::*;
use rustc::middle::ty;
use std::io::{self, Write};
pub fn write_mir_graphviz<W: Write>(mir: &Mir, w: &mut W) -> io::Result<()> {
try!(writeln!(w, "digraph Mir {{"));
// Global graph properties
try!(writeln!(w, r#"graph [fontname="monospace"];"#));
try!(writeln!(w, r#"node [fontname="monospace"];"#));
try!(writeln!(w, r#"edge [fontname="monospace"];"#));
// Graph label
try!(write_graph_label(mir, w));
// Nodes
for block in mir.all_basic_blocks() {
try!(write_node(block, mir, w));
}
// Edges
for source in mir.all_basic_blocks() {
try!(write_edges(source, mir, w));
}
writeln!(w, "}}")
}
fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
let data = mir.basic_block_data(block);
try!(write!(w, r#"bb{} [shape="none", label=<"#, block.index()));
try!(write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#));
try!(write!(w, r#"<tr><td bgcolor="gray" align="center">"#));
try!(write!(w, "{}", block.index()));
try!(write!(w, "</td></tr>"));
if !data.statements.is_empty() {
try!(write!(w, r#"<tr><td align="left" balign="left">"#));
for statement in &data.statements {
try!(write!(w, "{}", dot::escape_html(&format!("{:?}", statement))));
try!(write!(w, "<br/>"));
}
try!(write!(w, "</td></tr>"));
}
try!(write!(w, r#"<tr><td align="left">"#));
let mut terminator_head = String::new();
data.terminator.fmt_head(&mut terminator_head).unwrap();
try!(write!(w, "{}", dot::escape_html(&terminator_head)));
try!(write!(w, "</td></tr>"));
try!(write!(w, "</table>"));
writeln!(w, ">];")
}
fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
let terminator = &mir.basic_block_data(source).terminator;
let labels = terminator.fmt_successor_labels();
for (i, target) in terminator.successors().into_iter().enumerate() {
try!(write!(w, "bb{} -> bb{}", source.index(), target.index()));
try!(writeln!(w, r#" [label="{}"];"#, labels[i]));
}
Ok(())
}
fn write_graph_label<W: Write>(mir: &Mir, w: &mut W) -> io::Result<()> {
try!(write!(w, "label=<"));
try!(write!(w, "fn("));
for (i, arg) in mir.arg_decls.iter().enumerate() {
if i > 0 {
try!(write!(w, ", "));
}
try!(write!(w, "{}", dot::escape_html(&format!("a{}: {:?}", i, arg.ty))));
}
try!(write!(w, "{}", dot::escape_html(") -> ")));
match mir.return_ty {
ty::FnOutput::FnConverging(ty) =>
try!(write!(w, "{}", dot::escape_html(&format!("{:?}", ty)))),
ty::FnOutput::FnDiverging =>
try!(write!(w, "{}", dot::escape_html("!"))),
}
try!(write!(w, r#"<br align="left"/>"#));
for (i, var) in mir.var_decls.iter().enumerate() {
try!(write!(w, "let "));
if var.mutability == Mutability::Mut {
try!(write!(w, "mut "));
}
let text = format!("v{}: {:?}; // {}", i, var.ty, var.name);
try!(write!(w, "{}", dot::escape_html(&text)));
try!(write!(w, r#"<br align="left"/>"#));
}
for (i, temp) in mir.temp_decls.iter().enumerate() {
try!(write!(w, "{}", dot::escape_html(&format!("let t{}: {:?};", i, temp.ty))));
try!(write!(w, r#"<br align="left"/>"#));
}
writeln!(w, ">;")
}

View file

@ -1,166 +0,0 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use dot;
use rustc::mir::repr::*;
use std::borrow::IntoCow;
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct EdgeIndex {
source: BasicBlock,
target: BasicBlock,
index: usize,
}
impl<'a,'tcx> dot::Labeller<'a, BasicBlock, EdgeIndex> for Mir<'tcx> {
fn graph_id(&'a self) -> dot::Id<'a> {
dot::Id::new("Mir").unwrap()
}
fn node_id(&'a self, n: &BasicBlock) -> dot::Id<'a> {
dot::Id::new(format!("BB{}", n.index())).unwrap()
}
fn node_shape(&'a self, _: &BasicBlock) -> Option<dot::LabelText<'a>> {
Some(dot::LabelText::label("none"))
}
fn node_label(&'a self, &n: &BasicBlock) -> dot::LabelText<'a> {
let mut buffer = String::new();
buffer.push_str("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">");
buffer.push_str("<tr><td><b>");
buffer.push_str(&escape(format!("{:?}", n)));
buffer.push_str("</b></td></tr>");
let data = self.basic_block_data(n);
for statement in &data.statements {
buffer.push_str("<tr><td align=\"left\">");
buffer.push_str(&escape(format!("{:?}", statement)));
buffer.push_str("</td></tr>");
}
buffer.push_str("<tr><td align=\"left\" bgcolor=\"cornsilk\">");
buffer.push_str(&escape(format!("{:?}", &data.terminator)));
buffer.push_str("</td></tr>");
buffer.push_str("</table>");
dot::LabelText::html(buffer)
}
fn edge_label(&'a self, edge: &EdgeIndex) -> dot::LabelText<'a> {
dot::LabelText::label(format!("{}", edge.index))
}
}
impl<'a,'tcx> dot::GraphWalk<'a, BasicBlock, EdgeIndex> for Mir<'tcx> {
fn nodes(&'a self) -> dot::Nodes<'a, BasicBlock> {
self.all_basic_blocks().into_cow()
}
fn edges(&'a self) -> dot::Edges<'a, EdgeIndex> {
self.all_basic_blocks()
.into_iter()
.flat_map(|source| {
self.basic_block_data(source)
.terminator
.successors()
.iter()
.enumerate()
.map(move |(index, &target)| {
EdgeIndex {
source: source,
target: target,
index: index,
}
})
})
.collect::<Vec<_>>()
.into_cow()
}
fn source(&'a self, edge: &EdgeIndex) -> BasicBlock {
edge.source
}
fn target(&'a self, edge: &EdgeIndex) -> BasicBlock {
edge.target
}
}
fn escape(text: String) -> String {
let text = dot::escape_html(&text);
let text = all_to_subscript("Temp", text);
let text = all_to_subscript("Var", text);
let text = all_to_subscript("Arg", text);
let text = all_to_subscript("BB", text);
text
}
/// A call like `all_to_subscript("Temp", "Temp(123)")` will convert
/// to `Temp₁₂₃`.
fn all_to_subscript(header: &str, mut text: String) -> String {
let mut offset = 0;
while offset < text.len() {
if let Some(text1) = to_subscript1(header, &text, &mut offset) {
text = text1;
}
}
return text;
/// Looks for `Foo(\d*)` where `header=="Foo"` and replaces the `\d` with subscripts.
/// Updates `offset` to point to the next location where we might want to search.
/// Returns an updated string if changes were made, else None.
fn to_subscript1(header: &str, text: &str, offset: &mut usize) -> Option<String> {
let a = match text[*offset..].find(header) {
None => {
*offset = text.len();
return None;
}
Some(a) => a + *offset,
};
// Example:
//
// header: "Foo"
// text: ....Foo(123)...
// ^ ^
// a b
let b = a + header.len();
*offset = b;
let mut chars = text[b..].chars();
if Some('(') != chars.next() {
return None;
}
let mut result = String::new();
result.push_str(&text[..b]);
while let Some(c) = chars.next() {
if c == ')' {
break;
}
if !c.is_digit(10) {
return None;
}
// 0x208 is _0 in unicode, 0x209 is _1, etc
const SUBSCRIPTS: &'static str = "₀₁₂₃₄₅₆₇₈₉";
let n = (c as usize) - ('0' as usize);
result.extend(SUBSCRIPTS.chars().skip(n).take(1));
}
result.extend(chars);
return Some(result);
}
}

View file

@ -19,7 +19,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![crate_type = "dylib"]
#![feature(rustc_private)]
#![feature(into_cow)]
#[macro_use] extern crate log;
extern crate graphviz as dot;

View file

@ -21,7 +21,7 @@ extern crate rustc;
extern crate rustc_front;
use build;
use dot;
use graphviz;
use transform::*;
use rustc::mir::repr::Mir;
use hair::cx::Cx;
@ -157,7 +157,9 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
Some(s) => {
match
File::create(format!("{}{}", prefix, s))
.and_then(|ref mut output| dot::render(&mir, output))
.and_then(|ref mut output| {
graphviz::write_mir_graphviz(&mir, output)
})
{
Ok(()) => { }
Err(e) => {