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