1 use std
::collections
::BTreeSet
;
2 use std
::fmt
::Write
as _
;
3 use std
::fmt
::{Debug, Display}
;
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
;
10 use crate::transform
::MirSource
;
12 use rustc_data_structures
::fx
::FxHashMap
;
13 use rustc_hir
::def_id
::{DefId, LOCAL_CRATE}
;
14 use rustc_index
::vec
::Idx
;
15 use rustc_middle
::mir
::interpret
::{
16 read_target_uint
, AllocId
, Allocation
, ConstValue
, GlobalAlloc
, Pointer
,
18 use rustc_middle
::mir
::visit
::Visitor
;
19 use rustc_middle
::mir
::*;
20 use rustc_middle
::ty
::{self, TyCtxt, TypeFoldable, TypeVisitor}
;
21 use rustc_target
::abi
::Size
;
23 const INDENT
: &str = " ";
24 /// Alignment for lining up comments following MIR statements
25 pub(crate) const ALIGN
: usize = 40;
27 /// An indication of where we are in the control flow graph. Used for printing
28 /// extra information in `dump_mir`
30 /// We have not started dumping the control flow graph, but we are about to.
33 /// We just finished dumping the control flow graph. This is right before EOF
36 /// We are about to start dumping the given basic block.
37 BeforeBlock(BasicBlock
),
39 /// We are just about to dump the given statement or terminator.
40 BeforeLocation(Location
),
42 /// We just dumped the given statement or terminator.
43 AfterLocation(Location
),
45 /// We just dumped the terminator for a block but not the closing `}`.
46 AfterTerminator(BasicBlock
),
49 /// If the session is properly configured, dumps a human-readable
50 /// representation of the mir into:
53 /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
56 /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
57 /// where `<filter>` takes the following forms:
59 /// - `all` -- dump MIR for all fns, all passes, all everything
60 /// - a filter defined by a set of substrings combined with `&` and `|`
61 /// (`&` has higher precedence). At least one of the `|`-separated groups
62 /// must match; an `|`-separated group matches if all of its `&`-separated
63 /// substrings are matched.
67 /// - `nll` == match if `nll` appears in the name
68 /// - `foo & nll` == match if `foo` and `nll` both appear in the name
69 /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
70 /// or `typeck` appears in the name.
71 /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
72 /// or `typeck` and `bar` both appear in the name.
73 pub fn dump_mir
<'tcx
, F
>(
75 pass_num
: Option
<&dyn Display
>,
77 disambiguator
: &dyn Display
,
78 source
: MirSource
<'tcx
>,
82 F
: FnMut(PassWhere
, &mut dyn Write
) -> io
::Result
<()>,
84 if !dump_enabled(tcx
, pass_name
, source
.def_id()) {
88 dump_matched_mir_node(tcx
, pass_num
, pass_name
, disambiguator
, source
, body
, extra_data
);
91 pub fn dump_enabled
<'tcx
>(tcx
: TyCtxt
<'tcx
>, pass_name
: &str, def_id
: DefId
) -> bool
{
92 let filters
= match tcx
.sess
.opts
.debugging_opts
.dump_mir
{
94 Some(ref filters
) => filters
,
96 let node_path
= ty
::print
::with_forced_impl_filename_line(|| {
97 // see notes on #41697 below
98 tcx
.def_path_str(def_id
)
100 filters
.split('
|'
).any(|or_filter
| {
101 or_filter
.split('
&'
).all(|and_filter
| {
102 and_filter
== "all" || pass_name
.contains(and_filter
) || node_path
.contains(and_filter
)
107 // #41697 -- we use `with_forced_impl_filename_line()` because
108 // `def_path_str()` would otherwise trigger `type_of`, and this can
109 // run while we are already attempting to evaluate `type_of`.
111 fn dump_matched_mir_node
<'tcx
, F
>(
113 pass_num
: Option
<&dyn Display
>,
115 disambiguator
: &dyn Display
,
116 source
: MirSource
<'tcx
>,
120 F
: FnMut(PassWhere
, &mut dyn Write
) -> io
::Result
<()>,
122 let _
: io
::Result
<()> = try
{
123 let mut file
= create_dump_file(tcx
, "mir", pass_num
, pass_name
, disambiguator
, source
)?
;
124 let def_path
= ty
::print
::with_forced_impl_filename_line(|| {
125 // see notes on #41697 above
126 tcx
.def_path_str(source
.def_id())
128 write
!(file
, "// MIR for `{}", def_path
)?
;
129 match 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
, source
, 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
, source
)?
;
148 write_mir_fn_graphviz(tcx
, source
.def_id(), body
, false, &mut file
)?
;
152 if let Some(spanview
) = tcx
.sess
.opts
.debugging_opts
.dump_mir_spanview
{
153 let _
: io
::Result
<()> = try
{
155 create_dump_file(tcx
, "html", pass_num
, pass_name
, disambiguator
, source
)?
;
156 if source
.def_id().is_local() {
157 write_mir_fn_spanview(tcx
, source
.def_id(), body
, spanview
, &mut file
)?
;
163 /// Returns the path to the filename where we should dump a given MIR.
164 /// Also used by other bits of code (e.g., NLL inference) that dump
165 /// graphviz data or other things.
169 pass_num
: Option
<&dyn Display
>,
171 disambiguator
: &dyn Display
,
172 source
: MirSource
<'tcx
>,
174 let promotion_id
= match source
.promoted
{
175 Some(id
) => format
!("-{:?}", id
),
176 None
=> String
::new(),
179 let pass_num
= if tcx
.sess
.opts
.debugging_opts
.dump_mir_exclude_pass_number
{
183 None
=> ".-------".to_string(),
184 Some(pass_num
) => format
!(".{}", pass_num
),
188 let mut file_path
= PathBuf
::new();
189 file_path
.push(Path
::new(&tcx
.sess
.opts
.debugging_opts
.dump_mir_dir
));
191 let crate_name
= tcx
.crate_name(source
.def_id().krate
);
192 let item_name
= tcx
.def_path(source
.def_id()).to_filename_friendly_no_crate();
193 // All drop shims have the same DefId, so we have to add the type
194 // to get unique file names.
195 let shim_disambiguator
= match source
.instance
{
196 ty
::InstanceDef
::DropGlue(_
, Some(ty
)) => {
197 // Unfortunately, pretty-printed typed are not very filename-friendly.
198 // We dome some filtering.
199 let mut s
= ".".to_owned();
200 s
.extend(ty
.to_string().chars().filter_map(|c
| match c
{
202 '
:'
| '
<'
| '
>'
=> Some('_'
),
210 let file_name
= format
!(
211 "{}.{}{}{}{}.{}.{}.{}",
222 file_path
.push(&file_name
);
227 /// Attempts to open a file where we should dump a given MIR or other
228 /// bit of MIR-related data. Used by `mir-dump`, but also by other
229 /// bits of code (e.g., NLL inference) that dump graphviz data or
230 /// other things, and hence takes the extension as an argument.
231 pub(crate) fn create_dump_file(
234 pass_num
: Option
<&dyn Display
>,
236 disambiguator
: &dyn Display
,
237 source
: MirSource
<'tcx
>,
238 ) -> io
::Result
<io
::BufWriter
<fs
::File
>> {
239 let file_path
= dump_path(tcx
, extension
, pass_num
, pass_name
, disambiguator
, source
);
240 if let Some(parent
) = file_path
.parent() {
241 fs
::create_dir_all(parent
)?
;
243 Ok(io
::BufWriter
::new(fs
::File
::create(&file_path
)?
))
246 /// Write out a human-readable textual representation for the given MIR.
247 pub fn write_mir_pretty
<'tcx
>(
249 single
: Option
<DefId
>,
251 ) -> io
::Result
<()> {
252 writeln
!(w
, "// WARNING: This output format is intended for human consumers only")?
;
253 writeln
!(w
, "// and is subject to change without notice. Knock yourself out.")?
;
255 let mut first
= true;
256 for def_id
in dump_mir_def_ids(tcx
, single
) {
257 let body
= &tcx
.optimized_mir(def_id
);
262 // Put empty lines between all items
266 write_mir_fn(tcx
, MirSource
::item(def_id
), body
, &mut |_
, _
| Ok(()), w
)?
;
268 for (i
, body
) in tcx
.promoted_mir(def_id
).iter_enumerated() {
270 let src
= MirSource
{
271 instance
: ty
::InstanceDef
::Item(ty
::WithOptConstParam
::unknown(def_id
)),
274 write_mir_fn(tcx
, src
, body
, &mut |_
, _
| Ok(()), w
)?
;
280 /// Write out a human-readable textual representation for the given function.
281 pub fn write_mir_fn
<'tcx
, F
>(
283 src
: MirSource
<'tcx
>,
289 F
: FnMut(PassWhere
, &mut dyn Write
) -> io
::Result
<()>,
291 write_mir_intro(tcx
, src
, body
, w
)?
;
292 for block
in body
.basic_blocks().indices() {
293 extra_data(PassWhere
::BeforeBlock(block
), w
)?
;
294 write_basic_block(tcx
, block
, body
, extra_data
, w
)?
;
295 if block
.index() + 1 != body
.basic_blocks().len() {
302 write_allocations(tcx
, body
, w
)?
;
307 /// Write out a human-readable textual representation for the given basic block.
308 pub fn write_basic_block
<'tcx
, F
>(
316 F
: FnMut(PassWhere
, &mut dyn Write
) -> io
::Result
<()>,
318 let data
= &body
[block
];
320 // Basic block label at the top.
321 let cleanup_text
= if data
.is_cleanup { " (cleanup)" }
else { "" }
;
322 writeln
!(w
, "{}{:?}{}: {{", INDENT
, block
, cleanup_text
)?
;
324 // List of statements in the middle.
325 let mut current_location
= Location { block, statement_index: 0 }
;
326 for statement
in &data
.statements
{
327 extra_data(PassWhere
::BeforeLocation(current_location
), w
)?
;
328 let indented_body
= format
!("{0}{0}{1:?};", INDENT
, statement
);
333 if tcx
.sess
.verbose() { format!("{:?}
: ", current_location) } else { String::new() },
334 comment(tcx, statement.source_info),
338 write_extra(tcx, w, |visitor| {
339 visitor.visit_statement(statement, current_location);
342 extra_data(PassWhere::AfterLocation(current_location), w)?;
344 current_location.statement_index += 1;
347 // Terminator at the bottom.
348 extra_data(PassWhere::BeforeLocation(current_location), w)?;
349 let indented_terminator = format!("{0}{0}{1:?}
;", INDENT, data.terminator().kind);
354 if tcx
.sess
.verbose() { format!("{:?}
: ", current_location) } else { String::new() },
355 comment(tcx, data.terminator().source_info),
359 write_extra(tcx, w, |visitor| {
360 visitor.visit_terminator(data.terminator(), current_location);
363 extra_data(PassWhere::AfterLocation(current_location), w)?;
364 extra_data(PassWhere::AfterTerminator(block), w)?;
366 writeln!(w, "{}
}}", INDENT)
369 /// After we print the main statement, we sometimes dump extra
370 /// information. There's often a lot of little things "nuzzled up
" in
372 fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
374 F: FnMut(&mut ExtraComments<'tcx>),
376 let mut extra_comments = ExtraComments { tcx, comments: vec![] };
377 visit_op(&mut extra_comments);
378 for comment in extra_comments.comments {
379 writeln!(write, "{:A$}
// {}", "", comment, A = ALIGN)?;
384 struct ExtraComments
<'tcx
> {
386 comments
: Vec
<String
>,
389 impl ExtraComments
<'tcx
> {
390 fn push(&mut self, lines
: &str) {
391 for line
in lines
.split('
\n'
) {
392 self.comments
.push(line
.to_string());
397 impl Visitor
<'tcx
> for ExtraComments
<'tcx
> {
398 fn visit_constant(&mut self, constant
: &Constant
<'tcx
>, location
: Location
) {
399 self.super_constant(constant
, location
);
400 let Constant { span, user_ty, literal }
= constant
;
401 match literal
.ty
.kind() {
402 ty
::Int(_
) | ty
::Uint(_
) | ty
::Bool
| ty
::Char
=> {}
404 ty
::Tuple(tys
) if tys
.is_empty() => {}
406 self.push("mir::Constant");
407 self.push(&format
!("+ span: {}", self.tcx
.sess
.source_map().span_to_string(*span
)));
408 if let Some(user_ty
) = user_ty
{
409 self.push(&format
!("+ user_ty: {:?}", user_ty
));
411 self.push(&format
!("+ literal: {:?}", literal
));
416 fn visit_const(&mut self, constant
: &&'tcx ty
::Const
<'tcx
>, _
: Location
) {
417 self.super_const(constant
);
418 let ty
::Const { ty, val, .. }
= constant
;
420 ty
::Int(_
) | ty
::Uint(_
) | ty
::Bool
| ty
::Char
| ty
::Float(_
) => {}
422 ty
::Tuple(tys
) if tys
.is_empty() => {}
425 self.push("ty::Const");
426 self.push(&format
!("+ ty: {:?}", ty
));
427 self.push(&format
!("+ val: {:?}", val
));
432 fn visit_rvalue(&mut self, rvalue
: &Rvalue
<'tcx
>, location
: Location
) {
433 self.super_rvalue(rvalue
, location
);
434 if let Rvalue
::Aggregate(kind
, _
) = rvalue
{
436 AggregateKind
::Closure(def_id
, substs
) => {
437 self.push("closure");
438 self.push(&format
!("+ def_id: {:?}", def_id
));
439 self.push(&format
!("+ substs: {:#?}", substs
));
442 AggregateKind
::Generator(def_id
, substs
, movability
) => {
443 self.push("generator");
444 self.push(&format
!("+ def_id: {:?}", def_id
));
445 self.push(&format
!("+ substs: {:#?}", substs
));
446 self.push(&format
!("+ movability: {:?}", movability
));
449 AggregateKind
::Adt(_
, _
, _
, Some(user_ty
), _
) => {
451 self.push(&format
!("+ user_ty: {:?}", user_ty
));
460 fn comment(tcx
: TyCtxt
<'_
>, SourceInfo { span, scope }
: SourceInfo
) -> String
{
461 format
!("scope {} at {}", scope
.index(), tcx
.sess
.source_map().span_to_string(span
))
464 /// Prints local variables in a scope tree.
468 scope_tree
: &FxHashMap
<SourceScope
, Vec
<SourceScope
>>,
472 ) -> io
::Result
<()> {
473 let indent
= depth
* INDENT
.len();
475 // Local variable debuginfo.
476 for var_debug_info
in &body
.var_debug_info
{
477 if var_debug_info
.source_info
.scope
!= parent
{
478 // Not declared in this scope.
482 let indented_debug_info
= format
!(
483 "{0:1$}debug {2} => {3:?};",
484 INDENT
, indent
, var_debug_info
.name
, var_debug_info
.place
,
492 comment(tcx
, var_debug_info
.source_info
),
496 // Local variable types.
497 for (local
, local_decl
) in body
.local_decls
.iter_enumerated() {
498 if (1..body
.arg_count
+ 1).contains(&local
.index()) {
499 // Skip over argument locals, they're printed in the signature.
503 if local_decl
.source_info
.scope
!= parent
{
504 // Not declared in this scope.
508 let mut_str
= if local_decl
.mutability
== Mutability
::Mut { "mut " }
else { "" }
;
510 let mut indented_decl
=
511 format
!("{0:1$}let {2}{3:?}: {4:?}", INDENT
, indent
, mut_str
, local
, local_decl
.ty
);
512 if let Some(user_ty
) = &local_decl
.user_ty
{
513 for user_ty
in user_ty
.projections() {
514 write
!(indented_decl
, " as {:?}", user_ty
).unwrap();
517 indented_decl
.push('
;'
);
520 if local
== RETURN_PLACE { " return place".to_string() }
else { String::new() }
;
524 "{0:1$} //{2} in {3}",
528 comment(tcx
, local_decl
.source_info
),
532 let children
= match scope_tree
.get(&parent
) {
533 Some(children
) => children
,
534 None
=> return Ok(()),
537 for &child
in children
{
538 assert_eq
!(body
.source_scopes
[child
].parent_scope
, Some(parent
));
539 writeln
!(w
, "{0:1$}scope {2} {{", "", indent
, child
.index())?
;
540 write_scope_tree(tcx
, body
, scope_tree
, w
, child
, depth
+ 1)?
;
541 writeln
!(w
, "{0:1$}}}", "", depth
* INDENT
.len())?
;
547 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
548 /// local variables (both user-defined bindings and compiler temporaries).
549 pub fn write_mir_intro
<'tcx
>(
551 src
: MirSource
<'tcx
>,
554 ) -> io
::Result
<()> {
555 write_mir_sig(tcx
, src
, body
, w
)?
;
558 // construct a scope tree and write it out
559 let mut scope_tree
: FxHashMap
<SourceScope
, Vec
<SourceScope
>> = Default
::default();
560 for (index
, scope_data
) in body
.source_scopes
.iter().enumerate() {
561 if let Some(parent
) = scope_data
.parent_scope
{
562 scope_tree
.entry(parent
).or_default().push(SourceScope
::new(index
));
564 // Only the argument scope has no parent, because it's the root.
565 assert_eq
!(index
, OUTERMOST_SOURCE_SCOPE
.index());
569 write_scope_tree(tcx
, body
, &scope_tree
, w
, OUTERMOST_SOURCE_SCOPE
, 1)?
;
571 // Add an empty line before the first block is printed.
577 /// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
579 pub fn write_allocations
<'tcx
>(
583 ) -> io
::Result
<()> {
584 fn alloc_ids_from_alloc(alloc
: &Allocation
) -> impl DoubleEndedIterator
<Item
= AllocId
> + '_
{
585 alloc
.relocations().values().map(|(_
, id
)| *id
)
587 fn alloc_ids_from_const(val
: ConstValue
<'_
>) -> impl Iterator
<Item
= AllocId
> + '_
{
589 ConstValue
::Scalar(interpret
::Scalar
::Ptr(ptr
)) => {
590 Either
::Left(Either
::Left(std
::iter
::once(ptr
.alloc_id
)))
592 ConstValue
::Scalar(interpret
::Scalar
::Raw { .. }
) => {
593 Either
::Left(Either
::Right(std
::iter
::empty()))
595 ConstValue
::ByRef { alloc, .. }
| ConstValue
::Slice { data: alloc, .. }
=> {
596 Either
::Right(alloc_ids_from_alloc(alloc
))
600 struct CollectAllocIds(BTreeSet
<AllocId
>);
601 impl<'tcx
> TypeVisitor
<'tcx
> for CollectAllocIds
{
602 fn visit_const(&mut self, c
: &'tcx ty
::Const
<'tcx
>) -> bool
{
603 if let ty
::ConstKind
::Value(val
) = c
.val
{
604 self.0.extend(alloc_ids_from_const(val
));
606 c
.super_visit_with(self)
609 let mut visitor
= CollectAllocIds(Default
::default());
610 body
.visit_with(&mut visitor
);
611 // `seen` contains all seen allocations, including the ones we have *not* printed yet.
612 // The protocol is to first `insert` into `seen`, and only if that returns `true`
613 // then push to `todo`.
614 let mut seen
= visitor
.0;
615 let mut todo
: Vec
<_
> = seen
.iter().copied().collect();
616 while let Some(id
) = todo
.pop() {
617 let mut write_allocation_track_relocs
=
618 |w
: &mut dyn Write
, alloc
: &Allocation
| -> io
::Result
<()> {
619 // `.rev()` because we are popping them from the back of the `todo` vector.
620 for id
in alloc_ids_from_alloc(alloc
).rev() {
625 write
!(w
, "{}", display_allocation(tcx
, alloc
))
627 write
!(w
, "\n{}", id
)?
;
628 match tcx
.get_global_alloc(id
) {
629 // This can't really happen unless there are bugs, but it doesn't cost us anything to
630 // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
631 None
=> write
!(w
, " (deallocated)")?
,
632 Some(GlobalAlloc
::Function(inst
)) => write
!(w
, " (fn: {})", inst
)?
,
633 Some(GlobalAlloc
::Static(did
)) if !tcx
.is_foreign_item(did
) => {
634 match tcx
.eval_static_initializer(did
) {
636 write
!(w
, " (static: {}, ", tcx
.def_path_str(did
))?
;
637 write_allocation_track_relocs(w
, alloc
)?
;
641 " (static: {}, error during initializer evaluation)",
642 tcx
.def_path_str(did
)
646 Some(GlobalAlloc
::Static(did
)) => {
647 write
!(w
, " (extern static: {})", tcx
.def_path_str(did
))?
649 Some(GlobalAlloc
::Memory(alloc
)) => {
651 write_allocation_track_relocs(w
, alloc
)?
659 /// Dumps the size and metadata and content of an allocation to the given writer.
660 /// The expectation is that the caller first prints other relevant metadata, so the exact
661 /// format of this function is (*without* leading or trailing newline):
663 /// size: {}, align: {}) {
668 /// The byte format is similar to how hex editors print bytes. Each line starts with the address of
669 /// the start of the line, followed by all bytes in hex format (space separated).
670 /// If the allocation is small enough to fit into a single line, no start address is given.
671 /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
672 /// characters or characters whose value is larger than 127) with a `.`
673 /// This also prints relocations adequately.
674 pub fn display_allocation
<Tag
: Copy
+ Debug
, Extra
>(
676 alloc
: &'a Allocation
<Tag
, Extra
>,
677 ) -> RenderAllocation
<'a
, 'tcx
, Tag
, Extra
> {
678 RenderAllocation { tcx, alloc }
682 pub struct RenderAllocation
<'a
, 'tcx
, Tag
, Extra
> {
684 alloc
: &'a Allocation
<Tag
, Extra
>,
687 impl<Tag
: Copy
+ Debug
, Extra
> std
::fmt
::Display
for RenderAllocation
<'a
, 'tcx
, Tag
, Extra
> {
688 fn fmt(&self, w
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
689 let RenderAllocation { tcx, alloc }
= *self;
690 write
!(w
, "size: {}, align: {})", alloc
.size
.bytes(), alloc
.align
.bytes())?
;
691 if alloc
.size
== Size
::ZERO
{
693 return write
!(w
, " {{}}");
695 // Write allocation bytes.
697 write_allocation_bytes(tcx
, alloc
, w
, " ")?
;
703 fn write_allocation_endline(w
: &mut dyn std
::fmt
::Write
, ascii
: &str) -> std
::fmt
::Result
{
704 for _
in 0..(BYTES_PER_LINE
- ascii
.chars().count()) {
707 writeln
!(w
, " │ {}", ascii
)
710 /// Number of bytes to print per allocation hex dump line.
711 const BYTES_PER_LINE
: usize = 16;
713 /// Prints the line start address and returns the new line start address.
714 fn write_allocation_newline(
715 w
: &mut dyn std
::fmt
::Write
,
716 mut line_start
: Size
,
720 ) -> Result
<Size
, std
::fmt
::Error
> {
721 write_allocation_endline(w
, ascii
)?
;
722 line_start
+= Size
::from_bytes(BYTES_PER_LINE
);
723 write
!(w
, "{}0x{:02$x} │ ", prefix
, line_start
.bytes(), pos_width
)?
;
727 /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
728 /// is only one line). Note that your prefix should contain a trailing space as the lines are
729 /// printed directly after it.
730 fn write_allocation_bytes
<Tag
: Copy
+ Debug
, Extra
>(
732 alloc
: &Allocation
<Tag
, Extra
>,
733 w
: &mut dyn std
::fmt
::Write
,
735 ) -> std
::fmt
::Result
{
736 let num_lines
= alloc
.size
.bytes_usize().saturating_sub(BYTES_PER_LINE
);
737 // Number of chars needed to represent all line numbers.
738 let pos_width
= format
!("{:x}", alloc
.size
.bytes()).len();
741 write
!(w
, "{}0x{:02$x} │ ", prefix
, 0, pos_width
)?
;
743 write
!(w
, "{}", prefix
)?
;
746 let mut i
= Size
::ZERO
;
747 let mut line_start
= Size
::ZERO
;
749 let ptr_size
= tcx
.data_layout
.pointer_size
;
751 let mut ascii
= String
::new();
753 let oversized_ptr
= |target
: &mut String
, width
| {
754 if target
.len() > width
{
755 write
!(target
, " ({} ptr bytes)", ptr_size
.bytes()).unwrap();
759 while i
< alloc
.size
{
760 // The line start already has a space. While we could remove that space from the line start
761 // printing and unconditionally print a space here, that would cause the single-line case
762 // to have a single space before it, which looks weird.
766 if let Some(&(tag
, target_id
)) = alloc
.relocations().get(&i
) {
767 // Memory with a relocation must be defined
768 let j
= i
.bytes_usize();
770 .inspect_with_uninit_and_ptr_outside_interpreter(j
..j
+ ptr_size
.bytes_usize());
771 let offset
= read_target_uint(tcx
.data_layout
.endian
, offset
).unwrap();
772 let offset
= Size
::from_bytes(offset
);
773 let relocation_width
= |bytes
| bytes
* 3;
774 let ptr
= Pointer
::new_with_tag(target_id
, offset
, tag
);
775 let mut target
= format
!("{:?}", ptr
);
776 if target
.len() > relocation_width(ptr_size
.bytes_usize() - 1) {
777 // This is too long, try to save some space.
778 target
= format
!("{:#?}", ptr
);
780 if ((i
- line_start
) + ptr_size
).bytes_usize() > BYTES_PER_LINE
{
781 // This branch handles the situation where a relocation starts in the current line
782 // but ends in the next one.
783 let remainder
= Size
::from_bytes(BYTES_PER_LINE
) - (i
- line_start
);
784 let overflow
= ptr_size
- remainder
;
785 let remainder_width
= relocation_width(remainder
.bytes_usize()) - 2;
786 let overflow_width
= relocation_width(overflow
.bytes_usize() - 1) + 1;
788 for _
in 0..remainder
.bytes() - 1 {
791 if overflow_width
> remainder_width
&& overflow_width
>= target
.len() {
792 // The case where the relocation fits into the part in the next line
793 write
!(w
, "╾{0:─^1$}", "", remainder_width
)?
;
795 write_allocation_newline(w
, line_start
, &ascii
, pos_width
, prefix
)?
;
797 write
!(w
, "{0:─^1$}╼", target
, overflow_width
)?
;
799 oversized_ptr(&mut target
, remainder_width
);
800 write
!(w
, "╾{0:─^1$}", target
, remainder_width
)?
;
802 write_allocation_newline(w
, line_start
, &ascii
, pos_width
, prefix
)?
;
803 write
!(w
, "{0:─^1$}╼", "", overflow_width
)?
;
806 for _
in 0..overflow
.bytes() - 1 {
813 // This branch handles a relocation that starts and ends in the current line.
814 let relocation_width
= relocation_width(ptr_size
.bytes_usize() - 1);
815 oversized_ptr(&mut target
, relocation_width
);
817 write
!(w
, "╾{0:─^1$}╼", target
, relocation_width
)?
;
818 for _
in 0..ptr_size
.bytes() - 2 {
824 } else if alloc
.init_mask().is_range_initialized(i
, i
+ Size
::from_bytes(1)).is_ok() {
825 let j
= i
.bytes_usize();
827 // Checked definedness (and thus range) and relocations. This access also doesn't
828 // influence interpreter execution but is only for debugging.
829 let c
= alloc
.inspect_with_uninit_and_ptr_outside_interpreter(j
..j
+ 1)[0];
830 write
!(w
, "{:02x}", c
)?
;
831 if c
.is_ascii_control() || c
>= 0x80 {
834 ascii
.push(char::from(c
));
836 i
+= Size
::from_bytes(1);
840 i
+= Size
::from_bytes(1);
842 // Print a new line header if the next line still has some bytes to print.
843 if i
== line_start
+ Size
::from_bytes(BYTES_PER_LINE
) && i
!= alloc
.size
{
844 line_start
= write_allocation_newline(w
, line_start
, &ascii
, pos_width
, prefix
)?
;
848 write_allocation_endline(w
, &ascii
)?
;
855 src
: MirSource
<'tcx
>,
858 ) -> io
::Result
<()> {
859 use rustc_hir
::def
::DefKind
;
861 trace
!("write_mir_sig: {:?}", src
.instance
);
862 let kind
= tcx
.def_kind(src
.def_id());
863 let is_function
= match kind
{
864 DefKind
::Fn
| DefKind
::AssocFn
| DefKind
::Ctor(..) => true,
865 _
=> tcx
.is_closure(src
.def_id()),
867 match (kind
, src
.promoted
) {
868 (_
, Some(i
)) => write
!(w
, "{:?} in ", i
)?
,
869 (DefKind
::Const
| DefKind
::AssocConst
, _
) => write
!(w
, "const ")?
,
870 (DefKind
::Static
, _
) => {
871 write
!(w
, "static {}", if tcx
.is_mutable_static(src
.def_id()) { "mut " }
else { "" }
)?
873 (_
, _
) if is_function
=> write
!(w
, "fn ")?
,
874 (DefKind
::AnonConst
, _
) => {}
// things like anon const, not an item
875 _
=> bug
!("Unexpected def kind {:?}", kind
),
878 ty
::print
::with_forced_impl_filename_line(|| {
879 // see notes on #41697 elsewhere
880 write
!(w
, "{}", tcx
.def_path_str(src
.def_id()))
883 if src
.promoted
.is_none() && is_function
{
886 // fn argument types.
887 for (i
, arg
) in body
.args_iter().enumerate() {
891 write
!(w
, "{:?}: {}", Place
::from(arg
), body
.local_decls
[arg
].ty
)?
;
894 write
!(w
, ") -> {}", body
.return_ty())?
;
896 assert_eq
!(body
.arg_count
, 0);
897 write
!(w
, ": {} =", body
.return_ty())?
;
900 if let Some(yield_ty
) = body
.yield_ty
{
902 writeln
!(w
, "yields {}", yield_ty
)?
;
906 // Next thing that gets printed is the opening {
911 fn write_user_type_annotations(
915 ) -> io
::Result
<()> {
916 if !body
.user_type_annotations
.is_empty() {
917 writeln
!(w
, "| User Type Annotations")?
;
919 for (index
, annotation
) in body
.user_type_annotations
.iter_enumerated() {
922 "| {:?}: {:?} at {}",
925 tcx
.sess
.source_map().span_to_string(annotation
.span
)
928 if !body
.user_type_annotations
.is_empty() {
934 pub fn dump_mir_def_ids(tcx
: TyCtxt
<'_
>, single
: Option
<DefId
>) -> Vec
<DefId
> {
935 if let Some(i
) = single
{
938 tcx
.mir_keys(LOCAL_CRATE
).iter().map(|def_id
| def_id
.to_def_id()).collect()