]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/pretty.rs
change MIR dump filenames from `nodeN` to `DefPath`
[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::ty::item_path;
17 use rustc_data_structures::fx::FxHashMap;
18 use rustc_data_structures::indexed_vec::{Idx};
19 use std::fmt::Display;
20 use std::fs;
21 use std::io::{self, Write};
22 use std::path::{PathBuf, Path};
23 use super::graphviz::write_mir_fn_graphviz;
24
25 const INDENT: &'static str = "    ";
26 /// Alignment for lining up comments following MIR statements
27 const ALIGN: usize = 40;
28
29 /// An indication of where we are in the control flow graph. Used for printing
30 /// extra information in `dump_mir`
31 pub enum PassWhere {
32     /// We have not started dumping the control flow graph, but we are about to.
33     BeforeCFG,
34
35     /// We just finished dumping the control flow graph. This is right before EOF
36     AfterCFG,
37
38     /// We are about to start dumping the given basic block.
39     BeforeBlock(BasicBlock),
40
41     /// We are just about to dumpt the given statement or terminator.
42     InCFG(Location),
43 }
44
45 /// If the session is properly configured, dumps a human-readable
46 /// representation of the mir into:
47 ///
48 /// ```text
49 /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
50 /// ```
51 ///
52 /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
53 /// where `<filter>` takes the following forms:
54 ///
55 /// - `all` -- dump MIR for all fns, all passes, all everything
56 /// - `substring1&substring2,...` -- `&`-separated list of substrings
57 ///   that can appear in the pass-name or the `item_path_str` for the given
58 ///   node-id. If any one of the substrings match, the data is dumped out.
59 pub fn dump_mir<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
60                                    pass_num: Option<(MirSuite, MirPassIndex)>,
61                                    pass_name: &str,
62                                    disambiguator: &Display,
63                                    source: MirSource,
64                                    mir: &Mir<'tcx>,
65                                    extra_data: F)
66 where
67     F: FnMut(PassWhere, &mut Write) -> io::Result<()>
68 {
69     if !dump_enabled(tcx, pass_name, source) {
70         return;
71     }
72
73     let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
74         tcx.item_path_str(tcx.hir.local_def_id(source.item_id()))
75     });
76     dump_matched_mir_node(tcx, pass_num, pass_name, &node_path,
77                           disambiguator, source, mir, extra_data);
78 }
79
80 pub fn dump_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
81                                     pass_name: &str,
82                                     source: MirSource)
83                                     -> bool {
84     let filters = match tcx.sess.opts.debugging_opts.dump_mir {
85         None => return false,
86         Some(ref filters) => filters,
87     };
88     let node_id = source.item_id();
89     let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
90         tcx.item_path_str(tcx.hir.local_def_id(node_id))
91     });
92     filters.split("&")
93            .any(|filter| {
94                filter == "all" ||
95                    pass_name.contains(filter) ||
96                    node_path.contains(filter)
97            })
98 }
99
100 // #41697 -- we use `with_forced_impl_filename_line()` because
101 // `item_path_str()` would otherwise trigger `type_of`, and this can
102 // run while we are already attempting to evaluate `type_of`.
103
104 fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
105                                             pass_num: Option<(MirSuite, MirPassIndex)>,
106                                             pass_name: &str,
107                                             node_path: &str,
108                                             disambiguator: &Display,
109                                             source: MirSource,
110                                             mir: &Mir<'tcx>,
111                                             mut extra_data: F)
112 where
113     F: FnMut(PassWhere, &mut Write) -> io::Result<()>
114 {
115     let promotion_id = match source {
116         MirSource::Promoted(_, id) => format!("-{:?}", id),
117         MirSource::GeneratorDrop(_) => format!("-drop"),
118         _ => String::new()
119     };
120
121     let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
122         format!("")
123     } else {
124         match pass_num {
125             None => format!(".-------"),
126             Some((suite, pass_num)) => format!(".{:03}-{:03}", suite.0, pass_num.0),
127         }
128     };
129
130     let mut file_path = PathBuf::new();
131     if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
132         let p = Path::new(file_dir);
133         file_path.push(p);
134     };
135
136     let _ = fs::create_dir_all(&file_path);
137     let function_name  = tcx.hir.def_path_from_id(source.item_id())
138         .map(|d| d.to_filename_friendly_no_crate()) .unwrap_or(format!(".node{}", source.item_id()));
139     let file_name = format!("rustc{}{}{}.{}.{}.mir",
140                             function_name, promotion_id, pass_num, pass_name, disambiguator);
141     file_path.push(&file_name);
142     let _ = fs::File::create(&file_path).and_then(|mut file| {
143         writeln!(file, "// MIR for `{}`", node_path)?;
144         writeln!(file, "// source = {:?}", source)?;
145         writeln!(file, "// pass_name = {}", pass_name)?;
146         writeln!(file, "// disambiguator = {}", disambiguator)?;
147         if let Some(ref layout) = mir.generator_layout {
148             writeln!(file, "// generator_layout = {:?}", layout)?;
149         }
150         writeln!(file, "")?;
151         extra_data(PassWhere::BeforeCFG, &mut file)?;
152         write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?;
153         extra_data(PassWhere::AfterCFG, &mut file)?;
154         Ok(())
155     });
156
157     if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
158         file_path.set_extension("dot");
159         let _ = fs::File::create(&file_path).and_then(|mut file| {
160             write_mir_fn_graphviz(tcx, source.item_id(), mir, &mut file)?;
161             Ok(())
162         });
163     }
164 }
165
166 /// Write out a human-readable textual representation for the given MIR.
167 pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
168                                         single: Option<DefId>,
169                                         w: &mut Write)
170                                         -> io::Result<()>
171 {
172     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
173     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
174
175     let mut first = true;
176     for def_id in dump_mir_def_ids(tcx, single) {
177         let mir = &tcx.optimized_mir(def_id);
178
179         if first {
180             first = false;
181         } else {
182             // Put empty lines between all items
183             writeln!(w, "")?;
184         }
185
186         let id = tcx.hir.as_local_node_id(def_id).unwrap();
187         let src = MirSource::from_node(tcx, id);
188         write_mir_fn(tcx, src, mir, &mut |_, _| Ok(()), w)?;
189
190         for (i, mir) in mir.promoted.iter_enumerated() {
191             writeln!(w, "")?;
192             write_mir_fn(tcx, MirSource::Promoted(id, i), mir, &mut |_, _| Ok(()), w)?;
193         }
194     }
195     Ok(())
196 }
197
198 pub fn write_mir_fn<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
199                                        src: MirSource,
200                                        mir: &Mir<'tcx>,
201                                        extra_data: &mut F,
202                                        w: &mut Write)
203                                        -> io::Result<()>
204 where
205     F: FnMut(PassWhere, &mut Write) -> io::Result<()>
206 {
207     write_mir_intro(tcx, src, mir, w)?;
208     for block in mir.basic_blocks().indices() {
209         extra_data(PassWhere::BeforeBlock(block), w)?;
210         write_basic_block(tcx, block, mir, extra_data, w)?;
211         if block.index() + 1 != mir.basic_blocks().len() {
212             writeln!(w, "")?;
213         }
214     }
215
216     writeln!(w, "}}")?;
217     Ok(())
218 }
219
220 /// Write out a human-readable textual representation for the given basic block.
221 pub fn write_basic_block<F>(tcx: TyCtxt,
222                             block: BasicBlock,
223                             mir: &Mir,
224                             extra_data: &mut F,
225                             w: &mut Write)
226                             -> io::Result<()>
227 where
228     F: FnMut(PassWhere, &mut Write) -> io::Result<()>
229 {
230     let data = &mir[block];
231
232     // Basic block label at the top.
233     let cleanup_text = if data.is_cleanup { " // cleanup" } else { "" };
234     let lbl = format!("{}{:?}: {{", INDENT, block);
235     writeln!(w, "{0:1$}{2}", lbl, ALIGN, cleanup_text)?;
236
237     // List of statements in the middle.
238     let mut current_location = Location { block: block, statement_index: 0 };
239     for statement in &data.statements {
240         extra_data(PassWhere::InCFG(current_location), w)?;
241         let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
242         writeln!(w, "{0:1$} // {2}",
243                  indented_mir,
244                  ALIGN,
245                  comment(tcx, statement.source_info))?;
246
247         current_location.statement_index += 1;
248     }
249
250     // Terminator at the bottom.
251     extra_data(PassWhere::InCFG(current_location), w)?;
252     let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
253     writeln!(w, "{0:1$} // {2}",
254              indented_terminator,
255              ALIGN,
256              comment(tcx, data.terminator().source_info))?;
257
258     writeln!(w, "{}}}", INDENT)
259 }
260
261 fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
262     format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
263 }
264
265 /// Prints user-defined variables in a scope tree.
266 ///
267 /// Returns the total number of variables printed.
268 fn write_scope_tree(tcx: TyCtxt,
269                     mir: &Mir,
270                     scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
271                     w: &mut Write,
272                     parent: VisibilityScope,
273                     depth: usize)
274                     -> io::Result<()> {
275     let indent = depth * INDENT.len();
276
277     let children = match scope_tree.get(&parent) {
278         Some(childs) => childs,
279         None => return Ok(()),
280     };
281
282     for &child in children {
283         let data = &mir.visibility_scopes[child];
284         assert_eq!(data.parent_scope, Some(parent));
285         writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
286
287         // User variable types (including the user's name in a comment).
288         for local in mir.vars_iter() {
289             let var = &mir.local_decls[local];
290             let (name, source_info) = if var.source_info.scope == child {
291                 (var.name.unwrap(), var.source_info)
292             } else {
293                 // Not a variable or not declared in this scope.
294                 continue;
295             };
296
297             let mut_str = if var.mutability == Mutability::Mut {
298                 "mut "
299             } else {
300                 ""
301             };
302
303             let indent = indent + INDENT.len();
304             let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
305                                        INDENT,
306                                        indent,
307                                        mut_str,
308                                        local,
309                                        var.ty);
310             writeln!(w, "{0:1$} // \"{2}\" in {3}",
311                      indented_var,
312                      ALIGN,
313                      name,
314                      comment(tcx, source_info))?;
315         }
316
317         write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
318
319         writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
320     }
321
322     Ok(())
323 }
324
325 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
326 /// local variables (both user-defined bindings and compiler temporaries).
327 pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
328                                        src: MirSource,
329                                        mir: &Mir,
330                                        w: &mut Write)
331                                        -> io::Result<()> {
332     write_mir_sig(tcx, src, mir, w)?;
333     writeln!(w, " {{")?;
334
335     // construct a scope tree and write it out
336     let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
337     for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
338         if let Some(parent) = scope_data.parent_scope {
339             scope_tree.entry(parent)
340                       .or_insert(vec![])
341                       .push(VisibilityScope::new(index));
342         } else {
343             // Only the argument scope has no parent, because it's the root.
344             assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
345         }
346     }
347
348     // Print return pointer
349     let indented_retptr = format!("{}let mut {:?}: {};",
350                                   INDENT,
351                                   RETURN_POINTER,
352                                   mir.return_ty);
353     writeln!(w, "{0:1$} // return pointer",
354              indented_retptr,
355              ALIGN)?;
356
357     write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
358
359     write_temp_decls(mir, w)?;
360
361     // Add an empty line before the first block is printed.
362     writeln!(w, "")?;
363
364     Ok(())
365 }
366
367 fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
368                  -> io::Result<()>
369 {
370     match src {
371         MirSource::Fn(_) => write!(w, "fn")?,
372         MirSource::Const(_) => write!(w, "const")?,
373         MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
374         MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
375         MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?,
376         MirSource::GeneratorDrop(_) => write!(w, "drop_glue")?,
377     }
378
379     item_path::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere
380         write!(w, " {}", tcx.node_path_str(src.item_id()))
381     })?;
382
383     match src {
384         MirSource::Fn(_) | MirSource::GeneratorDrop(_) => {
385             write!(w, "(")?;
386
387             // fn argument types.
388             for (i, arg) in mir.args_iter().enumerate() {
389                 if i != 0 {
390                     write!(w, ", ")?;
391                 }
392                 write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
393             }
394
395             write!(w, ") -> {}", mir.return_ty)
396         }
397         MirSource::Const(..) |
398         MirSource::Static(..) |
399         MirSource::Promoted(..) => {
400             assert_eq!(mir.arg_count, 0);
401             write!(w, ": {} =", mir.return_ty)
402         }
403     }
404 }
405
406 fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
407     // Compiler-introduced temporary types.
408     for temp in mir.temps_iter() {
409         writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
410     }
411
412     Ok(())
413 }
414
415 pub fn dump_mir_def_ids(tcx: TyCtxt, single: Option<DefId>) -> Vec<DefId> {
416     if let Some(i) = single {
417         vec![i]
418     } else {
419         tcx.mir_keys(LOCAL_CRATE).iter().cloned().collect()
420     }
421 }