Skip to main content

rustc_codegen_ssa/mir/
debuginfo.rs

1use std::collections::hash_map::Entry;
2use std::marker::PhantomData;
3use std::ops::Range;
4
5use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, Size, VariantIdx};
6use rustc_data_structures::fx::FxHashMap;
7use rustc_index::IndexVec;
8use rustc_index::bit_set::DenseBitSet;
9use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
10use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
11use rustc_middle::ty::{Instance, Ty};
12use rustc_middle::{bug, mir, ty};
13use rustc_session::config::DebugInfo;
14use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, hygiene, sym};
15
16use super::operand::{OperandRef, OperandValue};
17use super::place::{PlaceRef, PlaceValue};
18use super::{FunctionCx, LocalRef, PerLocalVarDebugInfoIndexVec};
19use crate::traits::*;
20
21pub struct FunctionDebugContext<'tcx, S, L> {
22    /// Maps from source code to the corresponding debug info scope.
23    pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
24
25    /// Maps from an inlined function to its debug info declaration.
26    pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
27}
28
29#[derive(#[automatically_derived]
impl ::core::marker::Copy for VariableKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for VariableKind {
    #[inline]
    fn clone(&self) -> VariableKind {
        let _: ::core::clone::AssertParamIsClone<usize>;
        *self
    }
}Clone)]
30pub enum VariableKind {
31    ArgumentVariable(usize /*index*/),
32    LocalVariable,
33}
34
35/// Like `mir::VarDebugInfo`, but within a `mir::Local`.
36#[derive(#[automatically_derived]
impl<'tcx, D: ::core::clone::Clone> ::core::clone::Clone for
    PerLocalVarDebugInfo<'tcx, D> {
    #[inline]
    fn clone(&self) -> PerLocalVarDebugInfo<'tcx, D> {
        PerLocalVarDebugInfo {
            name: ::core::clone::Clone::clone(&self.name),
            source_info: ::core::clone::Clone::clone(&self.source_info),
            dbg_var: ::core::clone::Clone::clone(&self.dbg_var),
            fragment: ::core::clone::Clone::clone(&self.fragment),
            projection: ::core::clone::Clone::clone(&self.projection),
        }
    }
}Clone)]
37pub struct PerLocalVarDebugInfo<'tcx, D> {
38    pub name: Symbol,
39    pub source_info: mir::SourceInfo,
40
41    /// `DIVariable` returned by `create_dbg_var`.
42    pub dbg_var: Option<D>,
43
44    /// Byte range in the `dbg_var` covered by this fragment,
45    /// if this is a fragment of a composite `VarDebugInfo`.
46    pub fragment: Option<Range<Size>>,
47
48    /// `.place.projection` from `mir::VarDebugInfo`.
49    pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
50}
51
52/// Information needed to emit a constant.
53pub struct ConstDebugInfo<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
54    pub name: String,
55    pub source_info: mir::SourceInfo,
56    pub operand: OperandRef<'tcx, Bx::Value>,
57    pub dbg_var: Bx::DIVariable,
58    pub dbg_loc: Bx::DILocation,
59    pub fragment: Option<Range<Size>>,
60    pub _phantom: PhantomData<&'a ()>,
61}
62
63#[derive(#[automatically_derived]
impl<S: ::core::clone::Clone, L: ::core::clone::Clone> ::core::clone::Clone
    for DebugScope<S, L> {
    #[inline]
    fn clone(&self) -> DebugScope<S, L> {
        DebugScope {
            dbg_scope: ::core::clone::Clone::clone(&self.dbg_scope),
            inlined_at: ::core::clone::Clone::clone(&self.inlined_at),
            file_start_pos: ::core::clone::Clone::clone(&self.file_start_pos),
            file_end_pos: ::core::clone::Clone::clone(&self.file_end_pos),
        }
    }
}Clone, #[automatically_derived]
impl<S: ::core::marker::Copy, L: ::core::marker::Copy> ::core::marker::Copy
    for DebugScope<S, L> {
}Copy, #[automatically_derived]
impl<S: ::core::fmt::Debug, L: ::core::fmt::Debug> ::core::fmt::Debug for
    DebugScope<S, L> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f, "DebugScope",
            "dbg_scope", &self.dbg_scope, "inlined_at", &self.inlined_at,
            "file_start_pos", &self.file_start_pos, "file_end_pos",
            &&self.file_end_pos)
    }
}Debug)]
64pub struct DebugScope<S, L> {
65    pub dbg_scope: S,
66
67    /// Call site location, if this scope was inlined from another function.
68    pub inlined_at: Option<L>,
69
70    // Start and end offsets of the file to which this DIScope belongs.
71    // These are used to quickly determine whether some span refers to the same file.
72    pub file_start_pos: BytePos,
73    pub file_end_pos: BytePos,
74}
75
76impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
77    /// DILocations inherit source file name from the parent DIScope. Due to macro expansions
78    /// it may so happen that the current span belongs to a different file than the DIScope
79    /// corresponding to span's containing source scope. If so, we need to create a DIScope
80    /// "extension" into that file.
81    pub fn adjust_dbg_scope_for_span<
82        'a,
83        Bx: BuilderMethods<'a, 'tcx, DIScope = S, DILocation = L>,
84    >(
85        &self,
86        bx: &mut Bx,
87        span: Span,
88    ) -> S {
89        let pos = span.lo();
90        if pos < self.file_start_pos || pos >= self.file_end_pos {
91            let sm = bx.sess().source_map();
92            bx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file)
93        } else {
94            self.dbg_scope
95        }
96    }
97}
98
99trait DebugInfoOffsetLocation<'tcx, Bx> {
100    fn deref(&self, bx: &mut Bx) -> Self;
101    fn layout(&self) -> TyAndLayout<'tcx>;
102    fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self;
103    fn project_constant_index(&self, bx: &mut Bx, offset: u64) -> Self;
104    fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self;
105}
106
107impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
108    for PlaceRef<'tcx, Bx::Value>
109{
110    fn deref(&self, bx: &mut Bx) -> Self {
111        bx.load_operand(*self).deref(bx.cx())
112    }
113
114    fn layout(&self) -> TyAndLayout<'tcx> {
115        self.layout
116    }
117
118    fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self {
119        PlaceRef::project_field(*self, bx, field.index())
120    }
121
122    fn project_constant_index(&self, bx: &mut Bx, offset: u64) -> Self {
123        let lloffset = bx.cx().const_usize(offset);
124        self.project_index(bx, lloffset)
125    }
126
127    fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
128        self.project_downcast(bx, variant)
129    }
130}
131
132impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
133    for TyAndLayout<'tcx>
134{
135    fn deref(&self, bx: &mut Bx) -> Self {
136        bx.cx().layout_of(
137            self.ty.builtin_deref(true).unwrap_or_else(|| ::rustc_middle::util::bug::bug_fmt(format_args!("cannot deref `{0}`",
        self.ty))bug!("cannot deref `{}`", self.ty)),
138        )
139    }
140
141    fn layout(&self) -> TyAndLayout<'tcx> {
142        *self
143    }
144
145    fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self {
146        self.field(bx.cx(), field.index())
147    }
148
149    fn project_constant_index(&self, bx: &mut Bx, index: u64) -> Self {
150        self.field(bx.cx(), index as usize)
151    }
152
153    fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
154        self.for_variant(bx.cx(), variant)
155    }
156}
157
158struct DebugInfoOffset<T> {
159    /// Offset from the `base` used to calculate the debuginfo offset.
160    direct_offset: Size,
161    /// Each offset in this vector indicates one level of indirection from the base or previous
162    /// indirect offset plus a dereference.
163    indirect_offsets: Vec<Size>,
164    /// The final location debuginfo should point to.
165    result: T,
166}
167
168fn calculate_debuginfo_offset<
169    'a,
170    'tcx,
171    Bx: BuilderMethods<'a, 'tcx>,
172    L: DebugInfoOffsetLocation<'tcx, Bx>,
173>(
174    bx: &mut Bx,
175    projection: &[mir::PlaceElem<'tcx>],
176    base: L,
177) -> DebugInfoOffset<L> {
178    let mut direct_offset = Size::ZERO;
179    // FIXME(eddyb) use smallvec here.
180    let mut indirect_offsets = ::alloc::vec::Vec::new()vec![];
181    let mut place = base;
182
183    for elem in projection {
184        match *elem {
185            mir::ProjectionElem::Deref => {
186                indirect_offsets.push(Size::ZERO);
187                place = place.deref(bx);
188            }
189            mir::ProjectionElem::Field(field, _) => {
190                let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
191                *offset += place.layout().fields.offset(field.index());
192                place = place.project_field(bx, field);
193            }
194            mir::ProjectionElem::Downcast(_, variant) => {
195                place = place.downcast(bx, variant);
196            }
197            mir::ProjectionElem::ConstantIndex {
198                offset: index,
199                min_length: _,
200                from_end: false,
201            } => {
202                let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
203                let FieldsShape::Array { stride, count: _ } = place.layout().fields else {
204                    ::rustc_middle::util::bug::bug_fmt(format_args!("ConstantIndex on non-array type {0:?}",
        place.layout()))bug!("ConstantIndex on non-array type {:?}", place.layout())
205                };
206                *offset += stride * index;
207                place = place.project_constant_index(bx, index);
208            }
209            _ => {
210                // Sanity check for `can_use_in_debuginfo`.
211                if !!elem.can_use_in_debuginfo() {
    ::core::panicking::panic("assertion failed: !elem.can_use_in_debuginfo()")
};assert!(!elem.can_use_in_debuginfo());
212                ::rustc_middle::util::bug::bug_fmt(format_args!("unsupported var debuginfo projection `{0:?}`",
        projection))bug!("unsupported var debuginfo projection `{:?}`", projection)
213            }
214        }
215    }
216
217    DebugInfoOffset { direct_offset, indirect_offsets, result: place }
218}
219
220impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
221    pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
222        bx.set_span(source_info.span);
223        if let Some(dbg_loc) = self.dbg_loc(bx, source_info) {
224            bx.set_dbg_loc(dbg_loc);
225        }
226    }
227
228    fn dbg_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) -> Option<Bx::DILocation> {
229        let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(bx, source_info)?;
230        Some(bx.dbg_loc(dbg_scope, inlined_at, span))
231    }
232
233    fn adjusted_span_and_dbg_scope(
234        &self,
235        bx: &mut Bx,
236        source_info: mir::SourceInfo,
237    ) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
238        let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
239        let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span);
240        Some((scope.adjust_dbg_scope_for_span(bx, span), scope.inlined_at, span))
241    }
242
243    fn spill_operand_to_stack(
244        operand: OperandRef<'tcx, Bx::Value>,
245        name: Option<String>,
246        bx: &mut Bx,
247    ) -> PlaceRef<'tcx, Bx::Value> {
248        // "Spill" the value onto the stack, for debuginfo,
249        // without forcing non-debuginfo uses of the local
250        // to also load from the stack every single time.
251        // FIXME(#68817) use `llvm.dbg.value` instead,
252        // at least for the cases which LLVM handles correctly.
253        let spill_slot = PlaceRef::alloca(bx, operand.layout);
254        if let Some(name) = name {
255            bx.set_var_name(spill_slot.val.llval, &(name + ".dbg.spill"));
256        }
257        operand.val.store(bx, spill_slot);
258        spill_slot
259    }
260
261    // Indicates that local is set to a new value. The `layout` and `projection` are used to
262    // calculate the offset.
263    fn debug_new_val_to_local(
264        &self,
265        bx: &mut Bx,
266        local: mir::Local,
267        base: PlaceRef<'tcx, Bx::Value>,
268        projection: &[mir::PlaceElem<'tcx>],
269    ) {
270        let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
271        if !full_debug_info {
272            return;
273        }
274
275        let vars = match &self.per_local_var_debug_info {
276            Some(per_local) => &per_local[local],
277            None => return,
278        };
279
280        let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
281            calculate_debuginfo_offset(bx, projection, base.layout);
282        for var in vars.iter() {
283            let Some(dbg_var) = var.dbg_var else {
284                continue;
285            };
286            let Some(dbg_loc) = self.dbg_loc(bx, var.source_info) else {
287                continue;
288            };
289            bx.dbg_var_value(
290                dbg_var,
291                dbg_loc,
292                base.val.llval,
293                direct_offset,
294                &indirect_offsets,
295                &var.fragment,
296            );
297        }
298    }
299
300    fn debug_poison_to_local(&self, bx: &mut Bx, local: mir::Local) {
301        let ty = self.monomorphize(self.mir.local_decls[local].ty);
302        let layout = bx.cx().layout_of(ty);
303        let to_backend_ty = bx.cx().immediate_backend_type(layout);
304        let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
305        self.debug_new_val_to_local(bx, local, place_ref, &[]);
306    }
307
308    /// Apply debuginfo and/or name, after creating the `alloca` for a local,
309    /// or initializing the local with an operand (whichever applies).
310    pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
311        let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
312
313        let vars = match &self.per_local_var_debug_info {
314            Some(per_local) => &per_local[local],
315            None => return,
316        };
317        let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
318        let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
319
320        let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
321            let arg_index = local.index() - 1;
322
323            // Add debuginfo even to unnamed arguments.
324            // FIXME(eddyb) is this really needed?
325            if arg_index == 0 && has_proj() {
326                // Hide closure environments from debuginfo.
327                // FIXME(eddyb) shouldn't `ArgumentVariable` indices
328                // be offset to account for the hidden environment?
329                None
330            } else if whole_local_var.is_some() {
331                // No need to make up anything, there is a `mir::VarDebugInfo`
332                // covering the whole local.
333                // FIXME(eddyb) take `whole_local_var.source_info.scope` into
334                // account, just in case it doesn't use `ArgumentVariable`
335                // (after #67586 gets fixed).
336                None
337            } else {
338                let name = sym::empty;
339                let decl = &self.mir.local_decls[local];
340                let dbg_var = if full_debug_info {
341                    self.adjusted_span_and_dbg_scope(bx, decl.source_info).map(
342                        |(dbg_scope, _, span)| {
343                            // FIXME(eddyb) is this `+ 1` needed at all?
344                            let kind = VariableKind::ArgumentVariable(arg_index + 1);
345
346                            let arg_ty = self.monomorphize(decl.ty);
347
348                            bx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
349                        },
350                    )
351                } else {
352                    None
353                };
354
355                Some(PerLocalVarDebugInfo {
356                    name,
357                    source_info: decl.source_info,
358                    dbg_var,
359                    fragment: None,
360                    projection: ty::List::empty(),
361                })
362            }
363        } else {
364            None
365        };
366
367        let local_ref = &self.locals[local];
368
369        let name = if bx.sess().fewer_names() {
370            None
371        } else {
372            Some(match whole_local_var.or_else(|| fallback_var.clone()) {
373                Some(var) if var.name != sym::empty => var.name.to_string(),
374                _ => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}", local))
    })format!("{local:?}"),
375            })
376        };
377
378        if let Some(name) = &name {
379            match local_ref {
380                LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
381                    bx.set_var_name(place.val.llval, name);
382                }
383                LocalRef::Operand(operand) => match operand.val {
384                    OperandValue::Ref(PlaceValue { llval: x, .. }) | OperandValue::Immediate(x) => {
385                        bx.set_var_name(x, name);
386                    }
387                    OperandValue::Pair(a, b) => {
388                        // FIXME(eddyb) these are scalar components,
389                        // maybe extract the high-level fields?
390                        bx.set_var_name(a, &(name.clone() + ".0"));
391                        bx.set_var_name(b, &(name.clone() + ".1"));
392                    }
393                    OperandValue::ZeroSized => {
394                        // These never have a value to talk about
395                    }
396                },
397                LocalRef::PendingOperand => {}
398            }
399        }
400
401        if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
402            return;
403        }
404
405        let base = match local_ref {
406            LocalRef::PendingOperand => return,
407
408            LocalRef::Operand(operand) => {
409                // Don't spill operands onto the stack in naked functions.
410                // See: https://github.com/rust-lang/rust/issues/42779
411                let attrs = bx.tcx().codegen_instance_attrs(self.instance.def);
412                if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
413                    return;
414                }
415
416                // Don't spill `<vscale x N x i1>` for `N != 16`:
417                //
418                // SVE predicates are only one bit for each byte in an SVE vector (which makes
419                // sense, the predicate only needs to keep track of whether a lane is
420                // enabled/disabled). i.e. a `<vscale x 16 x i8>` vector has a `<vscale x 16 x i1>`
421                // predicate type. `<vscale x 16 x i1>` corresponds to two bytes of storage,
422                // multiplied by the `vscale`, with one bit for each of the sixteen lanes.
423                //
424                // For a vector with fewer elements, such as `svint32_t`/`<vscale x 4 x i32>`,
425                // while only a `<vscale x 4 x i1>` predicate type would be strictly necessary,
426                // relevant intrinsics still take a `svbool_t`/`<vscale x 16 x i1>` - this is
427                // because a `<vscale x 4 x i1>` is only half of a byte (for `vscale=1`), and with
428                // memory being byte-addressable, it's unclear how to store that.
429                //
430                // Due to this, LLVM ultimately decided not to support stores of `<vscale x N x i1>`
431                // for `N != 16`. As for `vscale=1` and `N` fewer than sixteen, partial bytes would
432                // need to be stored (except for `N=8`, but that also isn't supported). `N` can
433                // never be greater than sixteen as that ends up larger than the 128-bit increment
434                // size.
435                //
436                // Internally, with an intrinsic operating on a `svint32_t`/`<vscale x 4 x i32>`
437                // (for example), the intrinsic takes the `svbool_t`/`<vscale x 16 x i1>` predicate
438                // and casts it to a `svbool4_t`/`<vscale x 4 x i1>`. Therefore, it's important that
439                // the `<vscale x 4 x i1>` never spills because that'll cause errors during
440                // instruction selection. Spilling to the stack to create debuginfo for these
441                // intermediate values must be avoided and doing so won't affect the
442                // debugging experience anyway.
443                if operand.layout.ty.is_scalable_vector()
444                    && bx.sess().target.arch == rustc_target::spec::Arch::AArch64
445                {
446                    let (count, element_ty, _) =
447                        operand.layout.ty.scalable_vector_parts(bx.tcx()).unwrap();
448                    // i.e. `<vscale x N x i1>` when `N != 16`
449                    if element_ty.is_bool() && count != 16 {
450                        return;
451                    }
452                }
453
454                Self::spill_operand_to_stack(*operand, name, bx)
455            }
456
457            LocalRef::Place(place) => *place,
458
459            // FIXME(eddyb) add debuginfo for unsized places too.
460            LocalRef::UnsizedPlace(_) => return,
461        };
462
463        let vars = vars.iter().cloned().chain(fallback_var);
464
465        for var in vars {
466            self.debug_introduce_local_as_var(bx, local, base, var);
467        }
468    }
469
470    fn debug_introduce_local_as_var(
471        &self,
472        bx: &mut Bx,
473        local: mir::Local,
474        base: PlaceRef<'tcx, Bx::Value>,
475        var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
476    ) {
477        let Some(dbg_var) = var.dbg_var else { return };
478        let Some(dbg_loc) = self.dbg_loc(bx, var.source_info) else { return };
479
480        let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
481            calculate_debuginfo_offset(bx, var.projection, base.layout);
482
483        // When targeting MSVC, create extra allocas for arguments instead of pointing multiple
484        // dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
485        // not DWARF and LLVM doesn't support translating the resulting
486        // [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
487        // Creating extra allocas on the stack makes the resulting debug info simple enough
488        // that LLVM can generate correct CodeView records and thus the values appear in the
489        // debugger. (#83709)
490        let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
491            && self.mir.local_kind(local) == mir::LocalKind::Arg
492            // LLVM can handle simple things but anything more complex than just a direct
493            // offset or one indirect offset of 0 is too complex for it to generate CV records
494            // correctly.
495            && (direct_offset != Size::ZERO || !#[allow(non_exhaustive_omitted_patterns)] match &indirect_offsets[..] {
    [Size::ZERO] | [] => true,
    _ => false,
}matches!(&indirect_offsets[..], [Size::ZERO] | []));
496
497        if should_create_individual_allocas {
498            let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
499                calculate_debuginfo_offset(bx, var.projection, base);
500
501            // Create a variable which will be a pointer to the actual value
502            let ptr_ty = Ty::new_mut_ptr(bx.tcx(), place.layout.ty);
503            let ptr_layout = bx.layout_of(ptr_ty);
504            let alloca = PlaceRef::alloca(bx, ptr_layout);
505            bx.set_var_name(alloca.val.llval, &(var.name.to_string() + ".dbg.spill"));
506
507            // Write the pointer to the variable
508            bx.store_to_place(place.val.llval, alloca.val);
509
510            // Point the debug info to `*alloca` for the current variable
511            bx.dbg_var_addr(
512                dbg_var,
513                dbg_loc,
514                alloca.val.llval,
515                Size::ZERO,
516                &[Size::ZERO],
517                &var.fragment,
518            );
519        } else {
520            bx.dbg_var_addr(
521                dbg_var,
522                dbg_loc,
523                base.val.llval,
524                direct_offset,
525                &indirect_offsets,
526                &var.fragment,
527            );
528        }
529    }
530
531    pub(crate) fn debug_introduce_locals(
532        &self,
533        bx: &mut Bx,
534        consts: Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
535    ) {
536        if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
537            for local in self.locals.indices() {
538                self.debug_introduce_local(bx, local);
539            }
540
541            for ConstDebugInfo { name, source_info, operand, dbg_var, dbg_loc, fragment, .. } in
542                consts.into_iter()
543            {
544                self.set_debug_loc(bx, source_info);
545                let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
546                bx.clear_dbg_loc();
547
548                bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], &fragment);
549            }
550        }
551    }
552
553    /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
554    pub(crate) fn compute_per_local_var_debug_info(
555        &self,
556        bx: &mut Bx,
557    ) -> Option<(
558        PerLocalVarDebugInfoIndexVec<'tcx, Bx::DIVariable>,
559        Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
560    )> {
561        let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
562
563        let target_is_msvc = self.cx.sess().target.is_like_msvc;
564
565        if !full_debug_info && self.cx.sess().fewer_names() {
566            return None;
567        }
568
569        let mut per_local = IndexVec::from_elem(::alloc::vec::Vec::new()vec![], &self.mir.local_decls);
570        let mut constants = ::alloc::vec::Vec::new()vec![];
571        let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default();
572        for var in &self.mir.var_debug_info {
573            let dbg_scope_and_span = if full_debug_info {
574                self.adjusted_span_and_dbg_scope(bx, var.source_info)
575            } else {
576                None
577            };
578
579            let var_ty = if let Some(ref fragment) = var.composite {
580                self.monomorphize(fragment.ty)
581            } else {
582                match var.value {
583                    mir::VarDebugInfoContents::Place(place) => {
584                        self.monomorphized_place_ty(place.as_ref())
585                    }
586                    mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
587                }
588            };
589
590            let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
591                let var_kind = if let Some(arg_index) = var.argument_index
592                    && var.composite.is_none()
593                    && let mir::VarDebugInfoContents::Place(place) = var.value
594                    && place.projection.is_empty()
595                {
596                    let arg_index = arg_index as usize;
597                    if target_is_msvc {
598                        // ScalarPair parameters are spilled to the stack so they need to
599                        // be marked as a `LocalVariable` for MSVC debuggers to visualize
600                        // their data correctly. (See #81894 & #88625)
601                        let var_ty_layout = self.cx.layout_of(var_ty);
602                        if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } =
603                            var_ty_layout.backend_repr
604                        {
605                            VariableKind::LocalVariable
606                        } else {
607                            VariableKind::ArgumentVariable(arg_index)
608                        }
609                    } else {
610                        // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
611                        // offset in closures to account for the hidden environment?
612                        VariableKind::ArgumentVariable(arg_index)
613                    }
614                } else {
615                    VariableKind::LocalVariable
616                };
617
618                if let VariableKind::ArgumentVariable(arg_index) = var_kind {
619                    match params_seen.entry((dbg_scope, arg_index)) {
620                        Entry::Occupied(o) => o.get().clone(),
621                        Entry::Vacant(v) => v
622                            .insert(bx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span))
623                            .clone(),
624                    }
625                } else {
626                    bx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
627                }
628            });
629
630            let fragment = if let Some(ref fragment) = var.composite {
631                let var_layout = self.cx.layout_of(var_ty);
632
633                let DebugInfoOffset { direct_offset, indirect_offsets, result: fragment_layout } =
634                    calculate_debuginfo_offset(bx, &fragment.projection, var_layout);
635                if !indirect_offsets.is_empty() {
    ::core::panicking::panic("assertion failed: indirect_offsets.is_empty()")
};assert!(indirect_offsets.is_empty());
636
637                if fragment_layout.size == Size::ZERO {
638                    // Fragment is a ZST, so does not represent anything. Avoid generating anything
639                    // as this may conflict with a fragment that covers the entire variable.
640                    continue;
641                } else if fragment_layout.size == var_layout.size {
642                    // Fragment covers entire variable, so as far as
643                    // DWARF is concerned, it's not really a fragment.
644                    None
645                } else {
646                    Some(direct_offset..direct_offset + fragment_layout.size)
647                }
648            } else {
649                None
650            };
651
652            match var.value {
653                mir::VarDebugInfoContents::Place(place) => {
654                    per_local[place.local].push(PerLocalVarDebugInfo {
655                        name: var.name,
656                        source_info: var.source_info,
657                        dbg_var,
658                        fragment,
659                        projection: place.projection,
660                    });
661                }
662                mir::VarDebugInfoContents::Const(c) => {
663                    if let Some(dbg_var) = dbg_var {
664                        let Some(dbg_loc) = self.dbg_loc(bx, var.source_info) else { continue };
665
666                        let operand = self.eval_mir_constant_to_operand(bx, &c);
667                        constants.push(ConstDebugInfo {
668                            name: var.name.to_string(),
669                            source_info: var.source_info,
670                            operand,
671                            dbg_var,
672                            dbg_loc,
673                            fragment,
674                            _phantom: PhantomData,
675                        });
676                    }
677                }
678            }
679        }
680        Some((per_local, constants))
681    }
682
683    pub(crate) fn codegen_stmt_debuginfo(
684        &mut self,
685        bx: &mut Bx,
686        debuginfo: &mir::StmtDebugInfo<'tcx>,
687    ) {
688        match debuginfo {
689            mir::StmtDebugInfo::AssignRef(dest, place) => {
690                let local_ref = match self.locals[place.local] {
691                    // For an rvalue like `&(_1.1)`, when `BackendRepr` is `BackendRepr::Memory`, we allocate a block of memory to this place.
692                    // The place is an indirect pointer, we can refer to it directly.
693                    LocalRef::Place(place_ref) => Some((place_ref, place.projection.as_slice())),
694                    // For an rvalue like `&((*_1).1)`, we are calculating the address of `_1.1`.
695                    // The deref projection is no-op here.
696                    LocalRef::Operand(operand_ref) if place.is_indirect_first_projection() => {
697                        Some((operand_ref.deref(bx.cx()), &place.projection[1..]))
698                    }
699                    // For an rvalue like `&1`, when `BackendRepr` is `BackendRepr::Scalar`,
700                    // we cannot get the address.
701                    // N.B. `non_ssa_locals` returns that this is an SSA local.
702                    LocalRef::Operand(_) => None,
703                    LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => None,
704                }
705                .filter(|(_, projection)| {
706                    // Drop unsupported projections.
707                    projection.iter().all(|p| p.can_use_in_debuginfo())
708                });
709                if let Some((base, projection)) = local_ref {
710                    self.debug_new_val_to_local(bx, *dest, base, projection);
711                } else {
712                    // If the address cannot be calculated, use poison to indicate that the value has been optimized out.
713                    self.debug_poison_to_local(bx, *dest);
714                }
715            }
716            mir::StmtDebugInfo::InvalidAssign(local) => {
717                self.debug_poison_to_local(bx, *local);
718            }
719        }
720    }
721
722    pub(crate) fn codegen_stmt_debuginfos(
723        &mut self,
724        bx: &mut Bx,
725        debuginfos: &[mir::StmtDebugInfo<'tcx>],
726    ) {
727        for debuginfo in debuginfos {
728            self.codegen_stmt_debuginfo(bx, debuginfo);
729        }
730    }
731
732    /// Creates the function-specific debug context.
733    ///
734    /// Returns the FunctionDebugContext for the function which holds state needed
735    /// for debug info creation, if it is enabled.
736    pub(super) fn fill_function_debug_context(&mut self, bx: &mut Bx) {
737        if self.cx.sess().opts.debuginfo == DebugInfo::None {
738            return;
739        }
740
741        // Initialize fn debug context (including scopes).
742        self.debug_context = Some(FunctionDebugContext {
743            scopes: IndexVec::with_capacity(self.mir.source_scopes.len()),
744            inlined_function_scopes: Default::default(),
745        });
746
747        // Find all scopes with variables defined in them.
748        let variables = if self.cx.sess().opts.debuginfo == DebugInfo::Full {
749            let mut vars = DenseBitSet::new_empty(self.mir.source_scopes.len());
750            // FIXME(eddyb) take into account that arguments always have debuginfo,
751            // irrespective of their name (assuming full debuginfo is enabled).
752            // NOTE(eddyb) actually, on second thought, those are always in the
753            // function scope, which always exists.
754            for var_debug_info in &self.mir.var_debug_info {
755                vars.insert(var_debug_info.source_info.scope);
756            }
757            Some(vars)
758        } else {
759            // Nothing to emit, of course.
760            None
761        };
762
763        // Instantiate all scopes.
764        let mut discriminators = FxHashMap::default();
765        for scope in self.mir.source_scopes.indices() {
766            let scope_data = self.make_mir_scope(bx, &variables, &mut discriminators, scope);
767            let _s = self.debug_context.as_mut().unwrap().scopes.push(scope_data);
768            if true {
    {
        match (&_s, &scope) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(_s, scope);
769        }
770    }
771
772    fn make_mir_scope(
773        &mut self,
774        bx: &mut Bx,
775        variables: &Option<DenseBitSet<mir::SourceScope>>,
776        discriminators: &mut FxHashMap<BytePos, u32>,
777        scope: mir::SourceScope,
778    ) -> DebugScope<Bx::DIScope, Bx::DILocation> {
779        let scope_data = &self.mir.source_scopes[scope];
780        let parent_scope = if let Some(parent) = scope_data.parent_scope {
781            if true {
    if !(parent.as_u32() < scope.as_u32()) {
        ::core::panicking::panic("assertion failed: parent.as_u32() < scope.as_u32()")
    };
};debug_assert!(parent.as_u32() < scope.as_u32());
782            self.debug_context.as_ref().unwrap().scopes[parent]
783        } else {
784            // The root is the function itself.
785            let file = self.cx.sess().source_map().lookup_source_file(self.mir.span.lo());
786            let dbg_scope = bx.dbg_scope_fn(self.instance, self.fn_abi, Some(self.llfn));
787            return DebugScope {
788                dbg_scope,
789                inlined_at: None,
790                file_start_pos: file.start_pos,
791                file_end_pos: file.end_position(),
792            };
793        };
794
795        if let Some(vars) = variables
796            && !vars.contains(scope)
797            && scope_data.inlined.is_none()
798        {
799            // Do not create a DIScope if there are no variables defined in this
800            // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
801            return parent_scope;
802        }
803
804        let dbg_scope = match scope_data.inlined {
805            Some((callee, _)) => {
806                let callee = self.monomorphize(callee);
807                *self
808                    .debug_context
809                    .as_mut()
810                    .unwrap()
811                    .inlined_function_scopes
812                    .entry(callee)
813                    .or_insert_with(|| {
814                        let callee_fn_abi = self.cx.fn_abi_of_instance(callee, ty::List::empty());
815                        bx.dbg_scope_fn(callee, callee_fn_abi, None)
816                    })
817            }
818            None => bx.dbg_create_lexical_block(scope_data.span.lo(), parent_scope.dbg_scope),
819        };
820
821        let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
822            let callsite_span = hygiene::walk_chain_collapsed(callsite_span, self.mir.span);
823            let callsite_scope = parent_scope.adjust_dbg_scope_for_span(bx, callsite_span);
824            let loc = bx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span);
825
826            // NB: In order to produce proper debug info for variables (particularly
827            // arguments) in multiply-inlined functions, LLVM expects to see a single
828            // DILocalVariable with multiple different DILocations in the IR. While
829            // the source information for each DILocation would be identical, their
830            // inlinedAt attributes will be unique to the particular callsite.
831            //
832            // We generate DILocations here based on the callsite's location in the
833            // source code. A single location in the source code usually can't
834            // produce multiple distinct calls so this mostly works, until
835            // macros get involved. A macro can generate multiple calls
836            // at the same span, which breaks the assumption that we're going to
837            // produce a unique DILocation for every scope we process here. We
838            // have to explicitly add discriminators if we see inlines into the
839            // same source code location.
840            //
841            // Note further that we can't key this hashtable on the span itself,
842            // because these spans could have distinct SyntaxContexts. We have
843            // to key on exactly what we're giving to LLVM.
844            match discriminators.entry(callsite_span.lo()) {
845                Entry::Occupied(mut o) => {
846                    *o.get_mut() += 1;
847                    // NB: We have to emit *something* here or we'll fail LLVM IR verification
848                    // in at least some circumstances (see issue #135322) so if the required
849                    // discriminant cannot be encoded fall back to the dummy location.
850                    bx.dbg_location_clone_with_discriminator(loc, *o.get()).unwrap_or_else(|| {
851                        bx.dbg_loc(callsite_scope, parent_scope.inlined_at, DUMMY_SP)
852                    })
853                }
854                Entry::Vacant(v) => {
855                    v.insert(0);
856                    loc
857                }
858            }
859        });
860
861        let file = self.cx.sess().source_map().lookup_source_file(scope_data.span.lo());
862        DebugScope {
863            dbg_scope,
864            inlined_at: inlined_at.or(parent_scope.inlined_at),
865            file_start_pos: file.start_pos,
866            file_end_pos: file.end_position(),
867        }
868    }
869}