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