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