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