]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/util/pretty.rs
New upstream version 1.46.0+dfsg1
[rustc.git] / src / librustc_mir / util / pretty.rs
CommitLineData
f9f354fc
XL
1use std::collections::BTreeSet;
2use std::fmt::Write as _;
3use std::fmt::{Debug, Display};
4use std::fs;
5use std::io::{self, Write};
6use std::path::{Path, PathBuf};
7
dfeec247
XL
8use super::graphviz::write_mir_fn_graphviz;
9use crate::transform::MirSource;
ba9703b0 10use either::Either;
476ff2be 11use rustc_data_structures::fx::FxHashMap;
dfeec247 12use rustc_hir::def_id::{DefId, LOCAL_CRATE};
e74abb32 13use rustc_index::vec::Idx;
ba9703b0 14use rustc_middle::mir::interpret::{
f9f354fc 15 read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer,
ba9703b0
XL
16};
17use rustc_middle::mir::visit::Visitor;
18use rustc_middle::mir::*;
19use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitor};
20use rustc_target::abi::Size;
9cc50fc6 21
0731742a 22const INDENT: &str = " ";
a7813a04 23/// Alignment for lining up comments following MIR statements
ff7c6d11 24pub(crate) const ALIGN: usize = 40;
9cc50fc6 25
abe05a73
XL
26/// An indication of where we are in the control flow graph. Used for printing
27/// extra information in `dump_mir`
28pub enum PassWhere {
29 /// We have not started dumping the control flow graph, but we are about to.
30 BeforeCFG,
31
32 /// We just finished dumping the control flow graph. This is right before EOF
33 AfterCFG,
34
35 /// We are about to start dumping the given basic block.
36 BeforeBlock(BasicBlock),
37
ff7c6d11
XL
38 /// We are just about to dump the given statement or terminator.
39 BeforeLocation(Location),
40
41 /// We just dumped the given statement or terminator.
42 AfterLocation(Location),
8faf50e0
XL
43
44 /// We just dumped the terminator for a block but not the closing `}`.
45 AfterTerminator(BasicBlock),
abe05a73
XL
46}
47
54a0048b
SL
48/// If the session is properly configured, dumps a human-readable
49/// representation of the mir into:
50///
a7813a04 51/// ```text
7cac9316 52/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
54a0048b
SL
53/// ```
54///
55/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
56/// where `<filter>` takes the following forms:
57///
58/// - `all` -- dump MIR for all fns, all passes, all everything
ff7c6d11
XL
59/// - a filter defined by a set of substrings combined with `&` and `|`
60/// (`&` has higher precedence). At least one of the `|`-separated groups
61/// must match; an `|`-separated group matches if all of its `&`-separated
62/// substrings are matched.
63///
64/// Example:
65///
66/// - `nll` == match if `nll` appears in the name
67/// - `foo & nll` == match if `foo` and `nll` both appear in the name
68/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
69/// or `typeck` appears in the name.
70/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
71/// or `typeck` and `bar` both appear in the name.
dc9dc135
XL
72pub fn dump_mir<'tcx, F>(
73 tcx: TyCtxt<'tcx>,
0531ce1d 74 pass_num: Option<&dyn Display>,
ff7c6d11 75 pass_name: &str,
0531ce1d 76 disambiguator: &dyn Display,
9fa01778 77 source: MirSource<'tcx>,
dc9dc135 78 body: &Body<'tcx>,
ff7c6d11
XL
79 extra_data: F,
80) where
0531ce1d 81 F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
abe05a73 82{
ba9703b0 83 if !dump_enabled(tcx, pass_name, source.def_id()) {
7cac9316
XL
84 return;
85 }
86
ba9703b0 87 dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, source, body, extra_data);
7cac9316
XL
88}
89
ba9703b0 90pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool {
54a0048b 91 let filters = match tcx.sess.opts.debugging_opts.dump_mir {
7cac9316 92 None => return false,
54a0048b
SL
93 Some(ref filters) => filters,
94 };
532ac7d7 95 let node_path = ty::print::with_forced_impl_filename_line(|| {
ff7c6d11 96 // see notes on #41697 below
ba9703b0 97 tcx.def_path_str(def_id)
7cac9316 98 });
8faf50e0
XL
99 filters.split('|').any(|or_filter| {
100 or_filter.split('&').all(|and_filter| {
ff7c6d11
XL
101 and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter)
102 })
103 })
7cac9316 104}
54a0048b 105
7cac9316 106// #41697 -- we use `with_forced_impl_filename_line()` because
532ac7d7 107// `def_path_str()` would otherwise trigger `type_of`, and this can
7cac9316
XL
108// run while we are already attempting to evaluate `type_of`.
109
dc9dc135
XL
110fn dump_matched_mir_node<'tcx, F>(
111 tcx: TyCtxt<'tcx>,
0531ce1d 112 pass_num: Option<&dyn Display>,
ff7c6d11 113 pass_name: &str,
0531ce1d 114 disambiguator: &dyn Display,
9fa01778 115 source: MirSource<'tcx>,
dc9dc135 116 body: &Body<'tcx>,
ff7c6d11
XL
117 mut extra_data: F,
118) where
0531ce1d 119 F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
abe05a73 120{
9fa01778 121 let _: io::Result<()> = try {
ff7c6d11 122 let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?;
ba9703b0
XL
123 let def_path = ty::print::with_forced_impl_filename_line(|| {
124 // see notes on #41697 above
125 tcx.def_path_str(source.def_id())
126 });
127 write!(file, "// MIR for `{}", def_path)?;
128 match source.promoted {
129 None => write!(file, "`")?,
130 Some(promoted) => write!(file, "::{:?}`", promoted)?,
131 }
132 writeln!(file, " {} {}", disambiguator, pass_name)?;
dc9dc135 133 if let Some(ref layout) = body.generator_layout {
f035d41b 134 writeln!(file, "/* generator_layout = {:#?} */", layout)?;
ff7c6d11 135 }
ba9703b0 136 writeln!(file)?;
ff7c6d11 137 extra_data(PassWhere::BeforeCFG, &mut file)?;
f035d41b 138 write_user_type_annotations(tcx, body, &mut file)?;
dc9dc135 139 write_mir_fn(tcx, source, body, &mut extra_data, &mut file)?;
ff7c6d11 140 extra_data(PassWhere::AfterCFG, &mut file)?;
94b46f34 141 };
ff7c6d11
XL
142
143 if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
9fa01778 144 let _: io::Result<()> = try {
ff7c6d11
XL
145 let mut file =
146 create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?;
e74abb32 147 write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?;
94b46f34 148 };
ff7c6d11
XL
149 }
150}
151
152/// Returns the path to the filename where we should dump a given MIR.
153/// Also used by other bits of code (e.g., NLL inference) that dump
154/// graphviz data or other things.
155fn dump_path(
dc9dc135 156 tcx: TyCtxt<'_>,
ff7c6d11 157 extension: &str,
0531ce1d 158 pass_num: Option<&dyn Display>,
ff7c6d11 159 pass_name: &str,
0531ce1d 160 disambiguator: &dyn Display,
9fa01778 161 source: MirSource<'tcx>,
ff7c6d11 162) -> PathBuf {
abe05a73
XL
163 let promotion_id = match source.promoted {
164 Some(id) => format!("-{:?}", id),
ff7c6d11 165 None => String::new(),
3157f602
XL
166 };
167
7cac9316 168 let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
8faf50e0 169 String::new()
7cac9316
XL
170 } else {
171 match pass_num {
8faf50e0 172 None => ".-------".to_string(),
abe05a73 173 Some(pass_num) => format!(".{}", pass_num),
7cac9316
XL
174 }
175 };
176
5bcae85e 177 let mut file_path = PathBuf::new();
2c00a5a8 178 file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir));
abe05a73 179
dfeec247 180 let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
9fa01778
XL
181 // All drop shims have the same DefId, so we have to add the type
182 // to get unique file names.
183 let shim_disambiguator = match source.instance {
184 ty::InstanceDef::DropGlue(_, Some(ty)) => {
185 // Unfortunately, pretty-printed typed are not very filename-friendly.
186 // We dome some filtering.
187 let mut s = ".".to_owned();
dfeec247
XL
188 s.extend(ty.to_string().chars().filter_map(|c| match c {
189 ' ' => None,
190 ':' | '<' | '>' => Some('_'),
191 c => Some(c),
192 }));
9fa01778
XL
193 s
194 }
195 _ => String::new(),
196 };
ff7c6d11
XL
197
198 let file_name = format!(
9fa01778 199 "rustc.{}{}{}{}.{}.{}.{}",
dfeec247 200 item_name, shim_disambiguator, promotion_id, pass_num, pass_name, disambiguator, extension,
ff7c6d11
XL
201 );
202
5bcae85e 203 file_path.push(&file_name);
abe05a73 204
ff7c6d11
XL
205 file_path
206}
207
208/// Attempts to open a file where we should dump a given MIR or other
209/// bit of MIR-related data. Used by `mir-dump`, but also by other
210/// bits of code (e.g., NLL inference) that dump graphviz data or
211/// other things, and hence takes the extension as an argument.
212pub(crate) fn create_dump_file(
dc9dc135 213 tcx: TyCtxt<'_>,
ff7c6d11 214 extension: &str,
0531ce1d 215 pass_num: Option<&dyn Display>,
ff7c6d11 216 pass_name: &str,
0531ce1d 217 disambiguator: &dyn Display,
9fa01778 218 source: MirSource<'tcx>,
e1599b0c 219) -> io::Result<io::BufWriter<fs::File>> {
ff7c6d11
XL
220 let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source);
221 if let Some(parent) = file_path.parent() {
222 fs::create_dir_all(parent)?;
abe05a73 223 }
e1599b0c 224 Ok(io::BufWriter::new(fs::File::create(&file_path)?))
54a0048b
SL
225}
226
9cc50fc6 227/// Write out a human-readable textual representation for the given MIR.
dc9dc135
XL
228pub fn write_mir_pretty<'tcx>(
229 tcx: TyCtxt<'tcx>,
ff7c6d11 230 single: Option<DefId>,
0531ce1d 231 w: &mut dyn Write,
ff7c6d11 232) -> io::Result<()> {
dfeec247
XL
233 writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
234 writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
cc61c64b 235
a7813a04 236 let mut first = true;
7cac9316 237 for def_id in dump_mir_def_ids(tcx, single) {
dc9dc135 238 let body = &tcx.optimized_mir(def_id);
5bcae85e 239
a7813a04
XL
240 if first {
241 first = false;
242 } else {
243 // Put empty lines between all items
ba9703b0 244 writeln!(w)?;
a7813a04
XL
245 }
246
dc9dc135 247 write_mir_fn(tcx, MirSource::item(def_id), body, &mut |_, _| Ok(()), w)?;
a7813a04 248
416331ca 249 for (i, body) in tcx.promoted_mir(def_id).iter_enumerated() {
ba9703b0 250 writeln!(w)?;
dfeec247 251 let src = MirSource { instance: ty::InstanceDef::Item(def_id), promoted: Some(i) };
dc9dc135 252 write_mir_fn(tcx, src, body, &mut |_, _| Ok(()), w)?;
a7813a04 253 }
54a0048b
SL
254 }
255 Ok(())
256}
9cc50fc6 257
ba9703b0 258/// Write out a human-readable textual representation for the given function.
dc9dc135
XL
259pub fn write_mir_fn<'tcx, F>(
260 tcx: TyCtxt<'tcx>,
9fa01778 261 src: MirSource<'tcx>,
dc9dc135 262 body: &Body<'tcx>,
ff7c6d11 263 extra_data: &mut F,
0531ce1d 264 w: &mut dyn Write,
ff7c6d11 265) -> io::Result<()>
abe05a73 266where
0531ce1d 267 F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
abe05a73 268{
dc9dc135
XL
269 write_mir_intro(tcx, src, body, w)?;
270 for block in body.basic_blocks().indices() {
abe05a73 271 extra_data(PassWhere::BeforeBlock(block), w)?;
dc9dc135
XL
272 write_basic_block(tcx, block, body, extra_data, w)?;
273 if block.index() + 1 != body.basic_blocks().len() {
ba9703b0 274 writeln!(w)?;
3157f602 275 }
54a0048b
SL
276 }
277
54a0048b 278 writeln!(w, "}}")?;
ba9703b0
XL
279
280 write_allocations(tcx, body, w)?;
281
54a0048b 282 Ok(())
9cc50fc6
SL
283}
284
285/// Write out a human-readable textual representation for the given basic block.
dc9dc135
XL
286pub fn write_basic_block<'tcx, F>(
287 tcx: TyCtxt<'tcx>,
ff7c6d11 288 block: BasicBlock,
dc9dc135 289 body: &Body<'tcx>,
ff7c6d11 290 extra_data: &mut F,
0531ce1d 291 w: &mut dyn Write,
ff7c6d11 292) -> io::Result<()>
abe05a73 293where
0531ce1d 294 F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
abe05a73 295{
dc9dc135 296 let data = &body[block];
9cc50fc6
SL
297
298 // Basic block label at the top.
532ac7d7
XL
299 let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
300 writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?;
9cc50fc6
SL
301
302 // List of statements in the middle.
74b04a01 303 let mut current_location = Location { block, statement_index: 0 };
9cc50fc6 304 for statement in &data.statements {
ff7c6d11 305 extra_data(PassWhere::BeforeLocation(current_location), w)?;
dc9dc135 306 let indented_body = format!("{0}{0}{1:?};", INDENT, statement);
ff7c6d11
XL
307 writeln!(
308 w,
f9f354fc 309 "{:A$} // {}{}",
dc9dc135 310 indented_body,
f9f354fc 311 if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
ff7c6d11
XL
312 comment(tcx, statement.source_info),
313 A = ALIGN,
314 )?;
315
316 write_extra(tcx, w, |visitor| {
48663c56 317 visitor.visit_statement(statement, current_location);
ff7c6d11
XL
318 })?;
319
320 extra_data(PassWhere::AfterLocation(current_location), w)?;
54a0048b
SL
321
322 current_location.statement_index += 1;
9cc50fc6
SL
323 }
324
325 // Terminator at the bottom.
ff7c6d11 326 extra_data(PassWhere::BeforeLocation(current_location), w)?;
a7813a04 327 let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
ff7c6d11
XL
328 writeln!(
329 w,
f9f354fc 330 "{:A$} // {}{}",
ff7c6d11 331 indented_terminator,
f9f354fc 332 if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
ff7c6d11
XL
333 comment(tcx, data.terminator().source_info),
334 A = ALIGN,
335 )?;
336
337 write_extra(tcx, w, |visitor| {
48663c56 338 visitor.visit_terminator(data.terminator(), current_location);
ff7c6d11
XL
339 })?;
340
341 extra_data(PassWhere::AfterLocation(current_location), w)?;
8faf50e0 342 extra_data(PassWhere::AfterTerminator(block), w)?;
9cc50fc6 343
5bcae85e 344 writeln!(w, "{}}}", INDENT)
9cc50fc6
SL
345}
346
ff7c6d11
XL
347/// After we print the main statement, we sometimes dump extra
348/// information. There's often a lot of little things "nuzzled up" in
349/// a statement.
dc9dc135 350fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
ff7c6d11 351where
dc9dc135 352 F: FnMut(&mut ExtraComments<'tcx>),
ff7c6d11 353{
f035d41b 354 let mut extra_comments = ExtraComments { tcx, comments: vec![] };
ff7c6d11
XL
355 visit_op(&mut extra_comments);
356 for comment in extra_comments.comments {
357 writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
358 }
359 Ok(())
360}
361
dc9dc135 362struct ExtraComments<'tcx> {
f035d41b 363 tcx: TyCtxt<'tcx>,
ff7c6d11
XL
364 comments: Vec<String>,
365}
366
dc9dc135 367impl ExtraComments<'tcx> {
ff7c6d11 368 fn push(&mut self, lines: &str) {
8faf50e0 369 for line in lines.split('\n') {
ff7c6d11
XL
370 self.comments.push(line.to_string());
371 }
372 }
373}
374
dc9dc135 375impl Visitor<'tcx> for ExtraComments<'tcx> {
ff7c6d11
XL
376 fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
377 self.super_constant(constant, location);
e1599b0c 378 let Constant { span, user_ty, literal } = constant;
8faf50e0 379 self.push("mir::Constant");
f035d41b 380 self.push(&format!("+ span: {}", self.tcx.sess.source_map().span_to_string(*span)));
b7449926
XL
381 if let Some(user_ty) = user_ty {
382 self.push(&format!("+ user_ty: {:?}", user_ty));
383 }
2c00a5a8 384 self.push(&format!("+ literal: {:?}", literal));
ff7c6d11
XL
385 }
386
532ac7d7 387 fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
ff7c6d11 388 self.super_const(constant);
532ac7d7
XL
389 let ty::Const { ty, val, .. } = constant;
390 self.push("ty::Const");
391 self.push(&format!("+ ty: {:?}", ty));
392 self.push(&format!("+ val: {:?}", val));
ff7c6d11
XL
393 }
394
395 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
396 self.super_rvalue(rvalue, location);
ba9703b0
XL
397 if let Rvalue::Aggregate(kind, _) = rvalue {
398 match **kind {
ff7c6d11 399 AggregateKind::Closure(def_id, substs) => {
8faf50e0 400 self.push("closure");
2c00a5a8
XL
401 self.push(&format!("+ def_id: {:?}", def_id));
402 self.push(&format!("+ substs: {:#?}", substs));
ff7c6d11
XL
403 }
404
94b46f34 405 AggregateKind::Generator(def_id, substs, movability) => {
8faf50e0 406 self.push("generator");
2c00a5a8
XL
407 self.push(&format!("+ def_id: {:?}", def_id));
408 self.push(&format!("+ substs: {:#?}", substs));
94b46f34 409 self.push(&format!("+ movability: {:?}", movability));
ff7c6d11
XL
410 }
411
b7449926
XL
412 AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
413 self.push("adt");
414 self.push(&format!("+ user_ty: {:?}", user_ty));
415 }
416
ff7c6d11 417 _ => {}
ba9703b0 418 }
ff7c6d11
XL
419 }
420 }
421}
422
dc9dc135 423fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
dfeec247 424 format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_string(span))
54a0048b
SL
425}
426
48663c56 427/// Prints local variables in a scope tree.
ff7c6d11 428fn write_scope_tree(
dc9dc135
XL
429 tcx: TyCtxt<'_>,
430 body: &Body<'_>,
94b46f34 431 scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
0531ce1d 432 w: &mut dyn Write,
94b46f34 433 parent: SourceScope,
ff7c6d11
XL
434 depth: usize,
435) -> io::Result<()> {
3157f602 436 let indent = depth * INDENT.len();
a7813a04 437
60c5eb7d
XL
438 // Local variable debuginfo.
439 for var_debug_info in &body.var_debug_info {
440 if var_debug_info.source_info.scope != parent {
441 // Not declared in this scope.
442 continue;
443 }
444
445 let indented_debug_info = format!(
446 "{0:1$}debug {2} => {3:?};",
dfeec247 447 INDENT, indent, var_debug_info.name, var_debug_info.place,
60c5eb7d
XL
448 );
449
450 writeln!(
451 w,
452 "{0:1$} // in {2}",
453 indented_debug_info,
454 ALIGN,
455 comment(tcx, var_debug_info.source_info),
456 )?;
457 }
458
f9f354fc 459 // Local variable types.
dc9dc135 460 for (local, local_decl) in body.local_decls.iter_enumerated() {
dfeec247 461 if (1..body.arg_count + 1).contains(&local.index()) {
48663c56
XL
462 // Skip over argument locals, they're printed in the signature.
463 continue;
464 }
465
466 if local_decl.source_info.scope != parent {
467 // Not declared in this scope.
468 continue;
469 }
470
dfeec247 471 let mut_str = if local_decl.mutability == Mutability::Mut { "mut " } else { "" };
48663c56 472
dfeec247
XL
473 let mut indented_decl =
474 format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty);
f9f354fc
XL
475 if let Some(user_ty) = &local_decl.user_ty {
476 for user_ty in user_ty.projections() {
477 write!(indented_decl, " as {:?}", user_ty).unwrap();
478 }
48663c56
XL
479 }
480 indented_decl.push_str(";");
481
dfeec247 482 let local_name =
74b04a01 483 if local == RETURN_PLACE { " return place".to_string() } else { String::new() };
48663c56
XL
484
485 writeln!(
486 w,
487 "{0:1$} //{2} in {3}",
488 indented_decl,
489 ALIGN,
490 local_name,
491 comment(tcx, local_decl.source_info),
492 )?;
493 }
494
a7813a04 495 let children = match scope_tree.get(&parent) {
74b04a01 496 Some(children) => children,
a7813a04
XL
497 None => return Ok(()),
498 };
499
3157f602 500 for &child in children {
dc9dc135 501 assert_eq!(body.source_scopes[child].parent_scope, Some(parent));
3157f602 502 writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
dc9dc135 503 write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
3157f602 504 writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
54a0048b 505 }
a7813a04 506
54a0048b
SL
507 Ok(())
508}
509
9cc50fc6
SL
510/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
511/// local variables (both user-defined bindings and compiler temporaries).
dc9dc135
XL
512pub fn write_mir_intro<'tcx>(
513 tcx: TyCtxt<'tcx>,
9fa01778 514 src: MirSource<'tcx>,
dc9dc135 515 body: &Body<'_>,
0531ce1d 516 w: &mut dyn Write,
ff7c6d11 517) -> io::Result<()> {
dc9dc135 518 write_mir_sig(tcx, src, body, w)?;
2c00a5a8 519 writeln!(w, "{{")?;
3157f602
XL
520
521 // construct a scope tree and write it out
0bf4aa26 522 let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
dc9dc135 523 for (index, scope_data) in body.source_scopes.iter().enumerate() {
3157f602 524 if let Some(parent) = scope_data.parent_scope {
dfeec247 525 scope_tree.entry(parent).or_default().push(SourceScope::new(index));
3157f602
XL
526 } else {
527 // Only the argument scope has no parent, because it's the root.
94b46f34 528 assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
3157f602
XL
529 }
530 }
531
dc9dc135 532 write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
3157f602 533
c30ab7b3 534 // Add an empty line before the first block is printed.
ba9703b0
XL
535 writeln!(w)?;
536
537 Ok(())
538}
539
540/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
541/// allocations.
542pub fn write_allocations<'tcx>(
543 tcx: TyCtxt<'tcx>,
544 body: &Body<'_>,
545 w: &mut dyn Write,
546) -> io::Result<()> {
547 fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
548 alloc.relocations().values().map(|(_, id)| *id)
549 }
550 fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
551 match val {
552 ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => {
553 Either::Left(Either::Left(std::iter::once(ptr.alloc_id)))
554 }
555 ConstValue::Scalar(interpret::Scalar::Raw { .. }) => {
556 Either::Left(Either::Right(std::iter::empty()))
557 }
558 ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
559 Either::Right(alloc_ids_from_alloc(alloc))
560 }
561 }
562 }
563 struct CollectAllocIds(BTreeSet<AllocId>);
564 impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds {
565 fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
566 if let ty::ConstKind::Value(val) = c.val {
567 self.0.extend(alloc_ids_from_const(val));
568 }
569 c.super_visit_with(self)
570 }
571 }
572 let mut visitor = CollectAllocIds(Default::default());
573 body.visit_with(&mut visitor);
574 // `seen` contains all seen allocations, including the ones we have *not* printed yet.
575 // The protocol is to first `insert` into `seen`, and only if that returns `true`
576 // then push to `todo`.
577 let mut seen = visitor.0;
578 let mut todo: Vec<_> = seen.iter().copied().collect();
579 while let Some(id) = todo.pop() {
580 let mut write_allocation_track_relocs =
581 |w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> {
582 // `.rev()` because we are popping them from the back of the `todo` vector.
583 for id in alloc_ids_from_alloc(alloc).rev() {
584 if seen.insert(id) {
585 todo.push(id);
586 }
587 }
588 write_allocation(tcx, alloc, w)
589 };
590 write!(w, "\n{}", id)?;
f9f354fc 591 match tcx.get_global_alloc(id) {
ba9703b0
XL
592 // This can't really happen unless there are bugs, but it doesn't cost us anything to
593 // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
594 None => write!(w, " (deallocated)")?,
595 Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?,
596 Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
597 match tcx.const_eval_poly(did) {
598 Ok(ConstValue::ByRef { alloc, .. }) => {
599 write!(w, " (static: {}, ", tcx.def_path_str(did))?;
600 write_allocation_track_relocs(w, alloc)?;
601 }
602 Ok(_) => {
603 span_bug!(tcx.def_span(did), " static item without `ByRef` initializer")
604 }
605 Err(_) => write!(
606 w,
607 " (static: {}, error during initializer evaluation)",
608 tcx.def_path_str(did)
609 )?,
610 }
611 }
612 Some(GlobalAlloc::Static(did)) => {
613 write!(w, " (extern static: {})", tcx.def_path_str(did))?
614 }
615 Some(GlobalAlloc::Memory(alloc)) => {
616 write!(w, " (")?;
617 write_allocation_track_relocs(w, alloc)?
618 }
619 }
620 writeln!(w)?;
621 }
622 Ok(())
623}
624
625/// Dumps the size and metadata and content of an allocation to the given writer.
626/// The expectation is that the caller first prints other relevant metadata, so the exact
627/// format of this function is (*without* leading or trailing newline):
628/// ```
629/// size: {}, align: {}) {
630/// <bytes>
631/// }
632/// ```
633///
634/// The byte format is similar to how hex editors print bytes. Each line starts with the address of
635/// the start of the line, followed by all bytes in hex format (space separated).
636/// If the allocation is small enough to fit into a single line, no start address is given.
637/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
638/// characters or characters whose value is larger than 127) with a `.`
639/// This also prints relocations adequately.
f9f354fc 640pub fn write_allocation<Tag: Copy + Debug, Extra>(
ba9703b0
XL
641 tcx: TyCtxt<'tcx>,
642 alloc: &Allocation<Tag, Extra>,
643 w: &mut dyn Write,
644) -> io::Result<()> {
645 write!(w, "size: {}, align: {})", alloc.size.bytes(), alloc.align.bytes())?;
646 if alloc.size == Size::ZERO {
647 // We are done.
648 return write!(w, " {{}}");
649 }
650 // Write allocation bytes.
651 writeln!(w, " {{")?;
652 write_allocation_bytes(tcx, alloc, w, " ")?;
653 write!(w, "}}")?;
654 Ok(())
655}
656
657fn write_allocation_endline(w: &mut dyn Write, ascii: &str) -> io::Result<()> {
658 for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {
659 write!(w, " ")?;
660 }
661 writeln!(w, " │ {}", ascii)
662}
663
664/// Number of bytes to print per allocation hex dump line.
665const BYTES_PER_LINE: usize = 16;
666
667/// Prints the line start address and returns the new line start address.
668fn write_allocation_newline(
669 w: &mut dyn Write,
670 mut line_start: Size,
671 ascii: &str,
672 pos_width: usize,
673 prefix: &str,
674) -> io::Result<Size> {
675 write_allocation_endline(w, ascii)?;
676 line_start += Size::from_bytes(BYTES_PER_LINE);
677 write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;
678 Ok(line_start)
679}
680
681/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
682/// is only one line). Note that your prefix should contain a trailing space as the lines are
683/// printed directly after it.
f9f354fc 684fn write_allocation_bytes<Tag: Copy + Debug, Extra>(
ba9703b0
XL
685 tcx: TyCtxt<'tcx>,
686 alloc: &Allocation<Tag, Extra>,
687 w: &mut dyn Write,
688 prefix: &str,
689) -> io::Result<()> {
690 let num_lines = alloc.size.bytes_usize().saturating_sub(BYTES_PER_LINE);
691 // Number of chars needed to represent all line numbers.
692 let pos_width = format!("{:x}", alloc.size.bytes()).len();
693
694 if num_lines > 0 {
695 write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;
696 } else {
697 write!(w, "{}", prefix)?;
698 }
699
700 let mut i = Size::ZERO;
701 let mut line_start = Size::ZERO;
702
703 let ptr_size = tcx.data_layout.pointer_size;
704
705 let mut ascii = String::new();
706
707 let oversized_ptr = |target: &mut String, width| {
708 if target.len() > width {
709 write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
710 }
711 };
712
713 while i < alloc.size {
714 // The line start already has a space. While we could remove that space from the line start
715 // printing and unconditionally print a space here, that would cause the single-line case
716 // to have a single space before it, which looks weird.
717 if i != line_start {
718 write!(w, " ")?;
719 }
f9f354fc 720 if let Some(&(tag, target_id)) = alloc.relocations().get(&i) {
ba9703b0
XL
721 // Memory with a relocation must be defined
722 let j = i.bytes_usize();
723 let offset =
724 alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
725 let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
f9f354fc 726 let offset = Size::from_bytes(offset);
ba9703b0 727 let relocation_width = |bytes| bytes * 3;
f9f354fc
XL
728 let ptr = Pointer::new_with_tag(target_id, offset, tag);
729 let mut target = format!("{:?}", ptr);
730 if target.len() > relocation_width(ptr_size.bytes_usize() - 1) {
731 // This is too long, try to save some space.
732 target = format!("{:#?}", ptr);
733 }
ba9703b0
XL
734 if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
735 // This branch handles the situation where a relocation starts in the current line
736 // but ends in the next one.
737 let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
738 let overflow = ptr_size - remainder;
739 let remainder_width = relocation_width(remainder.bytes_usize()) - 2;
740 let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1;
741 ascii.push('╾');
742 for _ in 0..remainder.bytes() - 1 {
743 ascii.push('─');
744 }
745 if overflow_width > remainder_width && overflow_width >= target.len() {
746 // The case where the relocation fits into the part in the next line
747 write!(w, "╾{0:─^1$}", "", remainder_width)?;
748 line_start =
749 write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
750 ascii.clear();
751 write!(w, "{0:─^1$}╼", target, overflow_width)?;
752 } else {
753 oversized_ptr(&mut target, remainder_width);
754 write!(w, "╾{0:─^1$}", target, remainder_width)?;
755 line_start =
756 write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
757 write!(w, "{0:─^1$}╼", "", overflow_width)?;
758 ascii.clear();
759 }
760 for _ in 0..overflow.bytes() - 1 {
761 ascii.push('─');
762 }
763 ascii.push('╼');
764 i += ptr_size;
765 continue;
766 } else {
767 // This branch handles a relocation that starts and ends in the current line.
768 let relocation_width = relocation_width(ptr_size.bytes_usize() - 1);
769 oversized_ptr(&mut target, relocation_width);
770 ascii.push('╾');
771 write!(w, "╾{0:─^1$}╼", target, relocation_width)?;
772 for _ in 0..ptr_size.bytes() - 2 {
773 ascii.push('─');
774 }
775 ascii.push('╼');
776 i += ptr_size;
777 }
f9f354fc 778 } else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() {
ba9703b0
XL
779 let j = i.bytes_usize();
780
781 // Checked definedness (and thus range) and relocations. This access also doesn't
782 // influence interpreter execution but is only for debugging.
783 let c = alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + 1)[0];
784 write!(w, "{:02x}", c)?;
785 if c.is_ascii_control() || c >= 0x80 {
786 ascii.push('.');
787 } else {
788 ascii.push(char::from(c));
789 }
790 i += Size::from_bytes(1);
791 } else {
792 write!(w, "__")?;
793 ascii.push('░');
794 i += Size::from_bytes(1);
795 }
796 // Print a new line header if the next line still has some bytes to print.
797 if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size {
798 line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
799 ascii.clear();
800 }
801 }
802 write_allocation_endline(w, &ascii)?;
c30ab7b3
SL
803
804 Ok(())
3157f602
XL
805}
806
9fa01778 807fn write_mir_sig(
dc9dc135 808 tcx: TyCtxt<'_>,
9fa01778 809 src: MirSource<'tcx>,
dc9dc135 810 body: &Body<'_>,
9fa01778
XL
811 w: &mut dyn Write,
812) -> io::Result<()> {
dfeec247 813 use rustc_hir::def::DefKind;
9fa01778
XL
814
815 trace!("write_mir_sig: {:?}", src.instance);
48663c56
XL
816 let kind = tcx.def_kind(src.def_id());
817 let is_function = match kind {
f9f354fc 818 DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
9fa01778
XL
819 _ => tcx.is_closure(src.def_id()),
820 };
48663c56 821 match (kind, src.promoted) {
9fa01778 822 (_, Some(i)) => write!(w, "{:?} in ", i)?,
f9f354fc
XL
823 (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
824 (DefKind::Static, _) => {
dfeec247
XL
825 write!(w, "static {}", if tcx.is_mutable_static(src.def_id()) { "mut " } else { "" })?
826 }
9fa01778 827 (_, _) if is_function => write!(w, "fn ")?,
f9f354fc 828 (DefKind::AnonConst, _) => {} // things like anon const, not an item
48663c56 829 _ => bug!("Unexpected def kind {:?}", kind),
9cc50fc6
SL
830 }
831
532ac7d7 832 ty::print::with_forced_impl_filename_line(|| {
ff7c6d11 833 // see notes on #41697 elsewhere
ba9703b0 834 write!(w, "{}", tcx.def_path_str(src.def_id()))
7cac9316 835 })?;
a7813a04 836
9fa01778
XL
837 if src.promoted.is_none() && is_function {
838 write!(w, "(")?;
ea8adc8c 839
9fa01778 840 // fn argument types.
dc9dc135 841 for (i, arg) in body.args_iter().enumerate() {
9fa01778
XL
842 if i != 0 {
843 write!(w, ", ")?;
a7813a04 844 }
dc9dc135 845 write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
ea8adc8c 846 }
9fa01778 847
dc9dc135 848 write!(w, ") -> {}", body.return_ty())?;
9fa01778 849 } else {
dc9dc135
XL
850 assert_eq!(body.arg_count, 0);
851 write!(w, ": {} =", body.return_ty())?;
9cc50fc6 852 }
2c00a5a8 853
dc9dc135 854 if let Some(yield_ty) = body.yield_ty {
2c00a5a8
XL
855 writeln!(w)?;
856 writeln!(w, "yields {}", yield_ty)?;
857 }
858
9fa01778
XL
859 write!(w, " ")?;
860 // Next thing that gets printed is the opening {
861
2c00a5a8 862 Ok(())
3157f602 863}
9cc50fc6 864
f035d41b
XL
865fn write_user_type_annotations(
866 tcx: TyCtxt<'_>,
867 body: &Body<'_>,
868 w: &mut dyn Write,
869) -> io::Result<()> {
dc9dc135 870 if !body.user_type_annotations.is_empty() {
0731742a
XL
871 writeln!(w, "| User Type Annotations")?;
872 }
dc9dc135 873 for (index, annotation) in body.user_type_annotations.iter_enumerated() {
f035d41b
XL
874 writeln!(
875 w,
876 "| {:?}: {:?} at {}",
877 index.index(),
878 annotation.user_ty,
879 tcx.sess.source_map().span_to_string(annotation.span)
880 )?;
0731742a 881 }
dc9dc135 882 if !body.user_type_annotations.is_empty() {
0731742a
XL
883 writeln!(w, "|")?;
884 }
885 Ok(())
886}
887
dc9dc135 888pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
f9f354fc
XL
889 if let Some(i) = single {
890 vec![i]
891 } else {
892 tcx.mir_keys(LOCAL_CRATE).iter().map(|def_id| def_id.to_def_id()).collect()
893 }
7cac9316 894}