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