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