// 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::hir; use rustc::hir::def_id::DefId; use rustc::mir::*; use rustc::mir::transform::MirSource; use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::{Idx}; use std::fmt::Display; use std::fs; use std::io::{self, Write}; use std::path::{PathBuf, Path}; const INDENT: &'static str = " "; /// Alignment for lining up comments following MIR statements const ALIGN: usize = 40; /// If the session is properly configured, dumps a human-readable /// representation of the mir into: /// /// ```text /// rustc.node.. /// ``` /// /// Output from this function is controlled by passing `-Z dump-mir=`, /// where `` takes the following forms: /// /// - `all` -- dump MIR for all fns, all passes, all everything /// - `substring1&substring2,...` -- `&`-separated list of substrings /// that can appear in the pass-name or the `item_path_str` for the given /// node-id. If any one of the substrings match, the data is dumped out. pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pass_name: &str, disambiguator: &Display, src: MirSource, mir: &Mir<'tcx>) { let filters = match tcx.sess.opts.debugging_opts.dump_mir { None => return, Some(ref filters) => filters, }; let node_id = src.item_id(); let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id)); let is_matched = filters.split("&") .any(|filter| { filter == "all" || pass_name.contains(filter) || node_path.contains(filter) }); if !is_matched { return; } let promotion_id = match src { MirSource::Promoted(_, id) => format!("-{:?}", id), _ => String::new() }; let mut file_path = PathBuf::new(); if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir { let p = Path::new(file_dir); file_path.push(p); }; let file_name = format!("rustc.node{}{}.{}.{}.mir", node_id, promotion_id, pass_name, disambiguator); file_path.push(&file_name); let _ = fs::File::create(&file_path).and_then(|mut file| { writeln!(file, "// MIR for `{}`", node_path)?; writeln!(file, "// node_id = {}", node_id)?; writeln!(file, "// pass_name = {}", pass_name)?; writeln!(file, "// disambiguator = {}", disambiguator)?; writeln!(file, "")?; write_mir_fn(tcx, src, mir, &mut file)?; Ok(()) }); } /// Write out a human-readable textual representation for the given MIR. pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>, iter: I, w: &mut Write) -> io::Result<()> where I: Iterator, 'tcx: 'a { let mut first = true; for def_id in iter { let mir = &tcx.item_mir(def_id); if first { first = false; } else { // Put empty lines between all items writeln!(w, "")?; } let id = tcx.hir.as_local_node_id(def_id).unwrap(); let src = MirSource::from_node(tcx, id); write_mir_fn(tcx, src, mir, w)?; for (i, mir) in mir.promoted.iter_enumerated() { writeln!(w, "")?; write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?; } } Ok(()) } pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &Mir<'tcx>, w: &mut Write) -> io::Result<()> { write_mir_intro(tcx, src, mir, w)?; for block in mir.basic_blocks().indices() { write_basic_block(tcx, block, mir, w)?; if block.index() + 1 != mir.basic_blocks().len() { writeln!(w, "")?; } } writeln!(w, "}}")?; Ok(()) } /// Write out a human-readable textual representation for the given basic block. fn write_basic_block(tcx: TyCtxt, block: BasicBlock, mir: &Mir, w: &mut Write) -> io::Result<()> { let data = &mir[block]; // Basic block label at the top. writeln!(w, "{}{:?}: {{", INDENT, block)?; // List of statements in the middle. let mut current_location = Location { block: block, statement_index: 0 }; for statement in &data.statements { let indented_mir = format!("{0}{0}{1:?};", INDENT, statement); writeln!(w, "{0:1$} // {2}", indented_mir, ALIGN, comment(tcx, statement.source_info))?; current_location.statement_index += 1; } // Terminator at the bottom. let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); writeln!(w, "{0:1$} // {2}", indented_terminator, ALIGN, comment(tcx, data.terminator().source_info))?; writeln!(w, "{}}}", INDENT) } fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String { format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span)) } /// Prints user-defined variables in a scope tree. /// /// Returns the total number of variables printed. fn write_scope_tree(tcx: TyCtxt, mir: &Mir, scope_tree: &FxHashMap>, w: &mut Write, parent: VisibilityScope, depth: usize) -> io::Result<()> { let indent = depth * INDENT.len(); let children = match scope_tree.get(&parent) { Some(childs) => childs, None => return Ok(()), }; for &child in children { let data = &mir.visibility_scopes[child]; assert_eq!(data.parent_scope, Some(parent)); writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?; // User variable types (including the user's name in a comment). for local in mir.vars_iter() { let var = &mir.local_decls[local]; let (name, source_info) = if var.source_info.unwrap().scope == child { (var.name.unwrap(), var.source_info.unwrap()) } else { // Not a variable or not declared in this scope. continue; }; let mut_str = if var.mutability == Mutability::Mut { "mut " } else { "" }; let indent = indent + INDENT.len(); let indented_var = format!("{0:1$}let {2}{3:?}: {4};", INDENT, indent, mut_str, local, var.ty); writeln!(w, "{0:1$} // \"{2}\" in {3}", indented_var, ALIGN, name, comment(tcx, source_info))?; } write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?; writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; } Ok(()) } /// 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<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &Mir, w: &mut Write) -> io::Result<()> { write_mir_sig(tcx, src, mir, w)?; writeln!(w, " {{")?; // construct a scope tree and write it out let mut scope_tree: FxHashMap> = FxHashMap(); for (index, scope_data) in mir.visibility_scopes.iter().enumerate() { if let Some(parent) = scope_data.parent_scope { scope_tree.entry(parent) .or_insert(vec![]) .push(VisibilityScope::new(index)); } else { // Only the argument scope has no parent, because it's the root. assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index()); } } // Print return pointer let indented_retptr = format!("{}let mut {:?}: {};", INDENT, RETURN_POINTER, mir.return_ty); writeln!(w, "{0:1$} // return pointer", indented_retptr, ALIGN)?; write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?; write_temp_decls(mir, w)?; // Add an empty line before the first block is printed. writeln!(w, "")?; Ok(()) } fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) -> io::Result<()> { match src { MirSource::Fn(_) => write!(w, "fn")?, MirSource::Const(_) => write!(w, "const")?, MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?, MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?, MirSource::Promoted(_, i) => write!(w, "{:?} in", i)? } write!(w, " {}", tcx.node_path_str(src.item_id()))?; if let MirSource::Fn(_) = src { write!(w, "(")?; // fn argument types. for (i, arg) in mir.args_iter().enumerate() { if i != 0 { write!(w, ", ")?; } write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?; } write!(w, ") -> {}", mir.return_ty) } else { assert_eq!(mir.arg_count, 0); write!(w, ": {} =", mir.return_ty) } } fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> { // Compiler-introduced temporary types. for temp in mir.temps_iter() { writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?; } Ok(()) }