use rustc::hir::def_id::DefId; use rustc::mir::*; use rustc::ty::TyCtxt; use rustc_data_structures::indexed_vec::Idx; use std::fmt::Debug; use std::io::{self, Write}; use super::pretty::dump_mir_def_ids; /// Write a graphviz DOT graph of a list of MIRs. pub fn write_mir_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>, single: Option, w: &mut W) -> io::Result<()> where W: Write { for def_id in dump_mir_def_ids(tcx, single) { let mir = &tcx.optimized_mir(def_id); write_mir_fn_graphviz(tcx, def_id, mir, w)?; } Ok(()) } // Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so // it does not have to be user friendly. pub fn graphviz_safe_def_name(def_id: DefId) -> String { format!( "{}_{}_{}", def_id.krate.index(), def_id.index.address_space().index(), def_id.index.as_array_index(), ) } /// Write a graphviz DOT graph of the MIR. pub fn write_mir_fn_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>, def_id: DefId, mir: &Mir<'_>, w: &mut W) -> io::Result<()> where W: Write { writeln!(w, "digraph Mir_{} {{", graphviz_safe_def_name(def_id))?; // Global graph properties writeln!(w, r#" graph [fontname="monospace"];"#)?; writeln!(w, r#" node [fontname="monospace"];"#)?; writeln!(w, r#" edge [fontname="monospace"];"#)?; // Graph label write_graph_label(tcx, def_id, mir, w)?; // Nodes for (block, _) in mir.basic_blocks().iter_enumerated() { write_node(block, mir, w)?; } // Edges for (source, _) in mir.basic_blocks().iter_enumerated() { write_edges(source, mir, w)?; } writeln!(w, "}}") } /// Write a graphviz HTML-styled label for the given basic block, with /// all necessary escaping already performed. (This is suitable for /// emitting directly, as is done in this module, or for use with the /// LabelText::HtmlStr from libgraphviz.) /// /// `init` and `fini` are callbacks for emitting additional rows of /// data (using HTML enclosed with `` in the emitted text). pub fn write_node_label(block: BasicBlock, mir: &Mir<'_>, w: &mut W, num_cols: u32, init: INIT, fini: FINI) -> io::Result<()> where INIT: Fn(&mut W) -> io::Result<()>, FINI: Fn(&mut W) -> io::Result<()> { let data = &mir[block]; write!(w, r#""#)?; // Basic block number at the top. write!(w, r#""#, attrs=r#"bgcolor="gray" align="center""#, colspan=num_cols, blk=block.index())?; init(w)?; // List of statements in the middle. if !data.statements.is_empty() { write!(w, r#"")?; } // Terminator head at the bottom, not including the list of successor blocks. Those will be // displayed as labels on the edges between blocks. let mut terminator_head = String::new(); data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); write!(w, r#""#, dot::escape_html(&terminator_head))?; fini(w)?; // Close the table writeln!(w, "
{blk}
"#)?; for statement in &data.statements { write!(w, "{}
", escape(statement))?; } write!(w, "
{}
") } /// Write a graphviz DOT node for the given basic block. fn write_node(block: BasicBlock, mir: &Mir<'_>, w: &mut W) -> io::Result<()> { // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. write!(w, r#" {} [shape="none", label=<"#, node(block))?; write_node_label(block, mir, w, 1, |_| Ok(()), |_| Ok(()))?; // Close the node label and the node itself. writeln!(w, ">];") } /// Write graphviz DOT edges with labels between the given basic block and all of its successors. fn write_edges(source: BasicBlock, mir: &Mir<'_>, w: &mut W) -> io::Result<()> { let terminator = mir[source].terminator(); let labels = terminator.kind.fmt_successor_labels(); for (&target, label) in terminator.successors().zip(labels) { writeln!(w, r#" {} -> {} [label="{}"];"#, node(source), node(target), label)?; } Ok(()) } /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of /// all the variables and temporaries. fn write_graph_label<'a, 'gcx, 'tcx, W: Write>(tcx: TyCtxt<'a, 'gcx, 'tcx>, def_id: DefId, mir: &Mir<'_>, w: &mut W) -> io::Result<()> { write!(w, " label= 0 { write!(w, ", ")?; } write!(w, "{:?}: {}", Place::Base(PlaceBase::Local(arg)), escape(&mir.local_decls[arg].ty) )?; } write!(w, ") -> {}", escape(&mir.return_ty()))?; write!(w, r#"
"#)?; for local in mir.vars_and_temps_iter() { let decl = &mir.local_decls[local]; write!(w, "let ")?; if decl.mutability == Mutability::Mut { write!(w, "mut ")?; } if let Some(name) = decl.name { write!(w, r#"{:?}: {}; // {}
"#, Place::Base(PlaceBase::Local(local)), escape(&decl.ty), name)?; } else { write!(w, r#"let mut {:?}: {};
"#, Place::Base(PlaceBase::Local(local)), escape(&decl.ty))?; } } writeln!(w, ">;") } fn node(block: BasicBlock) -> String { format!("bb{}", block.index()) } fn escape(t: &T) -> String { dot::escape_html(&format!("{:?}", t)) }