]>
Commit | Line | Data |
---|---|---|
f9f354fc XL |
1 | use std::collections::BTreeSet; |
2 | use std::fmt::Write as _; | |
3 | use std::fmt::{Debug, Display}; | |
4 | use std::fs; | |
5 | use std::io::{self, Write}; | |
6 | use std::path::{Path, PathBuf}; | |
7 | ||
dfeec247 | 8 | use super::graphviz::write_mir_fn_graphviz; |
1b1a35ee | 9 | use super::spanview::write_mir_fn_spanview; |
dfeec247 | 10 | use crate::transform::MirSource; |
ba9703b0 | 11 | use either::Either; |
476ff2be | 12 | use rustc_data_structures::fx::FxHashMap; |
17df50a5 | 13 | use rustc_hir::def_id::DefId; |
e74abb32 | 14 | use rustc_index::vec::Idx; |
ba9703b0 | 15 | use rustc_middle::mir::interpret::{ |
f9f354fc | 16 | read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, |
ba9703b0 XL |
17 | }; |
18 | use rustc_middle::mir::visit::Visitor; | |
19 | use rustc_middle::mir::*; | |
fc512014 | 20 | use rustc_middle::ty::{self, TyCtxt, TyS, TypeFoldable, TypeVisitor}; |
ba9703b0 | 21 | use rustc_target::abi::Size; |
29967ef6 | 22 | use std::ops::ControlFlow; |
9cc50fc6 | 23 | |
0731742a | 24 | const INDENT: &str = " "; |
a7813a04 | 25 | /// Alignment for lining up comments following MIR statements |
ff7c6d11 | 26 | pub(crate) const ALIGN: usize = 40; |
9cc50fc6 | 27 | |
abe05a73 XL |
28 | /// An indication of where we are in the control flow graph. Used for printing |
29 | /// extra information in `dump_mir` | |
30 | pub enum PassWhere { | |
31 | /// We have not started dumping the control flow graph, but we are about to. | |
32 | BeforeCFG, | |
33 | ||
34 | /// We just finished dumping the control flow graph. This is right before EOF | |
35 | AfterCFG, | |
36 | ||
37 | /// We are about to start dumping the given basic block. | |
38 | BeforeBlock(BasicBlock), | |
39 | ||
ff7c6d11 XL |
40 | /// We are just about to dump the given statement or terminator. |
41 | BeforeLocation(Location), | |
42 | ||
43 | /// We just dumped the given statement or terminator. | |
44 | AfterLocation(Location), | |
8faf50e0 XL |
45 | |
46 | /// We just dumped the terminator for a block but not the closing `}`. | |
47 | AfterTerminator(BasicBlock), | |
abe05a73 XL |
48 | } |
49 | ||
54a0048b SL |
50 | /// If the session is properly configured, dumps a human-readable |
51 | /// representation of the mir into: | |
52 | /// | |
a7813a04 | 53 | /// ```text |
7cac9316 | 54 | /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator> |
54a0048b SL |
55 | /// ``` |
56 | /// | |
57 | /// Output from this function is controlled by passing `-Z dump-mir=<filter>`, | |
58 | /// where `<filter>` takes the following forms: | |
59 | /// | |
60 | /// - `all` -- dump MIR for all fns, all passes, all everything | |
ff7c6d11 XL |
61 | /// - a filter defined by a set of substrings combined with `&` and `|` |
62 | /// (`&` has higher precedence). At least one of the `|`-separated groups | |
63 | /// must match; an `|`-separated group matches if all of its `&`-separated | |
64 | /// substrings are matched. | |
65 | /// | |
66 | /// Example: | |
67 | /// | |
68 | /// - `nll` == match if `nll` appears in the name | |
69 | /// - `foo & nll` == match if `foo` and `nll` both appear in the name | |
70 | /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name | |
71 | /// or `typeck` appears in the name. | |
72 | /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name | |
73 | /// or `typeck` and `bar` both appear in the name. | |
dc9dc135 XL |
74 | pub fn dump_mir<'tcx, F>( |
75 | tcx: TyCtxt<'tcx>, | |
0531ce1d | 76 | pass_num: Option<&dyn Display>, |
ff7c6d11 | 77 | pass_name: &str, |
0531ce1d | 78 | disambiguator: &dyn Display, |
dc9dc135 | 79 | body: &Body<'tcx>, |
ff7c6d11 XL |
80 | extra_data: F, |
81 | ) where | |
0531ce1d | 82 | F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, |
abe05a73 | 83 | { |
29967ef6 | 84 | if !dump_enabled(tcx, pass_name, body.source.def_id()) { |
7cac9316 XL |
85 | return; |
86 | } | |
87 | ||
29967ef6 | 88 | dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data); |
7cac9316 XL |
89 | } |
90 | ||
ba9703b0 | 91 | pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool { |
54a0048b | 92 | let filters = match tcx.sess.opts.debugging_opts.dump_mir { |
7cac9316 | 93 | None => return false, |
54a0048b SL |
94 | Some(ref filters) => filters, |
95 | }; | |
532ac7d7 | 96 | let node_path = ty::print::with_forced_impl_filename_line(|| { |
ff7c6d11 | 97 | // see notes on #41697 below |
ba9703b0 | 98 | tcx.def_path_str(def_id) |
7cac9316 | 99 | }); |
8faf50e0 XL |
100 | filters.split('|').any(|or_filter| { |
101 | or_filter.split('&').all(|and_filter| { | |
ff7c6d11 XL |
102 | and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter) |
103 | }) | |
104 | }) | |
7cac9316 | 105 | } |
54a0048b | 106 | |
7cac9316 | 107 | // #41697 -- we use `with_forced_impl_filename_line()` because |
532ac7d7 | 108 | // `def_path_str()` would otherwise trigger `type_of`, and this can |
7cac9316 XL |
109 | // run while we are already attempting to evaluate `type_of`. |
110 | ||
dc9dc135 XL |
111 | fn dump_matched_mir_node<'tcx, F>( |
112 | tcx: TyCtxt<'tcx>, | |
0531ce1d | 113 | pass_num: Option<&dyn Display>, |
ff7c6d11 | 114 | pass_name: &str, |
0531ce1d | 115 | disambiguator: &dyn Display, |
dc9dc135 | 116 | body: &Body<'tcx>, |
ff7c6d11 XL |
117 | mut extra_data: F, |
118 | ) where | |
0531ce1d | 119 | F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, |
abe05a73 | 120 | { |
9fa01778 | 121 | let _: io::Result<()> = try { |
29967ef6 XL |
122 | let mut file = |
123 | create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body.source)?; | |
ba9703b0 XL |
124 | let def_path = ty::print::with_forced_impl_filename_line(|| { |
125 | // see notes on #41697 above | |
29967ef6 | 126 | tcx.def_path_str(body.source.def_id()) |
ba9703b0 XL |
127 | }); |
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. | |
166 | fn dump_file_basename( | |
dc9dc135 | 167 | tcx: TyCtxt<'_>, |
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. | |
250 | pub(crate) fn create_dump_file( | |
dc9dc135 | 251 | tcx: TyCtxt<'_>, |
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 | ||
dc9dc135 | 418 | impl 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 | ||
fc512014 XL |
426 | fn use_verbose(ty: &&TyS<'tcx>) -> bool { |
427 | match ty.kind() { | |
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, | |
431 | ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty())), | |
432 | ty::Array(ty, _) => use_verbose(ty), | |
433 | ty::FnDef(..) => false, | |
434 | _ => true, | |
435 | } | |
436 | } | |
437 | ||
dc9dc135 | 438 | impl Visitor<'tcx> for ExtraComments<'tcx> { |
ff7c6d11 XL |
439 | fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { |
440 | self.super_constant(constant, location); | |
e1599b0c | 441 | let Constant { span, user_ty, literal } = constant; |
6a06907d | 442 | match literal.ty().kind() { |
3dfed10e XL |
443 | ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {} |
444 | // Unit type | |
445 | ty::Tuple(tys) if tys.is_empty() => {} | |
446 | _ => { | |
447 | self.push("mir::Constant"); | |
17df50a5 XL |
448 | self.push(&format!( |
449 | "+ span: {}", | |
450 | self.tcx.sess.source_map().span_to_embeddable_string(*span) | |
451 | )); | |
3dfed10e XL |
452 | if let Some(user_ty) = user_ty { |
453 | self.push(&format!("+ user_ty: {:?}", user_ty)); | |
454 | } | |
6a06907d XL |
455 | match literal { |
456 | ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)), | |
457 | ConstantKind::Val(val, ty) => { | |
cdc7bbd5 XL |
458 | // To keep the diffs small, we render this almost like we render ty::Const |
459 | self.push(&format!( | |
460 | "+ literal: Const {{ ty: {}, val: Value({:?}) }}", | |
461 | ty, val | |
462 | )) | |
6a06907d XL |
463 | } |
464 | } | |
3dfed10e | 465 | } |
b7449926 | 466 | } |
ff7c6d11 XL |
467 | } |
468 | ||
532ac7d7 | 469 | fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { |
ff7c6d11 | 470 | self.super_const(constant); |
532ac7d7 | 471 | let ty::Const { ty, val, .. } = constant; |
fc512014 XL |
472 | if use_verbose(ty) { |
473 | self.push("ty::Const"); | |
474 | self.push(&format!("+ ty: {:?}", ty)); | |
cdc7bbd5 XL |
475 | let val = match val { |
476 | ty::ConstKind::Param(p) => format!("Param({})", p), | |
477 | ty::ConstKind::Infer(infer) => format!("Infer({:?})", infer), | |
478 | ty::ConstKind::Bound(idx, var) => format!("Bound({:?}, {:?})", idx, var), | |
479 | ty::ConstKind::Placeholder(ph) => format!("PlaceHolder({:?})", ph), | |
480 | ty::ConstKind::Unevaluated(uv) => format!( | |
481 | "Unevaluated({}, {:?}, {:?})", | |
482 | self.tcx.def_path_str(uv.def.did), | |
483 | uv.substs, | |
484 | uv.promoted | |
485 | ), | |
486 | ty::ConstKind::Value(val) => format!("Value({:?})", val), | |
487 | ty::ConstKind::Error(_) => format!("Error"), | |
488 | }; | |
489 | self.push(&format!("+ val: {}", val)); | |
3dfed10e | 490 | } |
ff7c6d11 XL |
491 | } |
492 | ||
493 | fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { | |
494 | self.super_rvalue(rvalue, location); | |
ba9703b0 XL |
495 | if let Rvalue::Aggregate(kind, _) = rvalue { |
496 | match **kind { | |
ff7c6d11 | 497 | AggregateKind::Closure(def_id, substs) => { |
8faf50e0 | 498 | self.push("closure"); |
2c00a5a8 XL |
499 | self.push(&format!("+ def_id: {:?}", def_id)); |
500 | self.push(&format!("+ substs: {:#?}", substs)); | |
ff7c6d11 XL |
501 | } |
502 | ||
94b46f34 | 503 | AggregateKind::Generator(def_id, substs, movability) => { |
8faf50e0 | 504 | self.push("generator"); |
2c00a5a8 XL |
505 | self.push(&format!("+ def_id: {:?}", def_id)); |
506 | self.push(&format!("+ substs: {:#?}", substs)); | |
94b46f34 | 507 | self.push(&format!("+ movability: {:?}", movability)); |
ff7c6d11 XL |
508 | } |
509 | ||
b7449926 XL |
510 | AggregateKind::Adt(_, _, _, Some(user_ty), _) => { |
511 | self.push("adt"); | |
512 | self.push(&format!("+ user_ty: {:?}", user_ty)); | |
513 | } | |
514 | ||
ff7c6d11 | 515 | _ => {} |
ba9703b0 | 516 | } |
ff7c6d11 XL |
517 | } |
518 | } | |
519 | } | |
520 | ||
dc9dc135 | 521 | fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { |
17df50a5 | 522 | format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_embeddable_string(span)) |
54a0048b SL |
523 | } |
524 | ||
48663c56 | 525 | /// Prints local variables in a scope tree. |
ff7c6d11 | 526 | fn write_scope_tree( |
dc9dc135 XL |
527 | tcx: TyCtxt<'_>, |
528 | body: &Body<'_>, | |
94b46f34 | 529 | scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>, |
0531ce1d | 530 | w: &mut dyn Write, |
94b46f34 | 531 | parent: SourceScope, |
ff7c6d11 XL |
532 | depth: usize, |
533 | ) -> io::Result<()> { | |
3157f602 | 534 | let indent = depth * INDENT.len(); |
a7813a04 | 535 | |
60c5eb7d XL |
536 | // Local variable debuginfo. |
537 | for var_debug_info in &body.var_debug_info { | |
538 | if var_debug_info.source_info.scope != parent { | |
539 | // Not declared in this scope. | |
540 | continue; | |
541 | } | |
542 | ||
543 | let indented_debug_info = format!( | |
544 | "{0:1$}debug {2} => {3:?};", | |
fc512014 | 545 | INDENT, indent, var_debug_info.name, var_debug_info.value, |
60c5eb7d XL |
546 | ); |
547 | ||
548 | writeln!( | |
549 | w, | |
550 | "{0:1$} // in {2}", | |
551 | indented_debug_info, | |
552 | ALIGN, | |
553 | comment(tcx, var_debug_info.source_info), | |
554 | )?; | |
555 | } | |
556 | ||
f9f354fc | 557 | // Local variable types. |
dc9dc135 | 558 | for (local, local_decl) in body.local_decls.iter_enumerated() { |
dfeec247 | 559 | if (1..body.arg_count + 1).contains(&local.index()) { |
48663c56 XL |
560 | // Skip over argument locals, they're printed in the signature. |
561 | continue; | |
562 | } | |
563 | ||
564 | if local_decl.source_info.scope != parent { | |
565 | // Not declared in this scope. | |
566 | continue; | |
567 | } | |
568 | ||
dfeec247 | 569 | let mut_str = if local_decl.mutability == Mutability::Mut { "mut " } else { "" }; |
48663c56 | 570 | |
dfeec247 XL |
571 | let mut indented_decl = |
572 | format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty); | |
f9f354fc XL |
573 | if let Some(user_ty) = &local_decl.user_ty { |
574 | for user_ty in user_ty.projections() { | |
575 | write!(indented_decl, " as {:?}", user_ty).unwrap(); | |
576 | } | |
48663c56 | 577 | } |
1b1a35ee | 578 | indented_decl.push(';'); |
48663c56 | 579 | |
dfeec247 | 580 | let local_name = |
74b04a01 | 581 | if local == RETURN_PLACE { " return place".to_string() } else { String::new() }; |
48663c56 XL |
582 | |
583 | writeln!( | |
584 | w, | |
585 | "{0:1$} //{2} in {3}", | |
586 | indented_decl, | |
587 | ALIGN, | |
588 | local_name, | |
589 | comment(tcx, local_decl.source_info), | |
590 | )?; | |
591 | } | |
592 | ||
a7813a04 | 593 | let children = match scope_tree.get(&parent) { |
74b04a01 | 594 | Some(children) => children, |
a7813a04 XL |
595 | None => return Ok(()), |
596 | }; | |
597 | ||
3157f602 | 598 | for &child in children { |
29967ef6 XL |
599 | let child_data = &body.source_scopes[child]; |
600 | assert_eq!(child_data.parent_scope, Some(parent)); | |
601 | ||
602 | let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined { | |
603 | ( | |
604 | format!( | |
605 | " (inlined {}{})", | |
606 | if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" }, | |
607 | callee | |
608 | ), | |
609 | Some(callsite_span), | |
610 | ) | |
611 | } else { | |
612 | (String::new(), None) | |
613 | }; | |
614 | ||
615 | let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special); | |
616 | ||
617 | if let Some(span) = span { | |
618 | writeln!( | |
619 | w, | |
620 | "{0:1$} // at {2}", | |
621 | indented_header, | |
622 | ALIGN, | |
17df50a5 | 623 | tcx.sess.source_map().span_to_embeddable_string(span), |
29967ef6 XL |
624 | )?; |
625 | } else { | |
626 | writeln!(w, "{}", indented_header)?; | |
627 | } | |
628 | ||
dc9dc135 | 629 | write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; |
3157f602 | 630 | writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; |
54a0048b | 631 | } |
a7813a04 | 632 | |
54a0048b SL |
633 | Ok(()) |
634 | } | |
635 | ||
9cc50fc6 SL |
636 | /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its |
637 | /// local variables (both user-defined bindings and compiler temporaries). | |
dc9dc135 XL |
638 | pub fn write_mir_intro<'tcx>( |
639 | tcx: TyCtxt<'tcx>, | |
dc9dc135 | 640 | body: &Body<'_>, |
0531ce1d | 641 | w: &mut dyn Write, |
ff7c6d11 | 642 | ) -> io::Result<()> { |
29967ef6 | 643 | write_mir_sig(tcx, body, w)?; |
2c00a5a8 | 644 | writeln!(w, "{{")?; |
3157f602 XL |
645 | |
646 | // construct a scope tree and write it out | |
0bf4aa26 | 647 | let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default(); |
dc9dc135 | 648 | for (index, scope_data) in body.source_scopes.iter().enumerate() { |
3157f602 | 649 | if let Some(parent) = scope_data.parent_scope { |
dfeec247 | 650 | scope_tree.entry(parent).or_default().push(SourceScope::new(index)); |
3157f602 XL |
651 | } else { |
652 | // Only the argument scope has no parent, because it's the root. | |
94b46f34 | 653 | assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index()); |
3157f602 XL |
654 | } |
655 | } | |
656 | ||
dc9dc135 | 657 | write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?; |
3157f602 | 658 | |
c30ab7b3 | 659 | // Add an empty line before the first block is printed. |
ba9703b0 XL |
660 | writeln!(w)?; |
661 | ||
662 | Ok(()) | |
663 | } | |
664 | ||
665 | /// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding | |
666 | /// allocations. | |
667 | pub fn write_allocations<'tcx>( | |
668 | tcx: TyCtxt<'tcx>, | |
669 | body: &Body<'_>, | |
670 | w: &mut dyn Write, | |
671 | ) -> io::Result<()> { | |
672 | fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator<Item = AllocId> + '_ { | |
673 | alloc.relocations().values().map(|(_, id)| *id) | |
674 | } | |
675 | fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ { | |
676 | match val { | |
677 | ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => { | |
678 | Either::Left(Either::Left(std::iter::once(ptr.alloc_id))) | |
679 | } | |
29967ef6 | 680 | ConstValue::Scalar(interpret::Scalar::Int { .. }) => { |
ba9703b0 XL |
681 | Either::Left(Either::Right(std::iter::empty())) |
682 | } | |
683 | ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => { | |
684 | Either::Right(alloc_ids_from_alloc(alloc)) | |
685 | } | |
686 | } | |
687 | } | |
688 | struct CollectAllocIds(BTreeSet<AllocId>); | |
689 | impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds { | |
fc512014 | 690 | fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { |
ba9703b0 XL |
691 | if let ty::ConstKind::Value(val) = c.val { |
692 | self.0.extend(alloc_ids_from_const(val)); | |
693 | } | |
694 | c.super_visit_with(self) | |
695 | } | |
696 | } | |
697 | let mut visitor = CollectAllocIds(Default::default()); | |
698 | body.visit_with(&mut visitor); | |
699 | // `seen` contains all seen allocations, including the ones we have *not* printed yet. | |
700 | // The protocol is to first `insert` into `seen`, and only if that returns `true` | |
701 | // then push to `todo`. | |
702 | let mut seen = visitor.0; | |
703 | let mut todo: Vec<_> = seen.iter().copied().collect(); | |
704 | while let Some(id) = todo.pop() { | |
705 | let mut write_allocation_track_relocs = | |
706 | |w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> { | |
707 | // `.rev()` because we are popping them from the back of the `todo` vector. | |
708 | for id in alloc_ids_from_alloc(alloc).rev() { | |
709 | if seen.insert(id) { | |
710 | todo.push(id); | |
711 | } | |
712 | } | |
3dfed10e | 713 | write!(w, "{}", display_allocation(tcx, alloc)) |
ba9703b0 XL |
714 | }; |
715 | write!(w, "\n{}", id)?; | |
f9f354fc | 716 | match tcx.get_global_alloc(id) { |
ba9703b0 XL |
717 | // This can't really happen unless there are bugs, but it doesn't cost us anything to |
718 | // gracefully handle it and allow buggy rustc to be debugged via allocation printing. | |
719 | None => write!(w, " (deallocated)")?, | |
720 | Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?, | |
721 | Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { | |
1b1a35ee XL |
722 | match tcx.eval_static_initializer(did) { |
723 | Ok(alloc) => { | |
ba9703b0 XL |
724 | write!(w, " (static: {}, ", tcx.def_path_str(did))?; |
725 | write_allocation_track_relocs(w, alloc)?; | |
726 | } | |
ba9703b0 XL |
727 | Err(_) => write!( |
728 | w, | |
729 | " (static: {}, error during initializer evaluation)", | |
730 | tcx.def_path_str(did) | |
731 | )?, | |
732 | } | |
733 | } | |
734 | Some(GlobalAlloc::Static(did)) => { | |
735 | write!(w, " (extern static: {})", tcx.def_path_str(did))? | |
736 | } | |
737 | Some(GlobalAlloc::Memory(alloc)) => { | |
738 | write!(w, " (")?; | |
739 | write_allocation_track_relocs(w, alloc)? | |
740 | } | |
741 | } | |
742 | writeln!(w)?; | |
743 | } | |
744 | Ok(()) | |
745 | } | |
746 | ||
747 | /// Dumps the size and metadata and content of an allocation to the given writer. | |
748 | /// The expectation is that the caller first prints other relevant metadata, so the exact | |
749 | /// format of this function is (*without* leading or trailing newline): | |
29967ef6 XL |
750 | /// |
751 | /// ```text | |
ba9703b0 XL |
752 | /// size: {}, align: {}) { |
753 | /// <bytes> | |
754 | /// } | |
755 | /// ``` | |
756 | /// | |
757 | /// The byte format is similar to how hex editors print bytes. Each line starts with the address of | |
758 | /// the start of the line, followed by all bytes in hex format (space separated). | |
759 | /// If the allocation is small enough to fit into a single line, no start address is given. | |
760 | /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control | |
761 | /// characters or characters whose value is larger than 127) with a `.` | |
762 | /// This also prints relocations adequately. | |
3dfed10e | 763 | pub fn display_allocation<Tag: Copy + Debug, Extra>( |
ba9703b0 | 764 | tcx: TyCtxt<'tcx>, |
3dfed10e XL |
765 | alloc: &'a Allocation<Tag, Extra>, |
766 | ) -> RenderAllocation<'a, 'tcx, Tag, Extra> { | |
767 | RenderAllocation { tcx, alloc } | |
768 | } | |
769 | ||
770 | #[doc(hidden)] | |
771 | pub struct RenderAllocation<'a, 'tcx, Tag, Extra> { | |
772 | tcx: TyCtxt<'tcx>, | |
773 | alloc: &'a Allocation<Tag, Extra>, | |
774 | } | |
775 | ||
776 | impl<Tag: Copy + Debug, Extra> std::fmt::Display for RenderAllocation<'a, 'tcx, Tag, Extra> { | |
777 | fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
778 | let RenderAllocation { tcx, alloc } = *self; | |
17df50a5 XL |
779 | write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?; |
780 | if alloc.size() == Size::ZERO { | |
3dfed10e XL |
781 | // We are done. |
782 | return write!(w, " {{}}"); | |
783 | } | |
784 | // Write allocation bytes. | |
785 | writeln!(w, " {{")?; | |
786 | write_allocation_bytes(tcx, alloc, w, " ")?; | |
787 | write!(w, "}}")?; | |
788 | Ok(()) | |
ba9703b0 | 789 | } |
ba9703b0 XL |
790 | } |
791 | ||
3dfed10e | 792 | fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result { |
ba9703b0 XL |
793 | for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) { |
794 | write!(w, " ")?; | |
795 | } | |
796 | writeln!(w, " │ {}", ascii) | |
797 | } | |
798 | ||
799 | /// Number of bytes to print per allocation hex dump line. | |
800 | const BYTES_PER_LINE: usize = 16; | |
801 | ||
802 | /// Prints the line start address and returns the new line start address. | |
803 | fn write_allocation_newline( | |
3dfed10e | 804 | w: &mut dyn std::fmt::Write, |
ba9703b0 XL |
805 | mut line_start: Size, |
806 | ascii: &str, | |
807 | pos_width: usize, | |
808 | prefix: &str, | |
3dfed10e | 809 | ) -> Result<Size, std::fmt::Error> { |
ba9703b0 XL |
810 | write_allocation_endline(w, ascii)?; |
811 | line_start += Size::from_bytes(BYTES_PER_LINE); | |
812 | write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?; | |
813 | Ok(line_start) | |
814 | } | |
815 | ||
816 | /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there | |
817 | /// is only one line). Note that your prefix should contain a trailing space as the lines are | |
818 | /// printed directly after it. | |
f9f354fc | 819 | fn write_allocation_bytes<Tag: Copy + Debug, Extra>( |
ba9703b0 XL |
820 | tcx: TyCtxt<'tcx>, |
821 | alloc: &Allocation<Tag, Extra>, | |
3dfed10e | 822 | w: &mut dyn std::fmt::Write, |
ba9703b0 | 823 | prefix: &str, |
3dfed10e | 824 | ) -> std::fmt::Result { |
17df50a5 | 825 | let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE); |
ba9703b0 | 826 | // Number of chars needed to represent all line numbers. |
17df50a5 | 827 | let pos_width = format!("{:x}", alloc.size().bytes()).len(); |
ba9703b0 XL |
828 | |
829 | if num_lines > 0 { | |
830 | write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?; | |
831 | } else { | |
832 | write!(w, "{}", prefix)?; | |
833 | } | |
834 | ||
835 | let mut i = Size::ZERO; | |
836 | let mut line_start = Size::ZERO; | |
837 | ||
838 | let ptr_size = tcx.data_layout.pointer_size; | |
839 | ||
840 | let mut ascii = String::new(); | |
841 | ||
842 | let oversized_ptr = |target: &mut String, width| { | |
843 | if target.len() > width { | |
844 | write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap(); | |
845 | } | |
846 | }; | |
847 | ||
17df50a5 | 848 | while i < alloc.size() { |
ba9703b0 XL |
849 | // The line start already has a space. While we could remove that space from the line start |
850 | // printing and unconditionally print a space here, that would cause the single-line case | |
851 | // to have a single space before it, which looks weird. | |
852 | if i != line_start { | |
853 | write!(w, " ")?; | |
854 | } | |
f9f354fc | 855 | if let Some(&(tag, target_id)) = alloc.relocations().get(&i) { |
ba9703b0 XL |
856 | // Memory with a relocation must be defined |
857 | let j = i.bytes_usize(); | |
3dfed10e XL |
858 | let offset = alloc |
859 | .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize()); | |
ba9703b0 | 860 | let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap(); |
f9f354fc | 861 | let offset = Size::from_bytes(offset); |
ba9703b0 | 862 | let relocation_width = |bytes| bytes * 3; |
f9f354fc XL |
863 | let ptr = Pointer::new_with_tag(target_id, offset, tag); |
864 | let mut target = format!("{:?}", ptr); | |
865 | if target.len() > relocation_width(ptr_size.bytes_usize() - 1) { | |
866 | // This is too long, try to save some space. | |
867 | target = format!("{:#?}", ptr); | |
868 | } | |
ba9703b0 XL |
869 | if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE { |
870 | // This branch handles the situation where a relocation starts in the current line | |
871 | // but ends in the next one. | |
872 | let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start); | |
873 | let overflow = ptr_size - remainder; | |
874 | let remainder_width = relocation_width(remainder.bytes_usize()) - 2; | |
875 | let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1; | |
876 | ascii.push('╾'); | |
877 | for _ in 0..remainder.bytes() - 1 { | |
878 | ascii.push('─'); | |
879 | } | |
880 | if overflow_width > remainder_width && overflow_width >= target.len() { | |
881 | // The case where the relocation fits into the part in the next line | |
882 | write!(w, "╾{0:─^1$}", "", remainder_width)?; | |
883 | line_start = | |
884 | write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; | |
885 | ascii.clear(); | |
886 | write!(w, "{0:─^1$}╼", target, overflow_width)?; | |
887 | } else { | |
888 | oversized_ptr(&mut target, remainder_width); | |
889 | write!(w, "╾{0:─^1$}", target, remainder_width)?; | |
890 | line_start = | |
891 | write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; | |
892 | write!(w, "{0:─^1$}╼", "", overflow_width)?; | |
893 | ascii.clear(); | |
894 | } | |
895 | for _ in 0..overflow.bytes() - 1 { | |
896 | ascii.push('─'); | |
897 | } | |
898 | ascii.push('╼'); | |
899 | i += ptr_size; | |
900 | continue; | |
901 | } else { | |
902 | // This branch handles a relocation that starts and ends in the current line. | |
903 | let relocation_width = relocation_width(ptr_size.bytes_usize() - 1); | |
904 | oversized_ptr(&mut target, relocation_width); | |
905 | ascii.push('╾'); | |
906 | write!(w, "╾{0:─^1$}╼", target, relocation_width)?; | |
907 | for _ in 0..ptr_size.bytes() - 2 { | |
908 | ascii.push('─'); | |
909 | } | |
910 | ascii.push('╼'); | |
911 | i += ptr_size; | |
912 | } | |
f9f354fc | 913 | } else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() { |
ba9703b0 XL |
914 | let j = i.bytes_usize(); |
915 | ||
916 | // Checked definedness (and thus range) and relocations. This access also doesn't | |
917 | // influence interpreter execution but is only for debugging. | |
3dfed10e | 918 | let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; |
ba9703b0 XL |
919 | write!(w, "{:02x}", c)?; |
920 | if c.is_ascii_control() || c >= 0x80 { | |
921 | ascii.push('.'); | |
922 | } else { | |
923 | ascii.push(char::from(c)); | |
924 | } | |
925 | i += Size::from_bytes(1); | |
926 | } else { | |
927 | write!(w, "__")?; | |
928 | ascii.push('░'); | |
929 | i += Size::from_bytes(1); | |
930 | } | |
931 | // Print a new line header if the next line still has some bytes to print. | |
17df50a5 | 932 | if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() { |
ba9703b0 XL |
933 | line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; |
934 | ascii.clear(); | |
935 | } | |
936 | } | |
937 | write_allocation_endline(w, &ascii)?; | |
c30ab7b3 SL |
938 | |
939 | Ok(()) | |
3157f602 XL |
940 | } |
941 | ||
29967ef6 | 942 | fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> { |
dfeec247 | 943 | use rustc_hir::def::DefKind; |
9fa01778 | 944 | |
29967ef6 XL |
945 | trace!("write_mir_sig: {:?}", body.source.instance); |
946 | let def_id = body.source.def_id(); | |
947 | let kind = tcx.def_kind(def_id); | |
48663c56 | 948 | let is_function = match kind { |
f9f354fc | 949 | DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, |
29967ef6 | 950 | _ => tcx.is_closure(def_id), |
9fa01778 | 951 | }; |
29967ef6 | 952 | match (kind, body.source.promoted) { |
9fa01778 | 953 | (_, Some(i)) => write!(w, "{:?} in ", i)?, |
f9f354fc XL |
954 | (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, |
955 | (DefKind::Static, _) => { | |
29967ef6 | 956 | write!(w, "static {}", if tcx.is_mutable_static(def_id) { "mut " } else { "" })? |
dfeec247 | 957 | } |
9fa01778 | 958 | (_, _) if is_function => write!(w, "fn ")?, |
f9f354fc | 959 | (DefKind::AnonConst, _) => {} // things like anon const, not an item |
48663c56 | 960 | _ => bug!("Unexpected def kind {:?}", kind), |
9cc50fc6 SL |
961 | } |
962 | ||
532ac7d7 | 963 | ty::print::with_forced_impl_filename_line(|| { |
ff7c6d11 | 964 | // see notes on #41697 elsewhere |
29967ef6 | 965 | write!(w, "{}", tcx.def_path_str(def_id)) |
7cac9316 | 966 | })?; |
a7813a04 | 967 | |
29967ef6 | 968 | if body.source.promoted.is_none() && is_function { |
9fa01778 | 969 | write!(w, "(")?; |
ea8adc8c | 970 | |
9fa01778 | 971 | // fn argument types. |
dc9dc135 | 972 | for (i, arg) in body.args_iter().enumerate() { |
9fa01778 XL |
973 | if i != 0 { |
974 | write!(w, ", ")?; | |
a7813a04 | 975 | } |
dc9dc135 | 976 | write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?; |
ea8adc8c | 977 | } |
9fa01778 | 978 | |
dc9dc135 | 979 | write!(w, ") -> {}", body.return_ty())?; |
9fa01778 | 980 | } else { |
dc9dc135 XL |
981 | assert_eq!(body.arg_count, 0); |
982 | write!(w, ": {} =", body.return_ty())?; | |
9cc50fc6 | 983 | } |
2c00a5a8 | 984 | |
6a06907d | 985 | if let Some(yield_ty) = body.yield_ty() { |
2c00a5a8 XL |
986 | writeln!(w)?; |
987 | writeln!(w, "yields {}", yield_ty)?; | |
988 | } | |
989 | ||
9fa01778 XL |
990 | write!(w, " ")?; |
991 | // Next thing that gets printed is the opening { | |
992 | ||
2c00a5a8 | 993 | Ok(()) |
3157f602 | 994 | } |
9cc50fc6 | 995 | |
f035d41b XL |
996 | fn write_user_type_annotations( |
997 | tcx: TyCtxt<'_>, | |
998 | body: &Body<'_>, | |
999 | w: &mut dyn Write, | |
1000 | ) -> io::Result<()> { | |
dc9dc135 | 1001 | if !body.user_type_annotations.is_empty() { |
0731742a XL |
1002 | writeln!(w, "| User Type Annotations")?; |
1003 | } | |
dc9dc135 | 1004 | for (index, annotation) in body.user_type_annotations.iter_enumerated() { |
f035d41b XL |
1005 | writeln!( |
1006 | w, | |
1007 | "| {:?}: {:?} at {}", | |
1008 | index.index(), | |
1009 | annotation.user_ty, | |
17df50a5 | 1010 | tcx.sess.source_map().span_to_embeddable_string(annotation.span) |
f035d41b | 1011 | )?; |
0731742a | 1012 | } |
dc9dc135 | 1013 | if !body.user_type_annotations.is_empty() { |
0731742a XL |
1014 | writeln!(w, "|")?; |
1015 | } | |
1016 | Ok(()) | |
1017 | } | |
1018 | ||
dc9dc135 | 1019 | pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> { |
f9f354fc XL |
1020 | if let Some(i) = single { |
1021 | vec![i] | |
1022 | } else { | |
17df50a5 | 1023 | tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect() |
f9f354fc | 1024 | } |
7cac9316 | 1025 | } |