From 661976cbd1ca3f9cd32d60cc0c48d9b291d7887f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 29 Dec 2015 20:06:19 -0600 Subject: [PATCH 1/8] Add a human-readable textual form for MIR. This can be dumped for a particular `fn` with the attribute `#![rustc_mir(pretty = "filename.mir"]`. --- src/librustc_mir/lib.rs | 6 +-- src/librustc_mir/mir_map.rs | 29 +++++++------ src/librustc_mir/pretty.rs | 85 +++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 17 deletions(-) create mode 100644 src/librustc_mir/pretty.rs diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 6a1134385d9..9cc40bbc383 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -29,8 +29,8 @@ extern crate syntax; pub mod build; -pub mod mir_map; +pub mod graphviz; mod hair; -mod graphviz; +pub mod mir_map; +pub mod pretty; pub mod transform; - diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 5c9399ebdad..a3ca4c05456 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,6 +22,7 @@ use build; use graphviz; +use pretty; use transform::*; use rustc::mir::repr::Mir; use hair::cx::Cx; @@ -152,29 +153,29 @@ fn visit_fn(&mut self, .flat_map(|a| a.meta_item_list()) .flat_map(|l| l.iter()); for item in meta_item_list { - if item.check_name("graphviz") { + if item.check_name("graphviz") || item.check_name("pretty") { match item.value_str() { Some(s) => { - match - File::create(format!("{}{}", prefix, s)) - .and_then(|ref mut output| { + let filename = format!("{}{}", prefix, s); + let result = File::create(&filename).and_then(|ref mut output| { + if item.check_name("graphviz") { graphviz::write_mir_graphviz(&mir, output) - }) - { - Ok(()) => { } - Err(e) => { - self.tcx.sess.span_fatal( - item.span, - &format!("Error writing graphviz \ - results to `{}`: {}", - s, e)); + } else { + pretty::write_mir_pretty(&mir, output) } + }); + + if let Err(e) = result { + self.tcx.sess.span_fatal( + item.span, + &format!("Error writing MIR {} results to `{}`: {}", + item.name(), filename, e)); } } None => { self.tcx.sess.span_err( item.span, - "graphviz attribute requires a path"); + &format!("{} attribute requires a path", item.name())); } } } diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs new file mode 100644 index 00000000000..1251154891b --- /dev/null +++ b/src/librustc_mir/pretty.rs @@ -0,0 +1,85 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::mir::repr::*; +use rustc::middle::ty; +use std::io::{self, Write}; + +const INDENT: &'static str = " "; + +/// Write out a human-readable textual representation for the given MIR. +pub fn write_mir_pretty(mir: &Mir, w: &mut W) -> io::Result<()> { + try!(write_mir_intro(mir, w)); + + // Nodes + for block in mir.all_basic_blocks() { + try!(write_basic_block(block, mir, w)); + } + + writeln!(w, "}}") +} + +/// Write out a human-readable textual representation for the given basic block. +fn write_basic_block(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { + let data = mir.basic_block_data(block); + + // Basic block label at the top. + try!(writeln!(w, "\n{}{:?}: {{", INDENT, block)); + + // List of statements in the middle. + for statement in &data.statements { + try!(writeln!(w, "{0}{0}{1:?};", INDENT, statement)); + } + + // Terminator at the bottom. + try!(writeln!(w, "{0}{0}{1:?};", INDENT, data.terminator)); + + writeln!(w, "{}}}", INDENT) +} + +/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its +/// local variables (both user-defined bindings and compiler temporaries). +fn write_mir_intro(mir: &Mir, w: &mut W) -> io::Result<()> { + try!(write!(w, "fn(")); + + // fn argument types. + for (i, arg) in mir.arg_decls.iter().enumerate() { + if i > 0 { + try!(write!(w, ", ")); + } + try!(write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty)); + } + + try!(write!(w, ") -> ")); + + // fn return type. + match mir.return_ty { + ty::FnOutput::FnConverging(ty) => try!(write!(w, "{}", ty)), + ty::FnOutput::FnDiverging => try!(write!(w, "!")), + } + + try!(writeln!(w, " {{")); + + // User variable types (including the user's name in a comment). + for (i, var) in mir.var_decls.iter().enumerate() { + try!(write!(w, "{}let ", INDENT)); + if var.mutability == Mutability::Mut { + try!(write!(w, "mut ")); + } + try!(writeln!(w, "{:?}: {}; // {}", Lvalue::Var(i as u32), var.ty, var.name)); + } + + // Compiler-introduced temporary types. + for (i, temp) in mir.temp_decls.iter().enumerate() { + try!(writeln!(w, "{}let {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty)); + } + + Ok(()) +} From 5a0c1b3a8876e34534926dd2d0e447ca97b9b05d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 29 Dec 2015 20:10:39 -0600 Subject: [PATCH 2/8] Print BasicBlock names in lowercase. --- src/librustc/mir/repr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index b6e86077ccf..93e1cffa6bc 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -184,7 +184,7 @@ pub fn index(self) -> usize { impl Debug for BasicBlock { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { - write!(fmt, "BB({})", self.0) + write!(fmt, "bb{}", self.0) } } From 9db76f311de670dd591c7c8ebb14799942c9161f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 31 Dec 2015 20:11:25 -0600 Subject: [PATCH 3/8] Use fmt::Result instead of Result<(), Error>. --- src/librustc/mir/repr.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 93e1cffa6bc..d6d3a1470a9 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -18,7 +18,7 @@ use syntax::ast::Name; use syntax::codemap::Span; use std::borrow::{Cow, IntoCow}; -use std::fmt::{Debug, Formatter, Error, Write}; +use std::fmt::{self, Debug, Formatter, Write}; use std::{iter, u32}; /// Lowered representation of a single function. @@ -183,7 +183,7 @@ pub fn index(self) -> usize { } impl Debug for BasicBlock { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { write!(fmt, "bb{}", self.0) } } @@ -317,7 +317,7 @@ pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> { } impl<'tcx> Debug for Terminator<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { try!(self.fmt_head(fmt)); let successors = self.successors(); let labels = self.fmt_successor_labels(); @@ -347,7 +347,7 @@ impl<'tcx> Terminator<'tcx> { /// Write the "head" part of the terminator; that is, its name and the data it uses to pick the /// successor basic block, if any. The only information not inlcuded is the list of possible /// successors, which may be rendered differently between the text and the graphviz format. - pub fn fmt_head(&self, fmt: &mut W) -> Result<(), Error> { + pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { use self::Terminator::*; match *self { Goto { .. } => write!(fmt, "goto"), @@ -421,7 +421,7 @@ pub enum DropKind { } impl<'tcx> Debug for Statement<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::StatementKind::*; match self.kind { Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv), @@ -541,7 +541,7 @@ pub fn elem(self, elem: LvalueElem<'tcx>) -> Lvalue<'tcx> { } impl<'tcx> Debug for Lvalue<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::Lvalue::*; match *self { @@ -588,7 +588,7 @@ pub enum Operand<'tcx> { } impl<'tcx> Debug for Operand<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::Operand::*; match *self { Constant(ref a) => write!(fmt, "{:?}", a), @@ -715,7 +715,7 @@ pub enum UnOp { } impl<'tcx> Debug for Rvalue<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::Rvalue::*; match *self { @@ -771,13 +771,13 @@ pub enum Literal<'tcx> { } impl<'tcx> Debug for Constant<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { write!(fmt, "{:?}", self.literal) } } impl<'tcx> Debug for Literal<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { use self::Literal::*; match *self { Item { def_id, .. } => @@ -788,7 +788,7 @@ fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { } /// Write a `ConstVal` in a way closer to the original source code than the `Debug` output. -pub fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> Result<(), Error> { +pub fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { use middle::const_eval::ConstVal::*; match *const_val { Float(f) => write!(fmt, "{:?}", f), From 522354415ea4f4c9bfbaddee249a367b173df1af Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 31 Dec 2015 21:38:44 -0600 Subject: [PATCH 4/8] Pretty-print aggregates more prettily in MIR. --- src/librustc/mir/repr.rs | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index d6d3a1470a9..56cd7432a73 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -727,10 +727,68 @@ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 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), InlineAsm(ref asm) => write!(fmt, "InlineAsm({:?})", asm), Slice { ref input, from_start, from_end } => write!(fmt, "{:?}[{:?}..-{:?}]", input, from_start, from_end), + + Aggregate(ref kind, ref lvs) => { + use self::AggregateKind::*; + + fn fmt_tuple(fmt: &mut Formatter, name: &str, lvs: &[Operand]) -> fmt::Result { + let mut tuple_fmt = fmt.debug_tuple(name); + for lv in lvs { + tuple_fmt.field(lv); + } + tuple_fmt.finish() + } + + match *kind { + Vec => write!(fmt, "{:?}", lvs), + + Tuple => { + if lvs.len() == 1 { + write!(fmt, "({:?},)", lvs[0]) + } else { + fmt_tuple(fmt, "", lvs) + } + } + + Adt(adt_def, variant, _) => { + let variant_def = &adt_def.variants[variant]; + let name = ty::tls::with(|tcx| tcx.item_path_str(variant_def.did)); + + match variant_def.kind() { + ty::VariantKind::Unit => write!(fmt, "{}", name), + ty::VariantKind::Tuple => fmt_tuple(fmt, &name, lvs), + ty::VariantKind::Struct => { + let mut struct_fmt = fmt.debug_struct(&name); + for (field, lv) in variant_def.fields.iter().zip(lvs) { + struct_fmt.field(&field.name.as_str(), lv); + } + struct_fmt.finish() + } + } + } + + Closure(def_id, _) => ty::tls::with(|tcx| { + if let Some(node_id) = tcx.map.as_local_node_id(def_id) { + let name = format!("[closure@{:?}]", tcx.map.span(node_id)); + let mut struct_fmt = fmt.debug_struct(&name); + + tcx.with_freevars(node_id, |freevars| { + for (freevar, lv) in freevars.iter().zip(lvs) { + let var_name = tcx.local_var_name_str(freevar.def.var_id()); + struct_fmt.field(&var_name, lv); + } + }); + + struct_fmt.finish() + } else { + write!(fmt, "[closure]") + } + }), + } + } } } } From 6a33221ea54ad4a496c06f5d93fb1e32c8076454 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 1 Jan 2016 00:39:02 -0600 Subject: [PATCH 5/8] Improve pretty-printing of references in MIR. --- src/librustc/mir/repr.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 56cd7432a73..9a5f675ad4b 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -721,7 +721,6 @@ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { match *self { 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), @@ -731,6 +730,14 @@ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Slice { ref input, from_start, from_end } => write!(fmt, "{:?}[{:?}..-{:?}]", input, from_start, from_end), + Ref(_, borrow_kind, ref lv) => { + let kind_str = match borrow_kind { + BorrowKind::Shared => "", + BorrowKind::Mut | BorrowKind::Unique => "mut ", + }; + write!(fmt, "&{}{:?}", kind_str, lv) + } + Aggregate(ref kind, ref lvs) => { use self::AggregateKind::*; From 19a50e4f2a899ad2b4a7b05709dcfd8450a7bbdc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 1 Jan 2016 21:01:17 -0600 Subject: [PATCH 6/8] Pretty-print static lvalues in MIR as just their path. --- src/librustc/mir/repr.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 9a5f675ad4b..74cd698dd5a 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -551,24 +551,24 @@ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { write!(fmt,"arg{:?}", id), Temp(id) => write!(fmt,"tmp{:?}", id), - Static(id) => - write!(fmt,"Static({:?})", id), + Static(def_id) => + write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))), ReturnPointer => - write!(fmt,"ReturnPointer"), + write!(fmt, "ReturnPointer"), Projection(ref data) => match data.elem { ProjectionElem::Downcast(ref adt_def, index) => - write!(fmt,"({:?} as {})", data.base, adt_def.variants[index].name), + write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name), ProjectionElem::Deref => - write!(fmt,"(*{:?})", data.base), + write!(fmt, "(*{:?})", data.base), ProjectionElem::Field(field) => - write!(fmt,"{:?}.{:?}", data.base, field.index()), + write!(fmt, "{:?}.{:?}", data.base, field.index()), ProjectionElem::Index(ref index) => - write!(fmt,"{:?}[{:?}]", data.base, index), + write!(fmt, "{:?}[{:?}]", data.base, index), ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => - write!(fmt,"{:?}[{:?} of {:?}]", data.base, offset, min_length), + write!(fmt, "{:?}[{:?} of {:?}]", data.base, offset, min_length), ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => - write!(fmt,"{:?}[-{:?} of {:?}]", data.base, offset, min_length), + write!(fmt, "{:?}[-{:?} of {:?}]", data.base, offset, min_length), }, } } From c9a7171e10f62ba01d263a57b4242beb8d4832dd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 1 Jan 2016 21:56:29 -0600 Subject: [PATCH 7/8] Pretty-print ReturnPointer as 'return' in MIR. --- src/librustc/mir/repr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 74cd698dd5a..75a588d424e 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -554,7 +554,7 @@ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Static(def_id) => write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))), ReturnPointer => - write!(fmt, "ReturnPointer"), + write!(fmt, "return"), Projection(ref data) => match data.elem { ProjectionElem::Downcast(ref adt_def, index) => From 080994a189c7b3680e822d3f6ddf448c152e6374 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 2 Jan 2016 07:10:25 -0600 Subject: [PATCH 8/8] Add 'mut' to MIR temp variable debug output. --- src/librustc_mir/pretty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index 1251154891b..163559f2792 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -78,7 +78,7 @@ fn write_mir_intro(mir: &Mir, w: &mut W) -> io::Result<()> { // Compiler-introduced temporary types. for (i, temp) in mir.temp_decls.iter().enumerate() { - try!(writeln!(w, "{}let {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty)); + try!(writeln!(w, "{}let mut {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty)); } Ok(())