]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/pretty.rs
515620d425389676617e3efa5f9cbba8cf5c96b3
[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::{Location, ScopeAuxiliaryVec, ScopeId};
12 use rustc::hir;
13 use rustc::mir::repr::*;
14 use rustc::mir::transform::MirSource;
15 use rustc::ty::{self, TyCtxt};
16 use rustc_data_structures::fnv::FnvHashMap;
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 syntax::ast::NodeId;
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_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_name: &str,
43                           disambiguator: &Display,
44                           src: MirSource,
45                           mir: &Mir<'tcx>,
46                           auxiliary: Option<&ScopeAuxiliaryVec>) {
47     let filters = match tcx.sess.opts.debugging_opts.dump_mir {
48         None => return,
49         Some(ref filters) => filters,
50     };
51     let node_id = src.item_id();
52     let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id));
53     let is_matched =
54         filters.split("&")
55                .any(|filter| {
56                    filter == "all" ||
57                        pass_name.contains(filter) ||
58                        node_path.contains(filter)
59                });
60     if !is_matched {
61         return;
62     }
63
64     let promotion_id = match src {
65         MirSource::Promoted(_, id) => format!("-{:?}", id),
66         _ => String::new()
67     };
68
69     let file_name = format!("rustc.node{}{}.{}.{}.mir",
70                             node_id, promotion_id, pass_name, disambiguator);
71     let _ = fs::File::create(&file_name).and_then(|mut file| {
72         try!(writeln!(file, "// MIR for `{}`", node_path));
73         try!(writeln!(file, "// node_id = {}", node_id));
74         try!(writeln!(file, "// pass_name = {}", pass_name));
75         try!(writeln!(file, "// disambiguator = {}", disambiguator));
76         try!(writeln!(file, ""));
77         try!(write_mir_fn(tcx, src, mir, &mut file, auxiliary));
78         Ok(())
79     });
80 }
81
82 /// Write out a human-readable textual representation for the given MIR.
83 pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
84                                          iter: I,
85                                          w: &mut Write)
86                                          -> io::Result<()>
87     where I: Iterator<Item=(&'a NodeId, &'a Mir<'tcx>)>, 'tcx: 'a
88 {
89     let mut first = true;
90     for (&id, mir) in iter {
91         if first {
92             first = false;
93         } else {
94             // Put empty lines between all items
95             writeln!(w, "")?;
96         }
97
98         let src = MirSource::from_node(tcx, id);
99         write_mir_fn(tcx, src, mir, w, None)?;
100
101         for (i, mir) in mir.promoted.iter_enumerated() {
102             writeln!(w, "")?;
103             write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w, None)?;
104         }
105     }
106     Ok(())
107 }
108
109 enum Annotation {
110     EnterScope(ScopeId),
111     ExitScope(ScopeId),
112 }
113
114 fn scope_entry_exit_annotations(auxiliary: Option<&ScopeAuxiliaryVec>)
115                                 -> FnvHashMap<Location, Vec<Annotation>>
116 {
117     // compute scope/entry exit annotations
118     let mut annotations = FnvHashMap();
119     if let Some(auxiliary) = auxiliary {
120         for (scope_id, auxiliary) in auxiliary.iter_enumerated() {
121             annotations.entry(auxiliary.dom)
122                        .or_insert(vec![])
123                        .push(Annotation::EnterScope(scope_id));
124
125             for &loc in &auxiliary.postdoms {
126                 annotations.entry(loc)
127                            .or_insert(vec![])
128                            .push(Annotation::ExitScope(scope_id));
129             }
130         }
131     }
132     return annotations;
133 }
134
135 pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
136                               src: MirSource,
137                               mir: &Mir<'tcx>,
138                               w: &mut Write,
139                               auxiliary: Option<&ScopeAuxiliaryVec>)
140                               -> io::Result<()> {
141     let annotations = scope_entry_exit_annotations(auxiliary);
142     write_mir_intro(tcx, src, mir, w)?;
143     for block in mir.basic_blocks().indices() {
144         write_basic_block(tcx, block, mir, w, &annotations)?;
145         if block.index() + 1 != mir.basic_blocks().len() {
146             writeln!(w, "")?;
147         }
148     }
149
150     writeln!(w, "}}")?;
151     Ok(())
152 }
153
154 /// Write out a human-readable textual representation for the given basic block.
155 fn write_basic_block(tcx: TyCtxt,
156                      block: BasicBlock,
157                      mir: &Mir,
158                      w: &mut Write,
159                      annotations: &FnvHashMap<Location, Vec<Annotation>>)
160                      -> io::Result<()> {
161     let data = &mir[block];
162
163     // Basic block label at the top.
164     writeln!(w, "{}{:?}: {{", INDENT, block)?;
165
166     // List of statements in the middle.
167     let mut current_location = Location { block: block, statement_index: 0 };
168     for statement in &data.statements {
169         if let Some(ref annotations) = annotations.get(&current_location) {
170             for annotation in annotations.iter() {
171                 match *annotation {
172                     Annotation::EnterScope(id) =>
173                         writeln!(w, "{0}{0}// Enter Scope({1})",
174                                  INDENT, id.index())?,
175                     Annotation::ExitScope(id) =>
176                         writeln!(w, "{0}{0}// Exit Scope({1})",
177                                  INDENT, id.index())?,
178                 }
179             }
180         }
181
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, "{}}}\n", 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 fn write_scope_tree(tcx: TyCtxt,
206                     mir: &Mir,
207                     scope_tree: &FnvHashMap<VisibilityScope, Vec<VisibilityScope>>,
208                     w: &mut Write,
209                     parent: VisibilityScope,
210                     depth: usize)
211                     -> io::Result<()> {
212     let indent = depth * INDENT.len();
213
214     let children = match scope_tree.get(&parent) {
215         Some(childs) => childs,
216         None => return Ok(()),
217     };
218
219     for &child in children {
220         let data = &mir.visibility_scopes[child];
221         assert_eq!(data.parent_scope, Some(parent));
222         writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
223
224         // User variable types (including the user's name in a comment).
225         for (id, var) in mir.var_decls.iter_enumerated() {
226             // Skip if not declared in this scope.
227             if var.source_info.scope != child {
228                 continue;
229             }
230
231             let mut_str = if var.mutability == Mutability::Mut {
232                 "mut "
233             } else {
234                 ""
235             };
236
237             let indent = indent + INDENT.len();
238             let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
239                                        INDENT,
240                                        indent,
241                                        mut_str,
242                                        id,
243                                        var.ty);
244             writeln!(w, "{0:1$} // \"{2}\" in {3}",
245                      indented_var,
246                      ALIGN,
247                      var.name,
248                      comment(tcx, var.source_info))?;
249         }
250
251         write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
252
253         writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
254     }
255
256     Ok(())
257 }
258
259 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
260 /// local variables (both user-defined bindings and compiler temporaries).
261 fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
262                              src: MirSource,
263                              mir: &Mir,
264                              w: &mut Write)
265                              -> io::Result<()> {
266     write_mir_sig(tcx, src, mir, w)?;
267     writeln!(w, " {{")?;
268
269     // construct a scope tree and write it out
270     let mut scope_tree: FnvHashMap<VisibilityScope, Vec<VisibilityScope>> = FnvHashMap();
271     for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
272         if let Some(parent) = scope_data.parent_scope {
273             scope_tree.entry(parent)
274                       .or_insert(vec![])
275                       .push(VisibilityScope::new(index));
276         } else {
277             // Only the argument scope has no parent, because it's the root.
278             assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
279         }
280     }
281
282     write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
283
284     write_mir_decls(mir, w)
285 }
286
287 fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
288                  -> io::Result<()>
289 {
290     match src {
291         MirSource::Fn(_) => write!(w, "fn")?,
292         MirSource::Const(_) => write!(w, "const")?,
293         MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
294         MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
295         MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
296     }
297
298     write!(w, " {}", tcx.node_path_str(src.item_id()))?;
299
300     if let MirSource::Fn(_) = src {
301         write!(w, "(")?;
302
303         // fn argument types.
304         for (i, arg) in mir.arg_decls.iter_enumerated() {
305             if i.index() != 0 {
306                 write!(w, ", ")?;
307             }
308             write!(w, "{:?}: {}", Lvalue::Arg(i), arg.ty)?;
309         }
310
311         write!(w, ") -> ")?;
312
313         // fn return type.
314         match mir.return_ty {
315             ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty),
316             ty::FnOutput::FnDiverging => write!(w, "!"),
317         }
318     } else {
319         assert!(mir.arg_decls.is_empty());
320         write!(w, ": {} =", mir.return_ty.unwrap())
321     }
322 }
323
324 fn write_mir_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
325     // Compiler-introduced temporary types.
326     for (id, temp) in mir.temp_decls.iter_enumerated() {
327         writeln!(w, "{}let mut {:?}: {};", INDENT, id, temp.ty)?;
328     }
329
330     // Wrote any declaration? Add an empty line before the first block is printed.
331     if !mir.var_decls.is_empty() || !mir.temp_decls.is_empty() {
332         writeln!(w, "")?;
333     }
334
335     Ok(())
336 }