]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/pretty.rs
rename `MirPassSet` to `MirSuite`
[rust.git] / src / librustc_mir / util / pretty.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc::hir;
12 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
13 use rustc::mir::*;
14 use rustc::mir::transform::{MirSuite, MirPassIndex, MirSource};
15 use rustc::ty::TyCtxt;
16 use rustc_data_structures::fx::FxHashMap;
17 use rustc_data_structures::indexed_vec::{Idx};
18 use std::fmt::Display;
19 use std::fs;
20 use std::io::{self, Write};
21 use std::path::{PathBuf, Path};
22
23 const INDENT: &'static str = "    ";
24 /// Alignment for lining up comments following MIR statements
25 const ALIGN: usize = 40;
26
27 /// If the session is properly configured, dumps a human-readable
28 /// representation of the mir into:
29 ///
30 /// ```text
31 /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
32 /// ```
33 ///
34 /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
35 /// where `<filter>` takes the following forms:
36 ///
37 /// - `all` -- dump MIR for all fns, all passes, all everything
38 /// - `substring1&substring2,...` -- `&`-separated list of substrings
39 ///   that can appear in the pass-name or the `item_path_str` for the given
40 ///   node-id. If any one of the substrings match, the data is dumped out.
41 pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
42                           pass_num: Option<(MirSuite, MirPassIndex)>,
43                           pass_name: &str,
44                           disambiguator: &Display,
45                           source: MirSource,
46                           mir: &Mir<'tcx>) {
47     if !dump_enabled(tcx, pass_name, source) {
48         return;
49     }
50
51     let node_path = tcx.item_path_str(tcx.hir.local_def_id(source.item_id()));
52     dump_matched_mir_node(tcx, pass_num, pass_name, &node_path,
53                           disambiguator, source, mir);
54     for (index, promoted_mir) in mir.promoted.iter_enumerated() {
55         let promoted_source = MirSource::Promoted(source.item_id(), index);
56         dump_matched_mir_node(tcx, pass_num, pass_name, &node_path, disambiguator,
57                               promoted_source, promoted_mir);
58     }
59 }
60
61 pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
62                               pass_name: &str,
63                               source: MirSource)
64                               -> bool {
65     let filters = match tcx.sess.opts.debugging_opts.dump_mir {
66         None => return false,
67         Some(ref filters) => filters,
68     };
69     let node_id = source.item_id();
70     let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
71     filters.split("&")
72            .any(|filter| {
73                filter == "all" ||
74                    pass_name.contains(filter) ||
75                    node_path.contains(filter)
76            })
77 }
78
79 fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
80                                    pass_num: Option<(MirSuite, MirPassIndex)>,
81                                    pass_name: &str,
82                                    node_path: &str,
83                                    disambiguator: &Display,
84                                    source: MirSource,
85                                    mir: &Mir<'tcx>) {
86     let promotion_id = match source {
87         MirSource::Promoted(_, id) => format!("-{:?}", id),
88         _ => String::new()
89     };
90
91     let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
92         format!("")
93     } else {
94         match pass_num {
95             None => format!(".-------"),
96             Some((pass_set, pass_num)) => format!(".{:03}-{:03}", pass_set.0, pass_num.0),
97         }
98     };
99
100     let mut file_path = PathBuf::new();
101     if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
102         let p = Path::new(file_dir);
103         file_path.push(p);
104     };
105     let file_name = format!("rustc.node{}{}{}.{}.{}.mir",
106                             source.item_id(), promotion_id, pass_num, pass_name, disambiguator);
107     file_path.push(&file_name);
108     let _ = fs::File::create(&file_path).and_then(|mut file| {
109         writeln!(file, "// MIR for `{}`", node_path)?;
110         writeln!(file, "// source = {:?}", source)?;
111         writeln!(file, "// pass_name = {}", pass_name)?;
112         writeln!(file, "// disambiguator = {}", disambiguator)?;
113         writeln!(file, "")?;
114         write_mir_fn(tcx, source, mir, &mut file)?;
115         Ok(())
116     });
117 }
118
119 /// Write out a human-readable textual representation for the given MIR.
120 pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
121                                   single: Option<DefId>,
122                                   w: &mut Write)
123                                   -> io::Result<()>
124 {
125     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
126     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
127
128     let mut first = true;
129     for def_id in dump_mir_def_ids(tcx, single) {
130         let mir = &tcx.item_mir(def_id);
131
132         if first {
133             first = false;
134         } else {
135             // Put empty lines between all items
136             writeln!(w, "")?;
137         }
138
139         let id = tcx.hir.as_local_node_id(def_id).unwrap();
140         let src = MirSource::from_node(tcx, id);
141         write_mir_fn(tcx, src, mir, w)?;
142
143         for (i, mir) in mir.promoted.iter_enumerated() {
144             writeln!(w, "")?;
145             write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
146         }
147     }
148     Ok(())
149 }
150
151 pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
152                               src: MirSource,
153                               mir: &Mir<'tcx>,
154                               w: &mut Write)
155                               -> io::Result<()> {
156     write_mir_intro(tcx, src, mir, w)?;
157     for block in mir.basic_blocks().indices() {
158         write_basic_block(tcx, block, mir, w)?;
159         if block.index() + 1 != mir.basic_blocks().len() {
160             writeln!(w, "")?;
161         }
162     }
163
164     writeln!(w, "}}")?;
165     Ok(())
166 }
167
168 /// Write out a human-readable textual representation for the given basic block.
169 fn write_basic_block(tcx: TyCtxt,
170                      block: BasicBlock,
171                      mir: &Mir,
172                      w: &mut Write)
173                      -> io::Result<()> {
174     let data = &mir[block];
175
176     // Basic block label at the top.
177     writeln!(w, "{}{:?}: {{", INDENT, block)?;
178
179     // List of statements in the middle.
180     let mut current_location = Location { block: block, statement_index: 0 };
181     for statement in &data.statements {
182         let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
183         writeln!(w, "{0:1$} // {2}",
184                  indented_mir,
185                  ALIGN,
186                  comment(tcx, statement.source_info))?;
187
188         current_location.statement_index += 1;
189     }
190
191     // Terminator at the bottom.
192     let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
193     writeln!(w, "{0:1$} // {2}",
194              indented_terminator,
195              ALIGN,
196              comment(tcx, data.terminator().source_info))?;
197
198     writeln!(w, "{}}}", INDENT)
199 }
200
201 fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
202     format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
203 }
204
205 /// Prints user-defined variables in a scope tree.
206 ///
207 /// Returns the total number of variables printed.
208 fn write_scope_tree(tcx: TyCtxt,
209                     mir: &Mir,
210                     scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
211                     w: &mut Write,
212                     parent: VisibilityScope,
213                     depth: usize)
214                     -> io::Result<()> {
215     let indent = depth * INDENT.len();
216
217     let children = match scope_tree.get(&parent) {
218         Some(childs) => childs,
219         None => return Ok(()),
220     };
221
222     for &child in children {
223         let data = &mir.visibility_scopes[child];
224         assert_eq!(data.parent_scope, Some(parent));
225         writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
226
227         // User variable types (including the user's name in a comment).
228         for local in mir.vars_iter() {
229             let var = &mir.local_decls[local];
230             let (name, source_info) = if var.source_info.scope == child {
231                 (var.name.unwrap(), var.source_info)
232             } else {
233                 // Not a variable or not declared in this scope.
234                 continue;
235             };
236
237             let mut_str = if var.mutability == Mutability::Mut {
238                 "mut "
239             } else {
240                 ""
241             };
242
243             let indent = indent + INDENT.len();
244             let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
245                                        INDENT,
246                                        indent,
247                                        mut_str,
248                                        local,
249                                        var.ty);
250             writeln!(w, "{0:1$} // \"{2}\" in {3}",
251                      indented_var,
252                      ALIGN,
253                      name,
254                      comment(tcx, source_info))?;
255         }
256
257         write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
258
259         writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
260     }
261
262     Ok(())
263 }
264
265 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
266 /// local variables (both user-defined bindings and compiler temporaries).
267 fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
268                              src: MirSource,
269                              mir: &Mir,
270                              w: &mut Write)
271                              -> io::Result<()> {
272     write_mir_sig(tcx, src, mir, w)?;
273     writeln!(w, " {{")?;
274
275     // construct a scope tree and write it out
276     let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
277     for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
278         if let Some(parent) = scope_data.parent_scope {
279             scope_tree.entry(parent)
280                       .or_insert(vec![])
281                       .push(VisibilityScope::new(index));
282         } else {
283             // Only the argument scope has no parent, because it's the root.
284             assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
285         }
286     }
287
288     // Print return pointer
289     let indented_retptr = format!("{}let mut {:?}: {};",
290                                   INDENT,
291                                   RETURN_POINTER,
292                                   mir.return_ty);
293     writeln!(w, "{0:1$} // return pointer",
294              indented_retptr,
295              ALIGN)?;
296
297     write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
298
299     write_temp_decls(mir, w)?;
300
301     // Add an empty line before the first block is printed.
302     writeln!(w, "")?;
303
304     Ok(())
305 }
306
307 fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
308                  -> io::Result<()>
309 {
310     match src {
311         MirSource::Fn(_) => write!(w, "fn")?,
312         MirSource::Const(_) => write!(w, "const")?,
313         MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
314         MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
315         MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
316     }
317
318     write!(w, " {}", tcx.node_path_str(src.item_id()))?;
319
320     if let MirSource::Fn(_) = src {
321         write!(w, "(")?;
322
323         // fn argument types.
324         for (i, arg) in mir.args_iter().enumerate() {
325             if i != 0 {
326                 write!(w, ", ")?;
327             }
328             write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
329         }
330
331         write!(w, ") -> {}", mir.return_ty)
332     } else {
333         assert_eq!(mir.arg_count, 0);
334         write!(w, ": {} =", mir.return_ty)
335     }
336 }
337
338 fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
339     // Compiler-introduced temporary types.
340     for temp in mir.temps_iter() {
341         writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
342     }
343
344     Ok(())
345 }
346
347 pub fn dump_mir_def_ids(tcx: TyCtxt, single: Option<DefId>) -> Vec<DefId> {
348     if let Some(i) = single {
349         vec![i]
350     } else {
351         tcx.mir_keys(LOCAL_CRATE).iter().cloned().collect()
352     }
353 }