]>
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 | |
04454e1e FG |
451 | let fmt_val = |val: &ConstValue<'tcx>| match val { |
452 | ConstValue::Scalar(s) => format!("Scalar({:?})", s), | |
453 | ConstValue::Slice { .. } => format!("Slice(..)"), | |
454 | ConstValue::ByRef { .. } => format!("ByRef(..)"), | |
455 | }; | |
456 | ||
923072b8 FG |
457 | let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree { |
458 | ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({:?})", leaf), | |
459 | ty::ValTree::Branch(_) => format!("ValTree::Branch(..)"), | |
460 | }; | |
461 | ||
5e7ed085 | 462 | let val = match literal { |
923072b8 | 463 | ConstantKind::Ty(ct) => match ct.kind() { |
5e7ed085 FG |
464 | ty::ConstKind::Param(p) => format!("Param({})", p), |
465 | ty::ConstKind::Unevaluated(uv) => format!( | |
466 | "Unevaluated({}, {:?}, {:?})", | |
467 | self.tcx.def_path_str(uv.def.did), | |
468 | uv.substs, | |
469 | uv.promoted, | |
470 | ), | |
923072b8 | 471 | ty::ConstKind::Value(val) => format!("Value({})", fmt_valtree(&val)), |
5e7ed085 FG |
472 | ty::ConstKind::Error(_) => "Error".to_string(), |
473 | // These variants shouldn't exist in the MIR. | |
474 | ty::ConstKind::Placeholder(_) | |
475 | | ty::ConstKind::Infer(_) | |
476 | | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal), | |
477 | }, | |
478 | // To keep the diffs small, we render this like we render `ty::Const::Value`. | |
479 | // | |
480 | // This changes once `ty::Const::Value` is represented using valtrees. | |
04454e1e | 481 | ConstantKind::Val(val, _) => format!("Value({})", fmt_val(&val)), |
cdc7bbd5 | 482 | }; |
5e7ed085 | 483 | |
923072b8 FG |
484 | // This reflects what `Const` looked liked before `val` was renamed |
485 | // as `kind`. We print it like this to avoid having to update | |
486 | // expected output in a lot of tests. | |
5e7ed085 | 487 | self.push(&format!("+ literal: Const {{ ty: {}, val: {} }}", literal.ty(), val)); |
3dfed10e | 488 | } |
ff7c6d11 XL |
489 | } |
490 | ||
491 | fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { | |
492 | self.super_rvalue(rvalue, location); | |
ba9703b0 XL |
493 | if let Rvalue::Aggregate(kind, _) = rvalue { |
494 | match **kind { | |
ff7c6d11 | 495 | AggregateKind::Closure(def_id, substs) => { |
8faf50e0 | 496 | self.push("closure"); |
2c00a5a8 XL |
497 | self.push(&format!("+ def_id: {:?}", def_id)); |
498 | self.push(&format!("+ substs: {:#?}", substs)); | |
ff7c6d11 XL |
499 | } |
500 | ||
94b46f34 | 501 | AggregateKind::Generator(def_id, substs, movability) => { |
8faf50e0 | 502 | self.push("generator"); |
2c00a5a8 XL |
503 | self.push(&format!("+ def_id: {:?}", def_id)); |
504 | self.push(&format!("+ substs: {:#?}", substs)); | |
94b46f34 | 505 | self.push(&format!("+ movability: {:?}", movability)); |
ff7c6d11 XL |
506 | } |
507 | ||
b7449926 XL |
508 | AggregateKind::Adt(_, _, _, Some(user_ty), _) => { |
509 | self.push("adt"); | |
510 | self.push(&format!("+ user_ty: {:?}", user_ty)); | |
511 | } | |
512 | ||
ff7c6d11 | 513 | _ => {} |
ba9703b0 | 514 | } |
ff7c6d11 XL |
515 | } |
516 | } | |
517 | } | |
518 | ||
dc9dc135 | 519 | fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { |
17df50a5 | 520 | format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_embeddable_string(span)) |
54a0048b SL |
521 | } |
522 | ||
48663c56 | 523 | /// Prints local variables in a scope tree. |
ff7c6d11 | 524 | fn write_scope_tree( |
dc9dc135 XL |
525 | tcx: TyCtxt<'_>, |
526 | body: &Body<'_>, | |
94b46f34 | 527 | scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>, |
0531ce1d | 528 | w: &mut dyn Write, |
94b46f34 | 529 | parent: SourceScope, |
ff7c6d11 XL |
530 | depth: usize, |
531 | ) -> io::Result<()> { | |
3157f602 | 532 | let indent = depth * INDENT.len(); |
a7813a04 | 533 | |
60c5eb7d XL |
534 | // Local variable debuginfo. |
535 | for var_debug_info in &body.var_debug_info { | |
536 | if var_debug_info.source_info.scope != parent { | |
537 | // Not declared in this scope. | |
538 | continue; | |
539 | } | |
540 | ||
541 | let indented_debug_info = format!( | |
542 | "{0:1$}debug {2} => {3:?};", | |
fc512014 | 543 | INDENT, indent, var_debug_info.name, var_debug_info.value, |
60c5eb7d XL |
544 | ); |
545 | ||
546 | writeln!( | |
547 | w, | |
548 | "{0:1$} // in {2}", | |
549 | indented_debug_info, | |
550 | ALIGN, | |
551 | comment(tcx, var_debug_info.source_info), | |
552 | )?; | |
553 | } | |
554 | ||
f9f354fc | 555 | // Local variable types. |
dc9dc135 | 556 | for (local, local_decl) in body.local_decls.iter_enumerated() { |
dfeec247 | 557 | if (1..body.arg_count + 1).contains(&local.index()) { |
48663c56 XL |
558 | // Skip over argument locals, they're printed in the signature. |
559 | continue; | |
560 | } | |
561 | ||
562 | if local_decl.source_info.scope != parent { | |
563 | // Not declared in this scope. | |
564 | continue; | |
565 | } | |
566 | ||
dfeec247 | 567 | let mut_str = if local_decl.mutability == Mutability::Mut { "mut " } else { "" }; |
48663c56 | 568 | |
dfeec247 XL |
569 | let mut indented_decl = |
570 | format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty); | |
f9f354fc XL |
571 | if let Some(user_ty) = &local_decl.user_ty { |
572 | for user_ty in user_ty.projections() { | |
573 | write!(indented_decl, " as {:?}", user_ty).unwrap(); | |
574 | } | |
48663c56 | 575 | } |
1b1a35ee | 576 | indented_decl.push(';'); |
48663c56 | 577 | |
04454e1e | 578 | let local_name = if local == RETURN_PLACE { " return place" } else { "" }; |
48663c56 XL |
579 | |
580 | writeln!( | |
581 | w, | |
582 | "{0:1$} //{2} in {3}", | |
583 | indented_decl, | |
584 | ALIGN, | |
585 | local_name, | |
586 | comment(tcx, local_decl.source_info), | |
587 | )?; | |
588 | } | |
589 | ||
5e7ed085 FG |
590 | let Some(children) = scope_tree.get(&parent) else { |
591 | return Ok(()); | |
a7813a04 XL |
592 | }; |
593 | ||
3157f602 | 594 | for &child in children { |
29967ef6 XL |
595 | let child_data = &body.source_scopes[child]; |
596 | assert_eq!(child_data.parent_scope, Some(parent)); | |
597 | ||
598 | let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined { | |
599 | ( | |
600 | format!( | |
601 | " (inlined {}{})", | |
602 | if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" }, | |
603 | callee | |
604 | ), | |
605 | Some(callsite_span), | |
606 | ) | |
607 | } else { | |
608 | (String::new(), None) | |
609 | }; | |
610 | ||
611 | let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special); | |
612 | ||
613 | if let Some(span) = span { | |
614 | writeln!( | |
615 | w, | |
616 | "{0:1$} // at {2}", | |
617 | indented_header, | |
618 | ALIGN, | |
17df50a5 | 619 | tcx.sess.source_map().span_to_embeddable_string(span), |
29967ef6 XL |
620 | )?; |
621 | } else { | |
622 | writeln!(w, "{}", indented_header)?; | |
623 | } | |
624 | ||
dc9dc135 | 625 | write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; |
3157f602 | 626 | writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; |
54a0048b | 627 | } |
a7813a04 | 628 | |
54a0048b SL |
629 | Ok(()) |
630 | } | |
631 | ||
9cc50fc6 SL |
632 | /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its |
633 | /// local variables (both user-defined bindings and compiler temporaries). | |
dc9dc135 XL |
634 | pub fn write_mir_intro<'tcx>( |
635 | tcx: TyCtxt<'tcx>, | |
dc9dc135 | 636 | body: &Body<'_>, |
0531ce1d | 637 | w: &mut dyn Write, |
ff7c6d11 | 638 | ) -> io::Result<()> { |
29967ef6 | 639 | write_mir_sig(tcx, body, w)?; |
2c00a5a8 | 640 | writeln!(w, "{{")?; |
3157f602 XL |
641 | |
642 | // construct a scope tree and write it out | |
0bf4aa26 | 643 | let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default(); |
dc9dc135 | 644 | for (index, scope_data) in body.source_scopes.iter().enumerate() { |
3157f602 | 645 | if let Some(parent) = scope_data.parent_scope { |
dfeec247 | 646 | scope_tree.entry(parent).or_default().push(SourceScope::new(index)); |
3157f602 XL |
647 | } else { |
648 | // Only the argument scope has no parent, because it's the root. | |
94b46f34 | 649 | assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index()); |
3157f602 XL |
650 | } |
651 | } | |
652 | ||
dc9dc135 | 653 | write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?; |
3157f602 | 654 | |
c30ab7b3 | 655 | // Add an empty line before the first block is printed. |
ba9703b0 XL |
656 | writeln!(w)?; |
657 | ||
658 | Ok(()) | |
659 | } | |
660 | ||
661 | /// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding | |
662 | /// allocations. | |
663 | pub fn write_allocations<'tcx>( | |
664 | tcx: TyCtxt<'tcx>, | |
665 | body: &Body<'_>, | |
666 | w: &mut dyn Write, | |
667 | ) -> io::Result<()> { | |
5e7ed085 FG |
668 | fn alloc_ids_from_alloc( |
669 | alloc: ConstAllocation<'_>, | |
670 | ) -> impl DoubleEndedIterator<Item = AllocId> + '_ { | |
671 | alloc.inner().relocations().values().map(|id| *id) | |
ba9703b0 | 672 | } |
923072b8 FG |
673 | |
674 | fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ { | |
ba9703b0 | 675 | match val { |
5e7ed085 | 676 | ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => { |
136023e0 | 677 | Either::Left(Either::Left(std::iter::once(ptr.provenance))) |
ba9703b0 | 678 | } |
29967ef6 | 679 | ConstValue::Scalar(interpret::Scalar::Int { .. }) => { |
ba9703b0 XL |
680 | Either::Left(Either::Right(std::iter::empty())) |
681 | } | |
682 | ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => { | |
683 | Either::Right(alloc_ids_from_alloc(alloc)) | |
684 | } | |
685 | } | |
686 | } | |
687 | struct CollectAllocIds(BTreeSet<AllocId>); | |
94222f64 | 688 | |
5099ac24 | 689 | impl<'tcx> Visitor<'tcx> for CollectAllocIds { |
5099ac24 FG |
690 | fn visit_constant(&mut self, c: &Constant<'tcx>, loc: Location) { |
691 | match c.literal { | |
692 | ConstantKind::Ty(c) => self.visit_const(c, loc), | |
693 | ConstantKind::Val(val, _) => { | |
923072b8 | 694 | self.0.extend(alloc_ids_from_const_val(val)); |
5099ac24 FG |
695 | } |
696 | } | |
ba9703b0 XL |
697 | } |
698 | } | |
5099ac24 | 699 | |
ba9703b0 | 700 | let mut visitor = CollectAllocIds(Default::default()); |
5099ac24 FG |
701 | visitor.visit_body(body); |
702 | ||
ba9703b0 XL |
703 | // `seen` contains all seen allocations, including the ones we have *not* printed yet. |
704 | // The protocol is to first `insert` into `seen`, and only if that returns `true` | |
705 | // then push to `todo`. | |
706 | let mut seen = visitor.0; | |
707 | let mut todo: Vec<_> = seen.iter().copied().collect(); | |
708 | while let Some(id) = todo.pop() { | |
709 | let mut write_allocation_track_relocs = | |
5e7ed085 | 710 | |w: &mut dyn Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> { |
ba9703b0 XL |
711 | // `.rev()` because we are popping them from the back of the `todo` vector. |
712 | for id in alloc_ids_from_alloc(alloc).rev() { | |
713 | if seen.insert(id) { | |
714 | todo.push(id); | |
715 | } | |
716 | } | |
5e7ed085 | 717 | write!(w, "{}", display_allocation(tcx, alloc.inner())) |
ba9703b0 XL |
718 | }; |
719 | write!(w, "\n{}", id)?; | |
f9f354fc | 720 | match tcx.get_global_alloc(id) { |
ba9703b0 XL |
721 | // This can't really happen unless there are bugs, but it doesn't cost us anything to |
722 | // gracefully handle it and allow buggy rustc to be debugged via allocation printing. | |
723 | None => write!(w, " (deallocated)")?, | |
724 | Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?, | |
725 | Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { | |
1b1a35ee XL |
726 | match tcx.eval_static_initializer(did) { |
727 | Ok(alloc) => { | |
ba9703b0 XL |
728 | write!(w, " (static: {}, ", tcx.def_path_str(did))?; |
729 | write_allocation_track_relocs(w, alloc)?; | |
730 | } | |
ba9703b0 XL |
731 | Err(_) => write!( |
732 | w, | |
733 | " (static: {}, error during initializer evaluation)", | |
734 | tcx.def_path_str(did) | |
735 | )?, | |
736 | } | |
737 | } | |
738 | Some(GlobalAlloc::Static(did)) => { | |
739 | write!(w, " (extern static: {})", tcx.def_path_str(did))? | |
740 | } | |
741 | Some(GlobalAlloc::Memory(alloc)) => { | |
742 | write!(w, " (")?; | |
743 | write_allocation_track_relocs(w, alloc)? | |
744 | } | |
745 | } | |
746 | writeln!(w)?; | |
747 | } | |
748 | Ok(()) | |
749 | } | |
750 | ||
751 | /// Dumps the size and metadata and content of an allocation to the given writer. | |
752 | /// The expectation is that the caller first prints other relevant metadata, so the exact | |
753 | /// format of this function is (*without* leading or trailing newline): | |
29967ef6 XL |
754 | /// |
755 | /// ```text | |
ba9703b0 XL |
756 | /// size: {}, align: {}) { |
757 | /// <bytes> | |
758 | /// } | |
759 | /// ``` | |
760 | /// | |
761 | /// The byte format is similar to how hex editors print bytes. Each line starts with the address of | |
762 | /// the start of the line, followed by all bytes in hex format (space separated). | |
763 | /// If the allocation is small enough to fit into a single line, no start address is given. | |
764 | /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control | |
765 | /// characters or characters whose value is larger than 127) with a `.` | |
766 | /// This also prints relocations adequately. | |
a2a8927a | 767 | pub fn display_allocation<'a, 'tcx, Tag, Extra>( |
ba9703b0 | 768 | tcx: TyCtxt<'tcx>, |
3dfed10e XL |
769 | alloc: &'a Allocation<Tag, Extra>, |
770 | ) -> RenderAllocation<'a, 'tcx, Tag, Extra> { | |
771 | RenderAllocation { tcx, alloc } | |
772 | } | |
773 | ||
774 | #[doc(hidden)] | |
775 | pub struct RenderAllocation<'a, 'tcx, Tag, Extra> { | |
776 | tcx: TyCtxt<'tcx>, | |
777 | alloc: &'a Allocation<Tag, Extra>, | |
778 | } | |
779 | ||
a2a8927a XL |
780 | impl<'a, 'tcx, Tag: Provenance, Extra> std::fmt::Display |
781 | for RenderAllocation<'a, 'tcx, Tag, Extra> | |
782 | { | |
3dfed10e XL |
783 | fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
784 | let RenderAllocation { tcx, alloc } = *self; | |
17df50a5 XL |
785 | write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?; |
786 | if alloc.size() == Size::ZERO { | |
3dfed10e XL |
787 | // We are done. |
788 | return write!(w, " {{}}"); | |
789 | } | |
790 | // Write allocation bytes. | |
791 | writeln!(w, " {{")?; | |
792 | write_allocation_bytes(tcx, alloc, w, " ")?; | |
793 | write!(w, "}}")?; | |
794 | Ok(()) | |
ba9703b0 | 795 | } |
ba9703b0 XL |
796 | } |
797 | ||
3dfed10e | 798 | fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result { |
ba9703b0 XL |
799 | for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) { |
800 | write!(w, " ")?; | |
801 | } | |
802 | writeln!(w, " │ {}", ascii) | |
803 | } | |
804 | ||
805 | /// Number of bytes to print per allocation hex dump line. | |
806 | const BYTES_PER_LINE: usize = 16; | |
807 | ||
808 | /// Prints the line start address and returns the new line start address. | |
809 | fn write_allocation_newline( | |
3dfed10e | 810 | w: &mut dyn std::fmt::Write, |
ba9703b0 XL |
811 | mut line_start: Size, |
812 | ascii: &str, | |
813 | pos_width: usize, | |
814 | prefix: &str, | |
3dfed10e | 815 | ) -> Result<Size, std::fmt::Error> { |
ba9703b0 XL |
816 | write_allocation_endline(w, ascii)?; |
817 | line_start += Size::from_bytes(BYTES_PER_LINE); | |
818 | write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?; | |
819 | Ok(line_start) | |
820 | } | |
821 | ||
822 | /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there | |
823 | /// is only one line). Note that your prefix should contain a trailing space as the lines are | |
824 | /// printed directly after it. | |
a2a8927a | 825 | fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>( |
ba9703b0 XL |
826 | tcx: TyCtxt<'tcx>, |
827 | alloc: &Allocation<Tag, Extra>, | |
3dfed10e | 828 | w: &mut dyn std::fmt::Write, |
ba9703b0 | 829 | prefix: &str, |
3dfed10e | 830 | ) -> std::fmt::Result { |
17df50a5 | 831 | let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE); |
ba9703b0 | 832 | // Number of chars needed to represent all line numbers. |
136023e0 | 833 | let pos_width = hex_number_length(alloc.size().bytes()); |
ba9703b0 XL |
834 | |
835 | if num_lines > 0 { | |
836 | write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?; | |
837 | } else { | |
838 | write!(w, "{}", prefix)?; | |
839 | } | |
840 | ||
841 | let mut i = Size::ZERO; | |
842 | let mut line_start = Size::ZERO; | |
843 | ||
844 | let ptr_size = tcx.data_layout.pointer_size; | |
845 | ||
846 | let mut ascii = String::new(); | |
847 | ||
848 | let oversized_ptr = |target: &mut String, width| { | |
849 | if target.len() > width { | |
850 | write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap(); | |
851 | } | |
852 | }; | |
853 | ||
17df50a5 | 854 | while i < alloc.size() { |
ba9703b0 XL |
855 | // The line start already has a space. While we could remove that space from the line start |
856 | // printing and unconditionally print a space here, that would cause the single-line case | |
857 | // to have a single space before it, which looks weird. | |
858 | if i != line_start { | |
859 | write!(w, " ")?; | |
860 | } | |
136023e0 | 861 | if let Some(&tag) = alloc.relocations().get(&i) { |
ba9703b0 | 862 | // Memory with a relocation must be defined |
04454e1e | 863 | assert!(alloc.init_mask().is_range_initialized(i, i + ptr_size).is_ok()); |
ba9703b0 | 864 | let j = i.bytes_usize(); |
3dfed10e XL |
865 | let offset = alloc |
866 | .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize()); | |
ba9703b0 | 867 | let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap(); |
f9f354fc | 868 | let offset = Size::from_bytes(offset); |
ba9703b0 | 869 | let relocation_width = |bytes| bytes * 3; |
136023e0 | 870 | let ptr = Pointer::new(tag, offset); |
f9f354fc XL |
871 | let mut target = format!("{:?}", ptr); |
872 | if target.len() > relocation_width(ptr_size.bytes_usize() - 1) { | |
873 | // This is too long, try to save some space. | |
874 | target = format!("{:#?}", ptr); | |
875 | } | |
ba9703b0 XL |
876 | if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE { |
877 | // This branch handles the situation where a relocation starts in the current line | |
878 | // but ends in the next one. | |
879 | let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start); | |
880 | let overflow = ptr_size - remainder; | |
881 | let remainder_width = relocation_width(remainder.bytes_usize()) - 2; | |
882 | let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1; | |
883 | ascii.push('╾'); | |
884 | for _ in 0..remainder.bytes() - 1 { | |
885 | ascii.push('─'); | |
886 | } | |
887 | if overflow_width > remainder_width && overflow_width >= target.len() { | |
888 | // The case where the relocation fits into the part in the next line | |
889 | write!(w, "╾{0:─^1$}", "", remainder_width)?; | |
890 | line_start = | |
891 | write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; | |
892 | ascii.clear(); | |
893 | write!(w, "{0:─^1$}╼", target, overflow_width)?; | |
894 | } else { | |
895 | oversized_ptr(&mut target, remainder_width); | |
896 | write!(w, "╾{0:─^1$}", target, remainder_width)?; | |
897 | line_start = | |
898 | write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; | |
899 | write!(w, "{0:─^1$}╼", "", overflow_width)?; | |
900 | ascii.clear(); | |
901 | } | |
902 | for _ in 0..overflow.bytes() - 1 { | |
903 | ascii.push('─'); | |
904 | } | |
905 | ascii.push('╼'); | |
906 | i += ptr_size; | |
907 | continue; | |
908 | } else { | |
909 | // This branch handles a relocation that starts and ends in the current line. | |
910 | let relocation_width = relocation_width(ptr_size.bytes_usize() - 1); | |
911 | oversized_ptr(&mut target, relocation_width); | |
912 | ascii.push('╾'); | |
913 | write!(w, "╾{0:─^1$}╼", target, relocation_width)?; | |
914 | for _ in 0..ptr_size.bytes() - 2 { | |
915 | ascii.push('─'); | |
916 | } | |
917 | ascii.push('╼'); | |
918 | i += ptr_size; | |
919 | } | |
f9f354fc | 920 | } else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() { |
ba9703b0 XL |
921 | let j = i.bytes_usize(); |
922 | ||
923 | // Checked definedness (and thus range) and relocations. This access also doesn't | |
924 | // influence interpreter execution but is only for debugging. | |
3dfed10e | 925 | let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; |
ba9703b0 XL |
926 | write!(w, "{:02x}", c)?; |
927 | if c.is_ascii_control() || c >= 0x80 { | |
928 | ascii.push('.'); | |
929 | } else { | |
930 | ascii.push(char::from(c)); | |
931 | } | |
932 | i += Size::from_bytes(1); | |
933 | } else { | |
934 | write!(w, "__")?; | |
935 | ascii.push('░'); | |
936 | i += Size::from_bytes(1); | |
937 | } | |
938 | // Print a new line header if the next line still has some bytes to print. | |
17df50a5 | 939 | if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() { |
ba9703b0 XL |
940 | line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; |
941 | ascii.clear(); | |
942 | } | |
943 | } | |
944 | write_allocation_endline(w, &ascii)?; | |
c30ab7b3 SL |
945 | |
946 | Ok(()) | |
3157f602 XL |
947 | } |
948 | ||
29967ef6 | 949 | fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> { |
dfeec247 | 950 | use rustc_hir::def::DefKind; |
9fa01778 | 951 | |
29967ef6 XL |
952 | trace!("write_mir_sig: {:?}", body.source.instance); |
953 | let def_id = body.source.def_id(); | |
954 | let kind = tcx.def_kind(def_id); | |
48663c56 | 955 | let is_function = match kind { |
f9f354fc | 956 | DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, |
29967ef6 | 957 | _ => tcx.is_closure(def_id), |
9fa01778 | 958 | }; |
29967ef6 | 959 | match (kind, body.source.promoted) { |
9fa01778 | 960 | (_, Some(i)) => write!(w, "{:?} in ", i)?, |
f9f354fc | 961 | (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, |
5e7ed085 FG |
962 | (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?, |
963 | (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?, | |
9fa01778 | 964 | (_, _) if is_function => write!(w, "fn ")?, |
3c0e092e | 965 | (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item |
48663c56 | 966 | _ => bug!("Unexpected def kind {:?}", kind), |
9cc50fc6 SL |
967 | } |
968 | ||
5e7ed085 | 969 | ty::print::with_forced_impl_filename_line! { |
ff7c6d11 | 970 | // see notes on #41697 elsewhere |
5e7ed085 FG |
971 | write!(w, "{}", tcx.def_path_str(def_id))? |
972 | } | |
a7813a04 | 973 | |
29967ef6 | 974 | if body.source.promoted.is_none() && is_function { |
9fa01778 | 975 | write!(w, "(")?; |
ea8adc8c | 976 | |
9fa01778 | 977 | // fn argument types. |
dc9dc135 | 978 | for (i, arg) in body.args_iter().enumerate() { |
9fa01778 XL |
979 | if i != 0 { |
980 | write!(w, ", ")?; | |
a7813a04 | 981 | } |
dc9dc135 | 982 | write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?; |
ea8adc8c | 983 | } |
9fa01778 | 984 | |
dc9dc135 | 985 | write!(w, ") -> {}", body.return_ty())?; |
9fa01778 | 986 | } else { |
dc9dc135 XL |
987 | assert_eq!(body.arg_count, 0); |
988 | write!(w, ": {} =", body.return_ty())?; | |
9cc50fc6 | 989 | } |
2c00a5a8 | 990 | |
6a06907d | 991 | if let Some(yield_ty) = body.yield_ty() { |
2c00a5a8 XL |
992 | writeln!(w)?; |
993 | writeln!(w, "yields {}", yield_ty)?; | |
994 | } | |
995 | ||
9fa01778 XL |
996 | write!(w, " ")?; |
997 | // Next thing that gets printed is the opening { | |
998 | ||
2c00a5a8 | 999 | Ok(()) |
3157f602 | 1000 | } |
9cc50fc6 | 1001 | |
f035d41b XL |
1002 | fn write_user_type_annotations( |
1003 | tcx: TyCtxt<'_>, | |
1004 | body: &Body<'_>, | |
1005 | w: &mut dyn Write, | |
1006 | ) -> io::Result<()> { | |
dc9dc135 | 1007 | if !body.user_type_annotations.is_empty() { |
0731742a XL |
1008 | writeln!(w, "| User Type Annotations")?; |
1009 | } | |
dc9dc135 | 1010 | for (index, annotation) in body.user_type_annotations.iter_enumerated() { |
f035d41b XL |
1011 | writeln!( |
1012 | w, | |
923072b8 | 1013 | "| {:?}: user_ty: {:?}, span: {}, inferred_ty: {:?}", |
f035d41b XL |
1014 | index.index(), |
1015 | annotation.user_ty, | |
923072b8 FG |
1016 | tcx.sess.source_map().span_to_embeddable_string(annotation.span), |
1017 | annotation.inferred_ty, | |
f035d41b | 1018 | )?; |
0731742a | 1019 | } |
dc9dc135 | 1020 | if !body.user_type_annotations.is_empty() { |
0731742a XL |
1021 | writeln!(w, "|")?; |
1022 | } | |
1023 | Ok(()) | |
1024 | } | |
1025 | ||
dc9dc135 | 1026 | pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> { |
f9f354fc XL |
1027 | if let Some(i) = single { |
1028 | vec![i] | |
1029 | } else { | |
17df50a5 | 1030 | tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect() |
f9f354fc | 1031 | } |
7cac9316 | 1032 | } |
136023e0 XL |
1033 | |
1034 | /// Calc converted u64 decimal into hex and return it's length in chars | |
1035 | /// | |
1036 | /// ```ignore (cannot-test-private-function) | |
1037 | /// assert_eq!(1, hex_number_length(0)); | |
1038 | /// assert_eq!(1, hex_number_length(1)); | |
1039 | /// assert_eq!(2, hex_number_length(16)); | |
1040 | /// ``` | |
1041 | fn hex_number_length(x: u64) -> usize { | |
1042 | if x == 0 { | |
1043 | return 1; | |
1044 | } | |
1045 | let mut length = 0; | |
1046 | let mut x_left = x; | |
1047 | while x_left > 0 { | |
1048 | x_left /= 16; | |
1049 | length += 1; | |
1050 | } | |
1051 | length | |
1052 | } |