1 use std
::collections
::BTreeSet
;
3 use std
::fmt
::Write
as _
;
5 use std
::io
::{self, Write}
;
6 use std
::path
::{Path, PathBuf}
;
8 use super::graphviz
::write_mir_fn_graphviz
;
9 use super::spanview
::write_mir_fn_spanview
;
11 use rustc_data_structures
::fx
::FxHashMap
;
12 use rustc_hir
::def_id
::DefId
;
13 use rustc_index
::vec
::Idx
;
14 use rustc_middle
::mir
::interpret
::{
15 read_target_uint
, AllocId
, Allocation
, ConstAllocation
, ConstValue
, GlobalAlloc
, Pointer
,
18 use rustc_middle
::mir
::visit
::Visitor
;
19 use rustc_middle
::mir
::MirSource
;
20 use rustc_middle
::mir
::*;
21 use rustc_middle
::ty
::{self, TyCtxt}
;
22 use rustc_target
::abi
::Size
;
24 const INDENT
: &str = " ";
25 /// Alignment for lining up comments following MIR statements
26 pub(crate) const ALIGN
: usize = 40;
28 /// An indication of where we are in the control flow graph. Used for printing
29 /// extra information in `dump_mir`
31 /// We have not started dumping the control flow graph, but we are about to.
34 /// We just finished dumping the control flow graph. This is right before EOF
37 /// We are about to start dumping the given basic block.
38 BeforeBlock(BasicBlock
),
40 /// We are just about to dump the given statement or terminator.
41 BeforeLocation(Location
),
43 /// We just dumped the given statement or terminator.
44 AfterLocation(Location
),
46 /// We just dumped the terminator for a block but not the closing `}`.
47 AfterTerminator(BasicBlock
),
50 /// If the session is properly configured, dumps a human-readable
51 /// representation of the mir into:
54 /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
57 /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
58 /// where `<filter>` takes the following forms:
60 /// - `all` -- dump MIR for all fns, all passes, all everything
61 /// - a filter defined by a set of substrings combined with `&` and `|`
62 /// (`&` has higher precedence). At least one of the `|`-separated groups
63 /// must match; an `|`-separated group matches if all of its `&`-separated
64 /// substrings are matched.
68 /// - `nll` == match if `nll` appears in the name
69 /// - `foo & nll` == match if `foo` and `nll` both appear in the name
70 /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
71 /// or `typeck` appears in the name.
72 /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
73 /// or `typeck` and `bar` both appear in the name.
75 pub fn dump_mir
<'tcx
, F
>(
77 pass_num
: Option
<&dyn Display
>,
79 disambiguator
: &dyn Display
,
83 F
: FnMut(PassWhere
, &mut dyn Write
) -> io
::Result
<()>,
85 if !dump_enabled(tcx
, pass_name
, body
.source
.def_id()) {
89 dump_matched_mir_node(tcx
, pass_num
, pass_name
, disambiguator
, body
, extra_data
);
92 pub fn dump_enabled
<'tcx
>(tcx
: TyCtxt
<'tcx
>, pass_name
: &str, def_id
: DefId
) -> bool
{
93 let Some(ref filters
) = tcx
.sess
.opts
.debugging_opts
.dump_mir
else {
96 // see notes on #41697 below
97 let node_path
= ty
::print
::with_forced_impl_filename_line
!(tcx
.def_path_str(def_id
));
98 filters
.split('
|'
).any(|or_filter
| {
99 or_filter
.split('
&'
).all(|and_filter
| {
100 let and_filter_trimmed
= and_filter
.trim();
101 and_filter_trimmed
== "all"
102 || pass_name
.contains(and_filter_trimmed
)
103 || node_path
.contains(and_filter_trimmed
)
108 // #41697 -- we use `with_forced_impl_filename_line()` because
109 // `def_path_str()` would otherwise trigger `type_of`, and this can
110 // run while we are already attempting to evaluate `type_of`.
112 fn dump_matched_mir_node
<'tcx
, F
>(
114 pass_num
: Option
<&dyn Display
>,
116 disambiguator
: &dyn Display
,
120 F
: FnMut(PassWhere
, &mut dyn Write
) -> io
::Result
<()>,
122 let _
: io
::Result
<()> = try
{
124 create_dump_file(tcx
, "mir", pass_num
, pass_name
, disambiguator
, body
.source
)?
;
125 // see notes on #41697 above
127 ty
::print
::with_forced_impl_filename_line
!(tcx
.def_path_str(body
.source
.def_id()));
128 write
!(file
, "// MIR for `{}", def_path
)?
;
129 match body
.source
.promoted
{
130 None
=> write
!(file
, "`")?
,
131 Some(promoted
) => write
!(file
, "::{:?}`", promoted
)?
,
133 writeln
!(file
, " {} {}", disambiguator
, pass_name
)?
;
134 if let Some(ref layout
) = body
.generator_layout() {
135 writeln
!(file
, "/* generator_layout = {:#?} */", layout
)?
;
138 extra_data(PassWhere
::BeforeCFG
, &mut file
)?
;
139 write_user_type_annotations(tcx
, body
, &mut file
)?
;
140 write_mir_fn(tcx
, body
, &mut extra_data
, &mut file
)?
;
141 extra_data(PassWhere
::AfterCFG
, &mut file
)?
;
144 if tcx
.sess
.opts
.debugging_opts
.dump_mir_graphviz
{
145 let _
: io
::Result
<()> = try
{
147 create_dump_file(tcx
, "dot", pass_num
, pass_name
, disambiguator
, body
.source
)?
;
148 write_mir_fn_graphviz(tcx
, body
, false, &mut file
)?
;
152 if let Some(spanview
) = tcx
.sess
.opts
.debugging_opts
.dump_mir_spanview
{
153 let _
: io
::Result
<()> = try
{
155 dump_file_basename(tcx
, pass_num
, pass_name
, disambiguator
, body
.source
);
156 let mut file
= create_dump_file_with_basename(tcx
, &file_basename
, "html")?
;
157 if body
.source
.def_id().is_local() {
158 write_mir_fn_spanview(tcx
, body
, spanview
, &file_basename
, &mut file
)?
;
164 /// Returns the file basename portion (without extension) of a filename path
165 /// where we should dump a MIR representation output files.
166 fn dump_file_basename
<'tcx
>(
168 pass_num
: Option
<&dyn Display
>,
170 disambiguator
: &dyn Display
,
171 source
: MirSource
<'tcx
>,
173 let promotion_id
= match source
.promoted
{
174 Some(id
) => format
!("-{:?}", id
),
175 None
=> String
::new(),
178 let pass_num
= if tcx
.sess
.opts
.debugging_opts
.dump_mir_exclude_pass_number
{
182 None
=> ".-------".to_string(),
183 Some(pass_num
) => format
!(".{}", pass_num
),
187 let crate_name
= tcx
.crate_name(source
.def_id().krate
);
188 let item_name
= tcx
.def_path(source
.def_id()).to_filename_friendly_no_crate();
189 // All drop shims have the same DefId, so we have to add the type
190 // to get unique file names.
191 let shim_disambiguator
= match source
.instance
{
192 ty
::InstanceDef
::DropGlue(_
, Some(ty
)) => {
193 // Unfortunately, pretty-printed typed are not very filename-friendly.
194 // We dome some filtering.
195 let mut s
= ".".to_owned();
196 s
.extend(ty
.to_string().chars().filter_map(|c
| match c
{
198 '
:'
| '
<'
| '
>'
=> Some('_'
),
208 crate_name
, item_name
, shim_disambiguator
, promotion_id
, pass_num
, pass_name
, disambiguator
,
212 /// Returns the path to the filename where we should dump a given MIR.
213 /// Also used by other bits of code (e.g., NLL inference) that dump
214 /// graphviz data or other things.
215 fn dump_path(tcx
: TyCtxt
<'_
>, basename
: &str, extension
: &str) -> PathBuf
{
216 let mut file_path
= PathBuf
::new();
217 file_path
.push(Path
::new(&tcx
.sess
.opts
.debugging_opts
.dump_mir_dir
));
219 let file_name
= format
!("{}.{}", basename
, extension
,);
221 file_path
.push(&file_name
);
226 /// Attempts to open the MIR dump file with the given name and extension.
227 fn create_dump_file_with_basename(
231 ) -> io
::Result
<io
::BufWriter
<fs
::File
>> {
232 let file_path
= dump_path(tcx
, file_basename
, extension
);
233 if let Some(parent
) = file_path
.parent() {
234 fs
::create_dir_all(parent
).map_err(|e
| {
237 format
!("IO error creating MIR dump directory: {:?}; {}", parent
, e
),
241 Ok(io
::BufWriter
::new(fs
::File
::create(&file_path
).map_err(|e
| {
242 io
::Error
::new(e
.kind(), format
!("IO error creating MIR dump file: {:?}; {}", file_path
, e
))
246 /// Attempts to open a file where we should dump a given MIR or other
247 /// bit of MIR-related data. Used by `mir-dump`, but also by other
248 /// bits of code (e.g., NLL inference) that dump graphviz data or
249 /// other things, and hence takes the extension as an argument.
250 pub fn create_dump_file
<'tcx
>(
253 pass_num
: Option
<&dyn Display
>,
255 disambiguator
: &dyn Display
,
256 source
: MirSource
<'tcx
>,
257 ) -> io
::Result
<io
::BufWriter
<fs
::File
>> {
258 create_dump_file_with_basename(
260 &dump_file_basename(tcx
, pass_num
, pass_name
, disambiguator
, source
),
265 /// Write out a human-readable textual representation for the given MIR.
266 pub fn write_mir_pretty
<'tcx
>(
268 single
: Option
<DefId
>,
270 ) -> io
::Result
<()> {
271 writeln
!(w
, "// WARNING: This output format is intended for human consumers only")?
;
272 writeln
!(w
, "// and is subject to change without notice. Knock yourself out.")?
;
274 let mut first
= true;
275 for def_id
in dump_mir_def_ids(tcx
, single
) {
279 // Put empty lines between all items
283 let render_body
= |w
: &mut dyn Write
, body
| -> io
::Result
<()> {
284 write_mir_fn(tcx
, body
, &mut |_
, _
| Ok(()), w
)?
;
286 for body
in tcx
.promoted_mir(def_id
) {
288 write_mir_fn(tcx
, body
, &mut |_
, _
| Ok(()), w
)?
;
293 // For `const fn` we want to render both the optimized MIR and the MIR for ctfe.
294 if tcx
.is_const_fn_raw(def_id
) {
295 render_body(w
, tcx
.optimized_mir(def_id
))?
;
297 writeln
!(w
, "// MIR FOR CTFE")?
;
298 // Do not use `render_body`, as that would render the promoteds again, but these
299 // are shared between mir_for_ctfe and optimized_mir
300 write_mir_fn(tcx
, tcx
.mir_for_ctfe(def_id
), &mut |_
, _
| Ok(()), w
)?
;
303 tcx
.instance_mir(ty
::InstanceDef
::Item(ty
::WithOptConstParam
::unknown(def_id
)));
304 render_body(w
, instance_mir
)?
;
310 /// Write out a human-readable textual representation for the given function.
311 pub fn write_mir_fn
<'tcx
, F
>(
318 F
: FnMut(PassWhere
, &mut dyn Write
) -> io
::Result
<()>,
320 write_mir_intro(tcx
, body
, w
)?
;
321 for block
in body
.basic_blocks().indices() {
322 extra_data(PassWhere
::BeforeBlock(block
), w
)?
;
323 write_basic_block(tcx
, block
, body
, extra_data
, w
)?
;
324 if block
.index() + 1 != body
.basic_blocks().len() {
331 write_allocations(tcx
, body
, w
)?
;
336 /// Write out a human-readable textual representation for the given basic block.
337 pub fn write_basic_block
<'tcx
, F
>(
345 F
: FnMut(PassWhere
, &mut dyn Write
) -> io
::Result
<()>,
347 let data
= &body
[block
];
349 // Basic block label at the top.
350 let cleanup_text
= if data
.is_cleanup { " (cleanup)" }
else { "" }
;
351 writeln
!(w
, "{}{:?}{}: {{", INDENT
, block
, cleanup_text
)?
;
353 // List of statements in the middle.
354 let mut current_location
= Location { block, statement_index: 0 }
;
355 for statement
in &data
.statements
{
356 extra_data(PassWhere
::BeforeLocation(current_location
), w
)?
;
357 let indented_body
= format
!("{0}{0}{1:?};", INDENT
, statement
);
362 if tcx
.sess
.verbose() { format!("{:?}
: ", current_location) } else { String::new() },
363 comment(tcx, statement.source_info),
367 write_extra(tcx, w, |visitor| {
368 visitor.visit_statement(statement, current_location);
371 extra_data(PassWhere::AfterLocation(current_location), w)?;
373 current_location.statement_index += 1;
376 // Terminator at the bottom.
377 extra_data(PassWhere::BeforeLocation(current_location), w)?;
378 let indented_terminator = format!("{0}{0}{1:?}
;", INDENT, data.terminator().kind);
383 if tcx
.sess
.verbose() { format!("{:?}
: ", current_location) } else { String::new() },
384 comment(tcx, data.terminator().source_info),
388 write_extra(tcx, w, |visitor| {
389 visitor.visit_terminator(data.terminator(), current_location);
392 extra_data(PassWhere::AfterLocation(current_location), w)?;
393 extra_data(PassWhere::AfterTerminator(block), w)?;
395 writeln!(w, "{}
}}", INDENT)
398 /// After we print the main statement, we sometimes dump extra
399 /// information. There's often a lot of little things "nuzzled up
" in
401 fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
403 F: FnMut(&mut ExtraComments<'tcx>),
405 let mut extra_comments = ExtraComments { tcx, comments: vec![] };
406 visit_op(&mut extra_comments);
407 for comment in extra_comments.comments {
408 writeln!(write, "{:A$}
// {}", "", comment, A = ALIGN)?;
413 struct ExtraComments
<'tcx
> {
415 comments
: Vec
<String
>,
418 impl<'tcx
> ExtraComments
<'tcx
> {
419 fn push(&mut self, lines
: &str) {
420 for line
in lines
.split('
\n'
) {
421 self.comments
.push(line
.to_string());
426 fn use_verbose
<'tcx
>(ty
: Ty
<'tcx
>, fn_def
: bool
) -> bool
{
428 ty
::Int(_
) | ty
::Uint(_
) | ty
::Bool
| ty
::Char
| ty
::Float(_
) => false,
430 ty
::Tuple(g_args
) if g_args
.is_empty() => false,
431 ty
::Tuple(g_args
) => g_args
.iter().any(|g_arg
| use_verbose(g_arg
, fn_def
)),
432 ty
::Array(ty
, _
) => use_verbose(ty
, fn_def
),
433 ty
::FnDef(..) => fn_def
,
438 impl<'tcx
> Visitor
<'tcx
> for ExtraComments
<'tcx
> {
439 fn visit_constant(&mut self, constant
: &Constant
<'tcx
>, _location
: Location
) {
440 let Constant { span, user_ty, literal }
= constant
;
441 if use_verbose(literal
.ty(), true) {
442 self.push("mir::Constant");
445 self.tcx
.sess
.source_map().span_to_embeddable_string(*span
)
447 if let Some(user_ty
) = user_ty
{
448 self.push(&format
!("+ user_ty: {:?}", user_ty
));
451 let fmt_val
= |val
: &ConstValue
<'tcx
>| match val
{
452 ConstValue
::Scalar(s
) => format
!("Scalar({:?})", s
),
453 ConstValue
::Slice { .. }
=> format
!("Slice(..)"),
454 ConstValue
::ByRef { .. }
=> format
!("ByRef(..)"),
457 let fmt_valtree
= |valtree
: &ty
::ValTree
<'tcx
>| match valtree
{
458 ty
::ValTree
::Leaf(leaf
) => format
!("ValTree::Leaf({:?})", leaf
),
459 ty
::ValTree
::Branch(_
) => format
!("ValTree::Branch(..)"),
462 let val
= match literal
{
463 ConstantKind
::Ty(ct
) => match ct
.kind() {
464 ty
::ConstKind
::Param(p
) => format
!("Param({})", p
),
465 ty
::ConstKind
::Unevaluated(uv
) => format
!(
466 "Unevaluated({}, {:?}, {:?})",
467 self.tcx
.def_path_str(uv
.def
.did
),
471 ty
::ConstKind
::Value(val
) => format
!("Value({})", fmt_valtree(&val
)),
472 ty
::ConstKind
::Error(_
) => "Error".to_string(),
473 // These variants shouldn't exist in the MIR.
474 ty
::ConstKind
::Placeholder(_
)
475 | ty
::ConstKind
::Infer(_
)
476 | ty
::ConstKind
::Bound(..) => bug
!("unexpected MIR constant: {:?}", literal
),
478 // To keep the diffs small, we render this like we render `ty::Const::Value`.
480 // This changes once `ty::Const::Value` is represented using valtrees.
481 ConstantKind
::Val(val
, _
) => format
!("Value({})", fmt_val(&val
)),
484 // This reflects what `Const` looked liked before `val` was renamed
485 // as `kind`. We print it like this to avoid having to update
486 // expected output in a lot of tests.
487 self.push(&format
!("+ literal: Const {{ ty: {}, val: {} }}", literal
.ty(), val
));
491 fn visit_rvalue(&mut self, rvalue
: &Rvalue
<'tcx
>, location
: Location
) {
492 self.super_rvalue(rvalue
, location
);
493 if let Rvalue
::Aggregate(kind
, _
) = rvalue
{
495 AggregateKind
::Closure(def_id
, substs
) => {
496 self.push("closure");
497 self.push(&format
!("+ def_id: {:?}", def_id
));
498 self.push(&format
!("+ substs: {:#?}", substs
));
501 AggregateKind
::Generator(def_id
, substs
, movability
) => {
502 self.push("generator");
503 self.push(&format
!("+ def_id: {:?}", def_id
));
504 self.push(&format
!("+ substs: {:#?}", substs
));
505 self.push(&format
!("+ movability: {:?}", movability
));
508 AggregateKind
::Adt(_
, _
, _
, Some(user_ty
), _
) => {
510 self.push(&format
!("+ user_ty: {:?}", user_ty
));
519 fn comment(tcx
: TyCtxt
<'_
>, SourceInfo { span, scope }
: SourceInfo
) -> String
{
520 format
!("scope {} at {}", scope
.index(), tcx
.sess
.source_map().span_to_embeddable_string(span
))
523 /// Prints local variables in a scope tree.
527 scope_tree
: &FxHashMap
<SourceScope
, Vec
<SourceScope
>>,
531 ) -> io
::Result
<()> {
532 let indent
= depth
* INDENT
.len();
534 // Local variable debuginfo.
535 for var_debug_info
in &body
.var_debug_info
{
536 if var_debug_info
.source_info
.scope
!= parent
{
537 // Not declared in this scope.
541 let indented_debug_info
= format
!(
542 "{0:1$}debug {2} => {3:?};",
543 INDENT
, indent
, var_debug_info
.name
, var_debug_info
.value
,
551 comment(tcx
, var_debug_info
.source_info
),
555 // Local variable types.
556 for (local
, local_decl
) in body
.local_decls
.iter_enumerated() {
557 if (1..body
.arg_count
+ 1).contains(&local
.index()) {
558 // Skip over argument locals, they're printed in the signature.
562 if local_decl
.source_info
.scope
!= parent
{
563 // Not declared in this scope.
567 let mut_str
= if local_decl
.mutability
== Mutability
::Mut { "mut " }
else { "" }
;
569 let mut indented_decl
=
570 format
!("{0:1$}let {2}{3:?}: {4:?}", INDENT
, indent
, mut_str
, local
, local_decl
.ty
);
571 if let Some(user_ty
) = &local_decl
.user_ty
{
572 for user_ty
in user_ty
.projections() {
573 write
!(indented_decl
, " as {:?}", user_ty
).unwrap();
576 indented_decl
.push('
;'
);
578 let local_name
= if local
== RETURN_PLACE { " return place" }
else { "" }
;
582 "{0:1$} //{2} in {3}",
586 comment(tcx
, local_decl
.source_info
),
590 let Some(children
) = scope_tree
.get(&parent
) else {
594 for &child
in children
{
595 let child_data
= &body
.source_scopes
[child
];
596 assert_eq
!(child_data
.parent_scope
, Some(parent
));
598 let (special
, span
) = if let Some((callee
, callsite_span
)) = child_data
.inlined
{
602 if callee
.def
.requires_caller_location(tcx
) { "#[track_caller] " }
else { "" }
,
608 (String
::new(), None
)
611 let indented_header
= format
!("{0:1$}scope {2}{3} {{", "", indent
, child
.index(), special
);
613 if let Some(span
) = span
{
619 tcx
.sess
.source_map().span_to_embeddable_string(span
),
622 writeln
!(w
, "{}", indented_header
)?
;
625 write_scope_tree(tcx
, body
, scope_tree
, w
, child
, depth
+ 1)?
;
626 writeln
!(w
, "{0:1$}}}", "", depth
* INDENT
.len())?
;
632 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
633 /// local variables (both user-defined bindings and compiler temporaries).
634 pub fn write_mir_intro
<'tcx
>(
638 ) -> io
::Result
<()> {
639 write_mir_sig(tcx
, body
, w
)?
;
642 // construct a scope tree and write it out
643 let mut scope_tree
: FxHashMap
<SourceScope
, Vec
<SourceScope
>> = Default
::default();
644 for (index
, scope_data
) in body
.source_scopes
.iter().enumerate() {
645 if let Some(parent
) = scope_data
.parent_scope
{
646 scope_tree
.entry(parent
).or_default().push(SourceScope
::new(index
));
648 // Only the argument scope has no parent, because it's the root.
649 assert_eq
!(index
, OUTERMOST_SOURCE_SCOPE
.index());
653 write_scope_tree(tcx
, body
, &scope_tree
, w
, OUTERMOST_SOURCE_SCOPE
, 1)?
;
655 // Add an empty line before the first block is printed.
661 /// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
663 pub fn write_allocations
<'tcx
>(
667 ) -> io
::Result
<()> {
668 fn alloc_ids_from_alloc(
669 alloc
: ConstAllocation
<'_
>,
670 ) -> impl DoubleEndedIterator
<Item
= AllocId
> + '_
{
671 alloc
.inner().relocations().values().map(|id
| *id
)
674 fn alloc_ids_from_const_val(val
: ConstValue
<'_
>) -> impl Iterator
<Item
= AllocId
> + '_
{
676 ConstValue
::Scalar(interpret
::Scalar
::Ptr(ptr
, _
)) => {
677 Either
::Left(Either
::Left(std
::iter
::once(ptr
.provenance
)))
679 ConstValue
::Scalar(interpret
::Scalar
::Int { .. }
) => {
680 Either
::Left(Either
::Right(std
::iter
::empty()))
682 ConstValue
::ByRef { alloc, .. }
| ConstValue
::Slice { data: alloc, .. }
=> {
683 Either
::Right(alloc_ids_from_alloc(alloc
))
687 struct CollectAllocIds(BTreeSet
<AllocId
>);
689 impl<'tcx
> Visitor
<'tcx
> for CollectAllocIds
{
690 fn visit_constant(&mut self, c
: &Constant
<'tcx
>, loc
: Location
) {
692 ConstantKind
::Ty(c
) => self.visit_const(c
, loc
),
693 ConstantKind
::Val(val
, _
) => {
694 self.0.extend(alloc_ids_from_const_val(val
));
700 let mut visitor
= CollectAllocIds(Default
::default());
701 visitor
.visit_body(body
);
703 // `seen` contains all seen allocations, including the ones we have *not* printed yet.
704 // The protocol is to first `insert` into `seen`, and only if that returns `true`
705 // then push to `todo`.
706 let mut seen
= visitor
.0;
707 let mut todo
: Vec
<_
> = seen
.iter().copied().collect();
708 while let Some(id
) = todo
.pop() {
709 let mut write_allocation_track_relocs
=
710 |w
: &mut dyn Write
, alloc
: ConstAllocation
<'tcx
>| -> io
::Result
<()> {
711 // `.rev()` because we are popping them from the back of the `todo` vector.
712 for id
in alloc_ids_from_alloc(alloc
).rev() {
717 write
!(w
, "{}", display_allocation(tcx
, alloc
.inner()))
719 write
!(w
, "\n{}", id
)?
;
720 match tcx
.get_global_alloc(id
) {
721 // This can't really happen unless there are bugs, but it doesn't cost us anything to
722 // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
723 None
=> write
!(w
, " (deallocated)")?
,
724 Some(GlobalAlloc
::Function(inst
)) => write
!(w
, " (fn: {})", inst
)?
,
725 Some(GlobalAlloc
::Static(did
)) if !tcx
.is_foreign_item(did
) => {
726 match tcx
.eval_static_initializer(did
) {
728 write
!(w
, " (static: {}, ", tcx
.def_path_str(did
))?
;
729 write_allocation_track_relocs(w
, alloc
)?
;
733 " (static: {}, error during initializer evaluation)",
734 tcx
.def_path_str(did
)
738 Some(GlobalAlloc
::Static(did
)) => {
739 write
!(w
, " (extern static: {})", tcx
.def_path_str(did
))?
741 Some(GlobalAlloc
::Memory(alloc
)) => {
743 write_allocation_track_relocs(w
, alloc
)?
751 /// Dumps the size and metadata and content of an allocation to the given writer.
752 /// The expectation is that the caller first prints other relevant metadata, so the exact
753 /// format of this function is (*without* leading or trailing newline):
756 /// size: {}, align: {}) {
761 /// The byte format is similar to how hex editors print bytes. Each line starts with the address of
762 /// the start of the line, followed by all bytes in hex format (space separated).
763 /// If the allocation is small enough to fit into a single line, no start address is given.
764 /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
765 /// characters or characters whose value is larger than 127) with a `.`
766 /// This also prints relocations adequately.
767 pub fn display_allocation
<'a
, 'tcx
, Tag
, Extra
>(
769 alloc
: &'a Allocation
<Tag
, Extra
>,
770 ) -> RenderAllocation
<'a
, 'tcx
, Tag
, Extra
> {
771 RenderAllocation { tcx, alloc }
775 pub struct RenderAllocation
<'a
, 'tcx
, Tag
, Extra
> {
777 alloc
: &'a Allocation
<Tag
, Extra
>,
780 impl<'a
, 'tcx
, Tag
: Provenance
, Extra
> std
::fmt
::Display
781 for RenderAllocation
<'a
, 'tcx
, Tag
, Extra
>
783 fn fmt(&self, w
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
784 let RenderAllocation { tcx, alloc }
= *self;
785 write
!(w
, "size: {}, align: {})", alloc
.size().bytes(), alloc
.align
.bytes())?
;
786 if alloc
.size() == Size
::ZERO
{
788 return write
!(w
, " {{}}");
790 // Write allocation bytes.
792 write_allocation_bytes(tcx
, alloc
, w
, " ")?
;
798 fn write_allocation_endline(w
: &mut dyn std
::fmt
::Write
, ascii
: &str) -> std
::fmt
::Result
{
799 for _
in 0..(BYTES_PER_LINE
- ascii
.chars().count()) {
802 writeln
!(w
, " │ {}", ascii
)
805 /// Number of bytes to print per allocation hex dump line.
806 const BYTES_PER_LINE
: usize = 16;
808 /// Prints the line start address and returns the new line start address.
809 fn write_allocation_newline(
810 w
: &mut dyn std
::fmt
::Write
,
811 mut line_start
: Size
,
815 ) -> Result
<Size
, std
::fmt
::Error
> {
816 write_allocation_endline(w
, ascii
)?
;
817 line_start
+= Size
::from_bytes(BYTES_PER_LINE
);
818 write
!(w
, "{}0x{:02$x} │ ", prefix
, line_start
.bytes(), pos_width
)?
;
822 /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
823 /// is only one line). Note that your prefix should contain a trailing space as the lines are
824 /// printed directly after it.
825 fn write_allocation_bytes
<'tcx
, Tag
: Provenance
, Extra
>(
827 alloc
: &Allocation
<Tag
, Extra
>,
828 w
: &mut dyn std
::fmt
::Write
,
830 ) -> std
::fmt
::Result
{
831 let num_lines
= alloc
.size().bytes_usize().saturating_sub(BYTES_PER_LINE
);
832 // Number of chars needed to represent all line numbers.
833 let pos_width
= hex_number_length(alloc
.size().bytes());
836 write
!(w
, "{}0x{:02$x} │ ", prefix
, 0, pos_width
)?
;
838 write
!(w
, "{}", prefix
)?
;
841 let mut i
= Size
::ZERO
;
842 let mut line_start
= Size
::ZERO
;
844 let ptr_size
= tcx
.data_layout
.pointer_size
;
846 let mut ascii
= String
::new();
848 let oversized_ptr
= |target
: &mut String
, width
| {
849 if target
.len() > width
{
850 write
!(target
, " ({} ptr bytes)", ptr_size
.bytes()).unwrap();
854 while i
< alloc
.size() {
855 // The line start already has a space. While we could remove that space from the line start
856 // printing and unconditionally print a space here, that would cause the single-line case
857 // to have a single space before it, which looks weird.
861 if let Some(&tag
) = alloc
.relocations().get(&i
) {
862 // Memory with a relocation must be defined
863 assert
!(alloc
.init_mask().is_range_initialized(i
, i
+ ptr_size
).is_ok());
864 let j
= i
.bytes_usize();
866 .inspect_with_uninit_and_ptr_outside_interpreter(j
..j
+ ptr_size
.bytes_usize());
867 let offset
= read_target_uint(tcx
.data_layout
.endian
, offset
).unwrap();
868 let offset
= Size
::from_bytes(offset
);
869 let relocation_width
= |bytes
| bytes
* 3;
870 let ptr
= Pointer
::new(tag
, offset
);
871 let mut target
= format
!("{:?}", ptr
);
872 if target
.len() > relocation_width(ptr_size
.bytes_usize() - 1) {
873 // This is too long, try to save some space.
874 target
= format
!("{:#?}", ptr
);
876 if ((i
- line_start
) + ptr_size
).bytes_usize() > BYTES_PER_LINE
{
877 // This branch handles the situation where a relocation starts in the current line
878 // but ends in the next one.
879 let remainder
= Size
::from_bytes(BYTES_PER_LINE
) - (i
- line_start
);
880 let overflow
= ptr_size
- remainder
;
881 let remainder_width
= relocation_width(remainder
.bytes_usize()) - 2;
882 let overflow_width
= relocation_width(overflow
.bytes_usize() - 1) + 1;
884 for _
in 0..remainder
.bytes() - 1 {
887 if overflow_width
> remainder_width
&& overflow_width
>= target
.len() {
888 // The case where the relocation fits into the part in the next line
889 write
!(w
, "╾{0:─^1$}", "", remainder_width
)?
;
891 write_allocation_newline(w
, line_start
, &ascii
, pos_width
, prefix
)?
;
893 write
!(w
, "{0:─^1$}╼", target
, overflow_width
)?
;
895 oversized_ptr(&mut target
, remainder_width
);
896 write
!(w
, "╾{0:─^1$}", target
, remainder_width
)?
;
898 write_allocation_newline(w
, line_start
, &ascii
, pos_width
, prefix
)?
;
899 write
!(w
, "{0:─^1$}╼", "", overflow_width
)?
;
902 for _
in 0..overflow
.bytes() - 1 {
909 // This branch handles a relocation that starts and ends in the current line.
910 let relocation_width
= relocation_width(ptr_size
.bytes_usize() - 1);
911 oversized_ptr(&mut target
, relocation_width
);
913 write
!(w
, "╾{0:─^1$}╼", target
, relocation_width
)?
;
914 for _
in 0..ptr_size
.bytes() - 2 {
920 } else if alloc
.init_mask().is_range_initialized(i
, i
+ Size
::from_bytes(1)).is_ok() {
921 let j
= i
.bytes_usize();
923 // Checked definedness (and thus range) and relocations. This access also doesn't
924 // influence interpreter execution but is only for debugging.
925 let c
= alloc
.inspect_with_uninit_and_ptr_outside_interpreter(j
..j
+ 1)[0];
926 write
!(w
, "{:02x}", c
)?
;
927 if c
.is_ascii_control() || c
>= 0x80 {
930 ascii
.push(char::from(c
));
932 i
+= Size
::from_bytes(1);
936 i
+= Size
::from_bytes(1);
938 // Print a new line header if the next line still has some bytes to print.
939 if i
== line_start
+ Size
::from_bytes(BYTES_PER_LINE
) && i
!= alloc
.size() {
940 line_start
= write_allocation_newline(w
, line_start
, &ascii
, pos_width
, prefix
)?
;
944 write_allocation_endline(w
, &ascii
)?
;
949 fn write_mir_sig(tcx
: TyCtxt
<'_
>, body
: &Body
<'_
>, w
: &mut dyn Write
) -> io
::Result
<()> {
950 use rustc_hir
::def
::DefKind
;
952 trace
!("write_mir_sig: {:?}", body
.source
.instance
);
953 let def_id
= body
.source
.def_id();
954 let kind
= tcx
.def_kind(def_id
);
955 let is_function
= match kind
{
956 DefKind
::Fn
| DefKind
::AssocFn
| DefKind
::Ctor(..) => true,
957 _
=> tcx
.is_closure(def_id
),
959 match (kind
, body
.source
.promoted
) {
960 (_
, Some(i
)) => write
!(w
, "{:?} in ", i
)?
,
961 (DefKind
::Const
| DefKind
::AssocConst
, _
) => write
!(w
, "const ")?
,
962 (DefKind
::Static(hir
::Mutability
::Not
), _
) => write
!(w
, "static ")?
,
963 (DefKind
::Static(hir
::Mutability
::Mut
), _
) => write
!(w
, "static mut ")?
,
964 (_
, _
) if is_function
=> write
!(w
, "fn ")?
,
965 (DefKind
::AnonConst
| DefKind
::InlineConst
, _
) => {}
// things like anon const, not an item
966 _
=> bug
!("Unexpected def kind {:?}", kind
),
969 ty
::print
::with_forced_impl_filename_line
! {
970 // see notes on #41697 elsewhere
971 write
!(w
, "{}", tcx
.def_path_str(def_id
))?
974 if body
.source
.promoted
.is_none() && is_function
{
977 // fn argument types.
978 for (i
, arg
) in body
.args_iter().enumerate() {
982 write
!(w
, "{:?}: {}", Place
::from(arg
), body
.local_decls
[arg
].ty
)?
;
985 write
!(w
, ") -> {}", body
.return_ty())?
;
987 assert_eq
!(body
.arg_count
, 0);
988 write
!(w
, ": {} =", body
.return_ty())?
;
991 if let Some(yield_ty
) = body
.yield_ty() {
993 writeln
!(w
, "yields {}", yield_ty
)?
;
997 // Next thing that gets printed is the opening {
1002 fn write_user_type_annotations(
1006 ) -> io
::Result
<()> {
1007 if !body
.user_type_annotations
.is_empty() {
1008 writeln
!(w
, "| User Type Annotations")?
;
1010 for (index
, annotation
) in body
.user_type_annotations
.iter_enumerated() {
1013 "| {:?}: user_ty: {:?}, span: {}, inferred_ty: {:?}",
1016 tcx
.sess
.source_map().span_to_embeddable_string(annotation
.span
),
1017 annotation
.inferred_ty
,
1020 if !body
.user_type_annotations
.is_empty() {
1026 pub fn dump_mir_def_ids(tcx
: TyCtxt
<'_
>, single
: Option
<DefId
>) -> Vec
<DefId
> {
1027 if let Some(i
) = single
{
1030 tcx
.mir_keys(()).iter().map(|def_id
| def_id
.to_def_id()).collect()
1034 /// Calc converted u64 decimal into hex and return it's length in chars
1036 /// ```ignore (cannot-test-private-function)
1037 /// assert_eq!(1, hex_number_length(0));
1038 /// assert_eq!(1, hex_number_length(1));
1039 /// assert_eq!(2, hex_number_length(16));
1041 fn hex_number_length(x
: u64) -> usize {