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