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.
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.
12 use rustc
::hir
::def_id
::{DefId, LOCAL_CRATE}
;
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
;
20 use std
::io
::{self, Write}
;
21 use std
::path
::{PathBuf, Path}
;
22 use super::graphviz
::write_mir_fn_graphviz
;
23 use transform
::MirSource
;
25 const INDENT
: &'
static str = " ";
26 /// Alignment for lining up comments following MIR statements
27 const ALIGN
: usize = 40;
29 /// An indication of where we are in the control flow graph. Used for printing
30 /// extra information in `dump_mir`
32 /// We have not started dumping the control flow graph, but we are about to.
35 /// We just finished dumping the control flow graph. This is right before EOF
38 /// We are about to start dumping the given basic block.
39 BeforeBlock(BasicBlock
),
41 /// We are just about to dumpt the given statement or terminator.
45 /// If the session is properly configured, dumps a human-readable
46 /// representation of the mir into:
49 /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
52 /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
53 /// where `<filter>` takes the following forms:
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
>,
62 disambiguator
: &Display
,
67 F
: FnMut(PassWhere
, &mut Write
) -> io
::Result
<()>
69 if !dump_enabled(tcx
, pass_name
, source
) {
73 let node_path
= item_path
::with_forced_impl_filename_line(|| { // see notes on #41697 below
74 tcx
.item_path_str(source
.def_id
)
76 dump_matched_mir_node(tcx
, pass_num
, pass_name
, &node_path
,
77 disambiguator
, source
, mir
, extra_data
);
80 pub fn dump_enabled
<'a
, 'gcx
, 'tcx
>(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
84 let filters
= match tcx
.sess
.opts
.debugging_opts
.dump_mir
{
86 Some(ref filters
) => filters
,
88 let node_path
= item_path
::with_forced_impl_filename_line(|| { // see notes on #41697 below
89 tcx
.item_path_str(source
.def_id
)
94 pass_name
.contains(filter
) ||
95 node_path
.contains(filter
)
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`.
103 fn dump_matched_mir_node
<'a
, 'gcx
, 'tcx
, F
>(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
104 pass_num
: Option
<&Display
>,
107 disambiguator
: &Display
,
112 F
: FnMut(PassWhere
, &mut Write
) -> io
::Result
<()>
114 let promotion_id
= match source
.promoted
{
115 Some(id
) => format
!("-{:?}", id
),
116 None
=> String
::new()
119 let pass_num
= if tcx
.sess
.opts
.debugging_opts
.dump_mir_exclude_pass_number
{
123 None
=> format
!(".-------"),
124 Some(pass_num
) => format
!(".{}", pass_num
),
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
);
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
)?
;
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
)?
;
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
)?
;
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
>,
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.")?
;
172 let mut first
= true;
173 for def_id
in dump_mir_def_ids(tcx
, single
) {
174 let mir
= &tcx
.optimized_mir(def_id
);
179 // Put empty lines between all items
183 write_mir_fn(tcx
, MirSource
::item(def_id
), mir
, &mut |_
, _
| Ok(()), w
)?
;
185 for (i
, mir
) in mir
.promoted
.iter_enumerated() {
187 let src
= MirSource
{
191 write_mir_fn(tcx
, src
, mir
, &mut |_
, _
| Ok(()), w
)?
;
197 pub fn write_mir_fn
<'a
, 'gcx
, 'tcx
, F
>(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
204 F
: FnMut(PassWhere
, &mut Write
) -> io
::Result
<()>
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() {
219 /// Write out a human-readable textual representation for the given basic block.
220 pub fn write_basic_block
<F
>(tcx
: TyCtxt
,
227 F
: FnMut(PassWhere
, &mut Write
) -> io
::Result
<()>
229 let data
= &mir
[block
];
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
)?
;
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}",
244 comment(tcx
, statement
.source_info
))?
;
246 current_location
.statement_index
+= 1;
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}",
255 comment(tcx
, data
.terminator().source_info
))?
;
257 writeln
!(w
, "{}}}", INDENT
)
260 fn comment(tcx
: TyCtxt
, SourceInfo { span, scope }
: SourceInfo
) -> String
{
261 format
!("scope {} at {}", scope
.index(), tcx
.sess
.codemap().span_to_string(span
))
264 /// Prints user-defined variables in a scope tree.
266 /// Returns the total number of variables printed.
267 fn write_scope_tree(tcx
: TyCtxt
,
269 scope_tree
: &FxHashMap
<VisibilityScope
, Vec
<VisibilityScope
>>,
271 parent
: VisibilityScope
,
274 let indent
= depth
* INDENT
.len();
276 let children
= match scope_tree
.get(&parent
) {
277 Some(childs
) => childs
,
278 None
=> return Ok(()),
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())?
;
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
)
292 // Not a variable or not declared in this scope.
296 let mut_str
= if var
.mutability
== Mutability
::Mut
{
302 let indent
= indent
+ INDENT
.len();
303 let indented_var
= format
!("{0:1$}let {2}{3:?}: {4};",
309 writeln
!(w
, "{0:1$} // \"{2}\" in {3}",
313 comment(tcx
, source_info
))?
;
316 write_scope_tree(tcx
, mir
, scope_tree
, w
, child
, depth
+ 1)?
;
318 writeln
!(w
, "{0:1$}}}", "", depth
* INDENT
.len())?
;
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
>,
331 write_mir_sig(tcx
, src
, mir
, w
)?
;
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
)
340 .push(VisibilityScope
::new(index
));
342 // Only the argument scope has no parent, because it's the root.
343 assert_eq
!(index
, ARGUMENT_VISIBILITY_SCOPE
.index());
347 // Print return pointer
348 let indented_retptr
= format
!("{}let mut {:?}: {};",
351 mir
.local_decls
[RETURN_POINTER
].ty
);
352 writeln
!(w
, "{0:1$} // return pointer",
356 write_scope_tree(tcx
, mir
, &scope_tree
, w
, ARGUMENT_VISIBILITY_SCOPE
, 1)?
;
358 write_temp_decls(mir
, w
)?
;
360 // Add an empty line before the first block is printed.
366 fn write_mir_sig(tcx
: TyCtxt
, src
: MirSource
, mir
: &Mir
, w
: &mut Write
)
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")?
,
379 item_path
::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere
380 write
!(w
, " {}", tcx
.item_path_str(src
.def_id
))
383 match (body_owner_kind
, src
.promoted
) {
384 (hir
::BodyOwnerKind
::Fn
, None
) => {
387 // fn argument types.
388 for (i
, arg
) in mir
.args_iter().enumerate() {
392 write
!(w
, "{:?}: {}", Lvalue
::Local(arg
), mir
.local_decls
[arg
].ty
)?
;
395 write
!(w
, ") -> {}", mir
.return_ty())
397 (hir
::BodyOwnerKind
::Const
, _
) |
398 (hir
::BodyOwnerKind
::Static(_
), _
) |
400 assert_eq
!(mir
.arg_count
, 0);
401 write
!(w
, ": {} =", mir
.return_ty())
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
)?
;
415 pub fn dump_mir_def_ids(tcx
: TyCtxt
, single
: Option
<DefId
>) -> Vec
<DefId
> {
416 if let Some(i
) = single
{
419 tcx
.mir_keys(LOCAL_CRATE
).iter().cloned().collect()