]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/util/pretty.rs
New upstream version 1.22.1+dfsg1
[rustc.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 MirSource::GeneratorDrop(_) => format!("-drop"),
98 _ => String::new()
99 };
100
101 let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
102 format!("")
103 } else {
104 match pass_num {
105 None => format!(".-------"),
106 Some((suite, pass_num)) => format!(".{:03}-{:03}", suite.0, pass_num.0),
107 }
108 };
109
110 let mut file_path = PathBuf::new();
111 if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
112 let p = Path::new(file_dir);
113 file_path.push(p);
114 };
115 let _ = fs::create_dir_all(&file_path);
116 let file_name = format!("rustc.node{}{}{}.{}.{}.mir",
117 source.item_id(), promotion_id, pass_num, pass_name, disambiguator);
118 file_path.push(&file_name);
119 let _ = fs::File::create(&file_path).and_then(|mut file| {
120 writeln!(file, "// MIR for `{}`", node_path)?;
121 writeln!(file, "// source = {:?}", source)?;
122 writeln!(file, "// pass_name = {}", pass_name)?;
123 writeln!(file, "// disambiguator = {}", disambiguator)?;
124 if let Some(ref layout) = mir.generator_layout {
125 writeln!(file, "// generator_layout = {:?}", layout)?;
126 }
127 writeln!(file, "")?;
128 write_mir_fn(tcx, source, mir, &mut file)?;
129 Ok(())
130 });
131 }
132
133 /// Write out a human-readable textual representation for the given MIR.
134 pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
135 single: Option<DefId>,
136 w: &mut Write)
137 -> io::Result<()>
138 {
139 writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
140 writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
141
142 let mut first = true;
143 for def_id in dump_mir_def_ids(tcx, single) {
144 let mir = &tcx.optimized_mir(def_id);
145
146 if first {
147 first = false;
148 } else {
149 // Put empty lines between all items
150 writeln!(w, "")?;
151 }
152
153 let id = tcx.hir.as_local_node_id(def_id).unwrap();
154 let src = MirSource::from_node(tcx, id);
155 write_mir_fn(tcx, src, mir, w)?;
156
157 for (i, mir) in mir.promoted.iter_enumerated() {
158 writeln!(w, "")?;
159 write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
160 }
161 }
162 Ok(())
163 }
164
165 pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
166 src: MirSource,
167 mir: &Mir<'tcx>,
168 w: &mut Write)
169 -> io::Result<()> {
170 write_mir_intro(tcx, src, mir, w)?;
171 for block in mir.basic_blocks().indices() {
172 write_basic_block(tcx, block, mir, w)?;
173 if block.index() + 1 != mir.basic_blocks().len() {
174 writeln!(w, "")?;
175 }
176 }
177
178 writeln!(w, "}}")?;
179 Ok(())
180 }
181
182 /// Write out a human-readable textual representation for the given basic block.
183 pub fn write_basic_block(tcx: TyCtxt,
184 block: BasicBlock,
185 mir: &Mir,
186 w: &mut Write)
187 -> io::Result<()> {
188 let data = &mir[block];
189
190 // Basic block label at the top.
191 let cleanup_text = if data.is_cleanup { " // cleanup" } else { "" };
192 let lbl = format!("{}{:?}: {{", INDENT, block);
193 writeln!(w, "{0:1$}{2}", lbl, ALIGN, cleanup_text)?;
194
195 // List of statements in the middle.
196 let mut current_location = Location { block: block, statement_index: 0 };
197 for statement in &data.statements {
198 let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
199 writeln!(w, "{0:1$} // {2}",
200 indented_mir,
201 ALIGN,
202 comment(tcx, statement.source_info))?;
203
204 current_location.statement_index += 1;
205 }
206
207 // Terminator at the bottom.
208 let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
209 writeln!(w, "{0:1$} // {2}",
210 indented_terminator,
211 ALIGN,
212 comment(tcx, data.terminator().source_info))?;
213
214 writeln!(w, "{}}}", INDENT)
215 }
216
217 fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
218 format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
219 }
220
221 /// Prints user-defined variables in a scope tree.
222 ///
223 /// Returns the total number of variables printed.
224 fn write_scope_tree(tcx: TyCtxt,
225 mir: &Mir,
226 scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
227 w: &mut Write,
228 parent: VisibilityScope,
229 depth: usize)
230 -> io::Result<()> {
231 let indent = depth * INDENT.len();
232
233 let children = match scope_tree.get(&parent) {
234 Some(childs) => childs,
235 None => return Ok(()),
236 };
237
238 for &child in children {
239 let data = &mir.visibility_scopes[child];
240 assert_eq!(data.parent_scope, Some(parent));
241 writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
242
243 // User variable types (including the user's name in a comment).
244 for local in mir.vars_iter() {
245 let var = &mir.local_decls[local];
246 let (name, source_info) = if var.source_info.scope == child {
247 (var.name.unwrap(), var.source_info)
248 } else {
249 // Not a variable or not declared in this scope.
250 continue;
251 };
252
253 let mut_str = if var.mutability == Mutability::Mut {
254 "mut "
255 } else {
256 ""
257 };
258
259 let indent = indent + INDENT.len();
260 let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
261 INDENT,
262 indent,
263 mut_str,
264 local,
265 var.ty);
266 writeln!(w, "{0:1$} // \"{2}\" in {3}",
267 indented_var,
268 ALIGN,
269 name,
270 comment(tcx, source_info))?;
271 }
272
273 write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
274
275 writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
276 }
277
278 Ok(())
279 }
280
281 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
282 /// local variables (both user-defined bindings and compiler temporaries).
283 pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
284 src: MirSource,
285 mir: &Mir,
286 w: &mut Write)
287 -> io::Result<()> {
288 write_mir_sig(tcx, src, mir, w)?;
289 writeln!(w, " {{")?;
290
291 // construct a scope tree and write it out
292 let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
293 for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
294 if let Some(parent) = scope_data.parent_scope {
295 scope_tree.entry(parent)
296 .or_insert(vec![])
297 .push(VisibilityScope::new(index));
298 } else {
299 // Only the argument scope has no parent, because it's the root.
300 assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
301 }
302 }
303
304 // Print return pointer
305 let indented_retptr = format!("{}let mut {:?}: {};",
306 INDENT,
307 RETURN_POINTER,
308 mir.return_ty);
309 writeln!(w, "{0:1$} // return pointer",
310 indented_retptr,
311 ALIGN)?;
312
313 write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
314
315 write_temp_decls(mir, w)?;
316
317 // Add an empty line before the first block is printed.
318 writeln!(w, "")?;
319
320 Ok(())
321 }
322
323 fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
324 -> io::Result<()>
325 {
326 match src {
327 MirSource::Fn(_) => write!(w, "fn")?,
328 MirSource::Const(_) => write!(w, "const")?,
329 MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
330 MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
331 MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?,
332 MirSource::GeneratorDrop(_) => write!(w, "drop_glue")?,
333 }
334
335 item_path::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere
336 write!(w, " {}", tcx.node_path_str(src.item_id()))
337 })?;
338
339 match src {
340 MirSource::Fn(_) | MirSource::GeneratorDrop(_) => {
341 write!(w, "(")?;
342
343 // fn argument types.
344 for (i, arg) in mir.args_iter().enumerate() {
345 if i != 0 {
346 write!(w, ", ")?;
347 }
348 write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
349 }
350
351 write!(w, ") -> {}", mir.return_ty)
352 }
353 MirSource::Const(..) |
354 MirSource::Static(..) |
355 MirSource::Promoted(..) => {
356 assert_eq!(mir.arg_count, 0);
357 write!(w, ": {} =", mir.return_ty)
358 }
359 }
360 }
361
362 fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
363 // Compiler-introduced temporary types.
364 for temp in mir.temps_iter() {
365 writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
366 }
367
368 Ok(())
369 }
370
371 pub fn dump_mir_def_ids(tcx: TyCtxt, single: Option<DefId>) -> Vec<DefId> {
372 if let Some(i) = single {
373 vec![i]
374 } else {
375 tcx.mir_keys(LOCAL_CRATE).iter().cloned().collect()
376 }
377 }