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