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