]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Rollup merge of #102847 - joshtriplett:bugfix-impl-fd-traits-for-io-types, r=m-ou-se
[rust.git] / compiler / rustc_codegen_ssa / src / mir / debuginfo.rs
1 use crate::traits::*;
2 use rustc_index::vec::IndexVec;
3 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
4 use rustc_middle::mir;
5 use rustc_middle::ty;
6 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
7 use rustc_session::config::DebugInfo;
8 use rustc_span::symbol::{kw, Symbol};
9 use rustc_span::{BytePos, Span};
10 use rustc_target::abi::Abi;
11 use rustc_target::abi::Size;
12
13 use super::operand::{OperandRef, OperandValue};
14 use super::place::PlaceRef;
15 use super::{FunctionCx, LocalRef};
16
17 pub struct FunctionDebugContext<S, L> {
18     pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
19 }
20
21 #[derive(Copy, Clone)]
22 pub enum VariableKind {
23     ArgumentVariable(usize /*index*/),
24     LocalVariable,
25 }
26
27 /// Like `mir::VarDebugInfo`, but within a `mir::Local`.
28 #[derive(Copy, Clone)]
29 pub struct PerLocalVarDebugInfo<'tcx, D> {
30     pub name: Symbol,
31     pub source_info: mir::SourceInfo,
32
33     /// `DIVariable` returned by `create_dbg_var`.
34     pub dbg_var: Option<D>,
35
36     /// `.place.projection` from `mir::VarDebugInfo`.
37     pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
38 }
39
40 #[derive(Clone, Copy, Debug)]
41 pub struct DebugScope<S, L> {
42     pub dbg_scope: S,
43
44     /// Call site location, if this scope was inlined from another function.
45     pub inlined_at: Option<L>,
46
47     // Start and end offsets of the file to which this DIScope belongs.
48     // These are used to quickly determine whether some span refers to the same file.
49     pub file_start_pos: BytePos,
50     pub file_end_pos: BytePos,
51 }
52
53 impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
54     /// DILocations inherit source file name from the parent DIScope.  Due to macro expansions
55     /// it may so happen that the current span belongs to a different file than the DIScope
56     /// corresponding to span's containing source scope.  If so, we need to create a DIScope
57     /// "extension" into that file.
58     pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>(
59         &self,
60         cx: &Cx,
61         span: Span,
62     ) -> S {
63         let pos = span.lo();
64         if pos < self.file_start_pos || pos >= self.file_end_pos {
65             let sm = cx.sess().source_map();
66             cx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file)
67         } else {
68             self.dbg_scope
69         }
70     }
71 }
72
73 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
74     pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
75         bx.set_span(source_info.span);
76         if let Some(dbg_loc) = self.dbg_loc(source_info) {
77             bx.set_dbg_loc(dbg_loc);
78         }
79     }
80
81     fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option<Bx::DILocation> {
82         let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?;
83         Some(self.cx.dbg_loc(dbg_scope, inlined_at, span))
84     }
85
86     fn adjusted_span_and_dbg_scope(
87         &self,
88         source_info: mir::SourceInfo,
89     ) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
90         let span = self.adjust_span_for_debugging(source_info.span);
91         let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
92         Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
93     }
94
95     /// In order to have a good line stepping behavior in debugger, we overwrite debug
96     /// locations of macro expansions with that of the outermost expansion site (when the macro is
97     /// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
98     fn adjust_span_for_debugging(&self, mut span: Span) -> Span {
99         // Bail out if debug info emission is not enabled.
100         if self.debug_context.is_none() {
101             return span;
102         }
103
104         if self.cx.tcx().should_collapse_debuginfo(span) {
105             // Walk up the macro expansion chain until we reach a non-expanded span.
106             // We also stop at the function body level because no line stepping can occur
107             // at the level above that.
108             // Use span of the outermost expansion site, while keeping the original lexical scope.
109             span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt());
110         }
111
112         span
113     }
114
115     fn spill_operand_to_stack(
116         operand: &OperandRef<'tcx, Bx::Value>,
117         name: Option<String>,
118         bx: &mut Bx,
119     ) -> PlaceRef<'tcx, Bx::Value> {
120         // "Spill" the value onto the stack, for debuginfo,
121         // without forcing non-debuginfo uses of the local
122         // to also load from the stack every single time.
123         // FIXME(#68817) use `llvm.dbg.value` instead,
124         // at least for the cases which LLVM handles correctly.
125         let spill_slot = PlaceRef::alloca(bx, operand.layout);
126         if let Some(name) = name {
127             bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill"));
128         }
129         operand.val.store(bx, spill_slot);
130         spill_slot
131     }
132
133     /// Apply debuginfo and/or name, after creating the `alloca` for a local,
134     /// or initializing the local with an operand (whichever applies).
135     pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
136         let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
137
138         // FIXME(eddyb) maybe name the return place as `_0` or `return`?
139         if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable()
140         {
141             return;
142         }
143
144         let vars = match &self.per_local_var_debug_info {
145             Some(per_local) => &per_local[local],
146             None => return,
147         };
148         let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
149         let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
150
151         let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
152             let arg_index = local.index() - 1;
153
154             // Add debuginfo even to unnamed arguments.
155             // FIXME(eddyb) is this really needed?
156             if arg_index == 0 && has_proj() {
157                 // Hide closure environments from debuginfo.
158                 // FIXME(eddyb) shouldn't `ArgumentVariable` indices
159                 // be offset to account for the hidden environment?
160                 None
161             } else if whole_local_var.is_some() {
162                 // No need to make up anything, there is a `mir::VarDebugInfo`
163                 // covering the whole local.
164                 // FIXME(eddyb) take `whole_local_var.source_info.scope` into
165                 // account, just in case it doesn't use `ArgumentVariable`
166                 // (after #67586 gets fixed).
167                 None
168             } else {
169                 let name = kw::Empty;
170                 let decl = &self.mir.local_decls[local];
171                 let dbg_var = if full_debug_info {
172                     self.adjusted_span_and_dbg_scope(decl.source_info).map(
173                         |(dbg_scope, _, span)| {
174                             // FIXME(eddyb) is this `+ 1` needed at all?
175                             let kind = VariableKind::ArgumentVariable(arg_index + 1);
176
177                             let arg_ty = self.monomorphize(decl.ty);
178
179                             self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
180                         },
181                     )
182                 } else {
183                     None
184                 };
185
186                 Some(PerLocalVarDebugInfo {
187                     name,
188                     source_info: decl.source_info,
189                     dbg_var,
190                     projection: ty::List::empty(),
191                 })
192             }
193         } else {
194             None
195         };
196
197         let local_ref = &self.locals[local];
198
199         let name = if bx.sess().fewer_names() {
200             None
201         } else {
202             Some(match whole_local_var.or(fallback_var) {
203                 Some(var) if var.name != kw::Empty => var.name.to_string(),
204                 _ => format!("{:?}", local),
205             })
206         };
207
208         if let Some(name) = &name {
209             match local_ref {
210                 LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
211                     bx.set_var_name(place.llval, name);
212                 }
213                 LocalRef::Operand(Some(operand)) => match operand.val {
214                     OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
215                         bx.set_var_name(x, name);
216                     }
217                     OperandValue::Pair(a, b) => {
218                         // FIXME(eddyb) these are scalar components,
219                         // maybe extract the high-level fields?
220                         bx.set_var_name(a, &(name.clone() + ".0"));
221                         bx.set_var_name(b, &(name.clone() + ".1"));
222                     }
223                 },
224                 LocalRef::Operand(None) => {}
225             }
226         }
227
228         if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
229             return;
230         }
231
232         let base = match local_ref {
233             LocalRef::Operand(None) => return,
234
235             LocalRef::Operand(Some(operand)) => {
236                 // Don't spill operands onto the stack in naked functions.
237                 // See: https://github.com/rust-lang/rust/issues/42779
238                 let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id());
239                 if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
240                     return;
241                 }
242
243                 Self::spill_operand_to_stack(operand, name, bx)
244             }
245
246             LocalRef::Place(place) => *place,
247
248             // FIXME(eddyb) add debuginfo for unsized places too.
249             LocalRef::UnsizedPlace(_) => return,
250         };
251
252         let vars = vars.iter().copied().chain(fallback_var);
253
254         for var in vars {
255             let Some(dbg_var) = var.dbg_var else { continue };
256             let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
257
258             let mut direct_offset = Size::ZERO;
259             // FIXME(eddyb) use smallvec here.
260             let mut indirect_offsets = vec![];
261             let mut place = base;
262
263             for elem in &var.projection[..] {
264                 match *elem {
265                     mir::ProjectionElem::Deref => {
266                         indirect_offsets.push(Size::ZERO);
267                         place = bx.load_operand(place).deref(bx.cx());
268                     }
269                     mir::ProjectionElem::Field(field, _) => {
270                         let i = field.index();
271                         let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
272                         *offset += place.layout.fields.offset(i);
273                         place = place.project_field(bx, i);
274                     }
275                     mir::ProjectionElem::Downcast(_, variant) => {
276                         place = place.project_downcast(bx, variant);
277                     }
278                     _ => span_bug!(
279                         var.source_info.span,
280                         "unsupported var debuginfo place `{:?}`",
281                         mir::Place { local, projection: var.projection },
282                     ),
283                 }
284             }
285
286             // When targeting MSVC, create extra allocas for arguments instead of pointing multiple
287             // dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
288             // not DWARF and LLVM doesn't support translating the resulting
289             // [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
290             // Creating extra allocas on the stack makes the resulting debug info simple enough
291             // that LLVM can generate correct CodeView records and thus the values appear in the
292             // debugger. (#83709)
293             let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
294                 && self.mir.local_kind(local) == mir::LocalKind::Arg
295                 // LLVM can handle simple things but anything more complex than just a direct
296                 // offset or one indirect offset of 0 is too complex for it to generate CV records
297                 // correctly.
298                 && (direct_offset != Size::ZERO
299                     || !matches!(&indirect_offsets[..], [Size::ZERO] | []));
300
301             if should_create_individual_allocas {
302                 // Create a variable which will be a pointer to the actual value
303                 let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut {
304                     mutbl: mir::Mutability::Mut,
305                     ty: place.layout.ty,
306                 }));
307                 let ptr_layout = bx.layout_of(ptr_ty);
308                 let alloca = PlaceRef::alloca(bx, ptr_layout);
309                 bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill"));
310
311                 // Write the pointer to the variable
312                 bx.store(place.llval, alloca.llval, alloca.align);
313
314                 // Point the debug info to `*alloca` for the current variable
315                 bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]);
316             } else {
317                 bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
318             }
319         }
320     }
321
322     pub fn debug_introduce_locals(&self, bx: &mut Bx) {
323         if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
324             for local in self.locals.indices() {
325                 self.debug_introduce_local(bx, local);
326             }
327         }
328     }
329
330     /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
331     pub fn compute_per_local_var_debug_info(
332         &self,
333         bx: &mut Bx,
334     ) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
335         let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
336
337         let target_is_msvc = self.cx.sess().target.is_like_msvc;
338
339         if !full_debug_info && self.cx.sess().fewer_names() {
340             return None;
341         }
342
343         let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
344         for var in &self.mir.var_debug_info {
345             let dbg_scope_and_span = if full_debug_info {
346                 self.adjusted_span_and_dbg_scope(var.source_info)
347             } else {
348                 None
349             };
350
351             let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
352                 let (var_ty, var_kind) = match var.value {
353                     mir::VarDebugInfoContents::Place(place) => {
354                         let var_ty = self.monomorphized_place_ty(place.as_ref());
355                         let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
356                             && place.projection.is_empty()
357                             && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
358                         {
359                             let arg_index = place.local.index() - 1;
360                             if target_is_msvc {
361                                 // ScalarPair parameters are spilled to the stack so they need to
362                                 // be marked as a `LocalVariable` for MSVC debuggers to visualize
363                                 // their data correctly. (See #81894 & #88625)
364                                 let var_ty_layout = self.cx.layout_of(var_ty);
365                                 if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
366                                     VariableKind::LocalVariable
367                                 } else {
368                                     VariableKind::ArgumentVariable(arg_index + 1)
369                                 }
370                             } else {
371                                 // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
372                                 // offset in closures to account for the hidden environment?
373                                 // Also, is this `+ 1` needed at all?
374                                 VariableKind::ArgumentVariable(arg_index + 1)
375                             }
376                         } else {
377                             VariableKind::LocalVariable
378                         };
379                         (var_ty, var_kind)
380                     }
381                     mir::VarDebugInfoContents::Const(c) => {
382                         let ty = self.monomorphize(c.ty());
383                         (ty, VariableKind::LocalVariable)
384                     }
385                 };
386
387                 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
388             });
389
390             match var.value {
391                 mir::VarDebugInfoContents::Place(place) => {
392                     per_local[place.local].push(PerLocalVarDebugInfo {
393                         name: var.name,
394                         source_info: var.source_info,
395                         dbg_var,
396                         projection: place.projection,
397                     });
398                 }
399                 mir::VarDebugInfoContents::Const(c) => {
400                     if let Some(dbg_var) = dbg_var {
401                         let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
402
403                         if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) {
404                             let base = Self::spill_operand_to_stack(
405                                 &operand,
406                                 Some(var.name.to_string()),
407                                 bx,
408                             );
409
410                             bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]);
411                         }
412                     }
413                 }
414             }
415         }
416         Some(per_local)
417     }
418 }