Auto merge of #30707 - tsion:mir-text, r=nikomatsakis

r? @nikomatsakis

Textual MIR can be dumped for a particular `fn` with `#![rustc_mir(pretty = "filename.mir")]`. Below is an example of the text output.

```rust
struct Point {
    x: i32,
    y: i32,
}

fn example() -> Point {
    let mut e = Point { x: 1, y: 2 };

    let num = 5;
    let plus_num = |x: i32| x + num;

    e.y = plus_num(e.x);
    e
}
```

```rust
fn() -> Point {
    let mut var0: Point; // e
    let var1: i32; // num
    let var2: [closure@test.rs:84:20: 84:36 num:&i32]; // plus_num
    let mut tmp0: ();
    let mut tmp1: &i32;
    let mut tmp2: ();
    let mut tmp3: i32;
    let mut tmp4: &[closure@test.rs:84:20: 84:36 num:&i32];
    let mut tmp5: i32;
    let mut tmp6: Point;

    bb0: {
        var0 = Point { x: 1, y: 2 };
        var1 = 5;
        tmp1 = &var1;
        var2 = [closure@test.rs:84:20: 84:36] { num: tmp1 };
        tmp4 = &var2;
        tmp5 = var0.0;
        tmp3 = tmp4(tmp5) -> [return: bb3, unwind: bb4];
    }

    bb1: {
        return;
    }

    bb2: {
        diverge;
    }

    bb3: {
        drop var0.1;
        var0.1 = tmp3;
        drop tmp2;
        drop var2;
        drop var0;
        tmp6 = var0;
        return = tmp6;
        drop tmp6;
        goto -> bb1;
    }

    bb4: {
        drop var2;
        goto -> bb5;
    }

    bb5: {
        drop var0;
        goto -> bb2;
    }
}
```

```rust
fn(arg0: &[closure@test.rs:84:20: 84:36 num:&i32], arg1: i32) -> i32 {
    let var0: i32; // x
    let mut tmp0: ();
    let mut tmp1: i32;
    let mut tmp2: i32;

    bb0: {
        var0 = arg1;
        tmp1 = var0;
        tmp2 = (*(*arg0).0);
        return = Add(tmp1, tmp2);
        goto -> bb1;
    }

    bb1: {
        return;
    }

    bb2: {
        diverge;
    }
}
```
This commit is contained in:
bors 2016-01-05 01:43:07 +00:00
commit e2d649405a
4 changed files with 191 additions and 40 deletions

View File

@ -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,8 +183,8 @@ pub fn index(self) -> usize {
}
impl Debug for BasicBlock {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
write!(fmt, "BB({})", self.0)
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<W: Write>(&self, fmt: &mut W) -> Result<(), Error> {
pub fn fmt_head<W: Write>(&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 {
@ -551,10 +551,10 @@ fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
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, "return"),
Projection(ref data) =>
match data.elem {
ProjectionElem::Downcast(ref adt_def, index) =>
@ -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,22 +715,87 @@ 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 {
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),
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),
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::*;
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]")
}
}),
}
}
}
}
}
@ -771,13 +836,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 +853,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<W: Write>(fmt: &mut W, const_val: &ConstVal) -> Result<(), Error> {
pub fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
use middle::const_eval::ConstVal::*;
match *const_val {
Float(f) => write!(fmt, "{:?}", f),

View File

@ -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;

View File

@ -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) => {
} else {
pretty::write_mir_pretty(&mir, output)
}
});
if let Err(e) = result {
self.tcx.sess.span_fatal(
item.span,
&format!("Error writing graphviz \
results to `{}`: {}",
s, e));
}
&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()));
}
}
}

View File

@ -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 <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 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<W: Write>(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<W: Write>(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<W: Write>(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 mut {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty));
}
Ok(())
}