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, ScalableElt, Size, VariantIdx};
6use rustc_data_structures::fx::FxHashMap;
7use rustc_index::IndexVec;
8use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
9use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
10use rustc_middle::ty::{Instance, Ty};
11use rustc_middle::{bug, mir, ty};
12use rustc_session::config::DebugInfo;
13use rustc_span::{BytePos, Span, Symbol, hygiene, sym};
14
15use super::operand::{OperandRef, OperandValue};
16use super::place::{PlaceRef, PlaceValue};
17use super::{FunctionCx, LocalRef, PerLocalVarDebugInfoIndexVec};
18use crate::traits::*;
19
20pub struct FunctionDebugContext<'tcx, S, L> {
21    /// Maps from source code to the corresponding debug info scope.
22    pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
23
24    /// Maps from an inlined function to its debug info declaration.
25    pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
26}
27
28#[derive(Copy, Clone)]
29pub enum VariableKind {
30    ArgumentVariable(usize /*index*/),
31    LocalVariable,
32}
33
34/// Like `mir::VarDebugInfo`, but within a `mir::Local`.
35#[derive(Clone)]
36pub struct PerLocalVarDebugInfo<'tcx, D> {
37    pub name: Symbol,
38    pub source_info: mir::SourceInfo,
39
40    /// `DIVariable` returned by `create_dbg_var`.
41    pub dbg_var: Option<D>,
42
43    /// Byte range in the `dbg_var` covered by this fragment,
44    /// if this is a fragment of a composite `VarDebugInfo`.
45    pub fragment: Option<Range<Size>>,
46
47    /// `.place.projection` from `mir::VarDebugInfo`.
48    pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
49}
50
51/// Information needed to emit a constant.
52pub struct ConstDebugInfo<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
53    pub name: String,
54    pub source_info: mir::SourceInfo,
55    pub operand: OperandRef<'tcx, Bx::Value>,
56    pub dbg_var: Bx::DIVariable,
57    pub dbg_loc: Bx::DILocation,
58    pub fragment: Option<Range<Size>>,
59    pub _phantom: PhantomData<&'a ()>,
60}
61
62#[derive(Clone, Copy, Debug)]
63pub struct DebugScope<S, L> {
64    pub dbg_scope: S,
65
66    /// Call site location, if this scope was inlined from another function.
67    pub inlined_at: Option<L>,
68
69    // Start and end offsets of the file to which this DIScope belongs.
70    // These are used to quickly determine whether some span refers to the same file.
71    pub file_start_pos: BytePos,
72    pub file_end_pos: BytePos,
73}
74
75impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
76    /// DILocations inherit source file name from the parent DIScope. Due to macro expansions
77    /// it may so happen that the current span belongs to a different file than the DIScope
78    /// corresponding to span's containing source scope. If so, we need to create a DIScope
79    /// "extension" into that file.
80    pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>(
81        &self,
82        cx: &Cx,
83        span: Span,
84    ) -> S {
85        let pos = span.lo();
86        if pos < self.file_start_pos || pos >= self.file_end_pos {
87            let sm = cx.sess().source_map();
88            cx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file)
89        } else {
90            self.dbg_scope
91        }
92    }
93}
94
95trait DebugInfoOffsetLocation<'tcx, Bx> {
96    fn deref(&self, bx: &mut Bx) -> Self;
97    fn layout(&self) -> TyAndLayout<'tcx>;
98    fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self;
99    fn project_constant_index(&self, bx: &mut Bx, offset: u64) -> Self;
100    fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self;
101}
102
103impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
104    for PlaceRef<'tcx, Bx::Value>
105{
106    fn deref(&self, bx: &mut Bx) -> Self {
107        bx.load_operand(*self).deref(bx.cx())
108    }
109
110    fn layout(&self) -> TyAndLayout<'tcx> {
111        self.layout
112    }
113
114    fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self {
115        PlaceRef::project_field(*self, bx, field.index())
116    }
117
118    fn project_constant_index(&self, bx: &mut Bx, offset: u64) -> Self {
119        let lloffset = bx.cx().const_usize(offset);
120        self.project_index(bx, lloffset)
121    }
122
123    fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
124        self.project_downcast(bx, variant)
125    }
126}
127
128impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
129    for TyAndLayout<'tcx>
130{
131    fn deref(&self, bx: &mut Bx) -> Self {
132        bx.cx().layout_of(
133            self.ty.builtin_deref(true).unwrap_or_else(|| bug!("cannot deref `{}`", self.ty)),
134        )
135    }
136
137    fn layout(&self) -> TyAndLayout<'tcx> {
138        *self
139    }
140
141    fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self {
142        self.field(bx.cx(), field.index())
143    }
144
145    fn project_constant_index(&self, bx: &mut Bx, index: u64) -> Self {
146        self.field(bx.cx(), index as usize)
147    }
148
149    fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
150        self.for_variant(bx.cx(), variant)
151    }
152}
153
154struct DebugInfoOffset<T> {
155    /// Offset from the `base` used to calculate the debuginfo offset.
156    direct_offset: Size,
157    /// Each offset in this vector indicates one level of indirection from the base or previous
158    /// indirect offset plus a dereference.
159    indirect_offsets: Vec<Size>,
160    /// The final location debuginfo should point to.
161    result: T,
162}
163
164fn calculate_debuginfo_offset<
165    'a,
166    'tcx,
167    Bx: BuilderMethods<'a, 'tcx>,
168    L: DebugInfoOffsetLocation<'tcx, Bx>,
169>(
170    bx: &mut Bx,
171    projection: &[mir::PlaceElem<'tcx>],
172    base: L,
173) -> DebugInfoOffset<L> {
174    let mut direct_offset = Size::ZERO;
175    // FIXME(eddyb) use smallvec here.
176    let mut indirect_offsets = vec![];
177    let mut place = base;
178
179    for elem in projection {
180        match *elem {
181            mir::ProjectionElem::Deref => {
182                indirect_offsets.push(Size::ZERO);
183                place = place.deref(bx);
184            }
185            mir::ProjectionElem::Field(field, _) => {
186                let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
187                *offset += place.layout().fields.offset(field.index());
188                place = place.project_field(bx, field);
189            }
190            mir::ProjectionElem::Downcast(_, variant) => {
191                place = place.downcast(bx, variant);
192            }
193            mir::ProjectionElem::ConstantIndex {
194                offset: index,
195                min_length: _,
196                from_end: false,
197            } => {
198                let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
199                let FieldsShape::Array { stride, count: _ } = place.layout().fields else {
200                    bug!("ConstantIndex on non-array type {:?}", place.layout())
201                };
202                *offset += stride * index;
203                place = place.project_constant_index(bx, index);
204            }
205            _ => {
206                // Sanity check for `can_use_in_debuginfo`.
207                assert!(!elem.can_use_in_debuginfo());
208                bug!("unsupported var debuginfo projection `{:?}`", projection)
209            }
210        }
211    }
212
213    DebugInfoOffset { direct_offset, indirect_offsets, result: place }
214}
215
216impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
217    pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
218        bx.set_span(source_info.span);
219        if let Some(dbg_loc) = self.dbg_loc(source_info) {
220            bx.set_dbg_loc(dbg_loc);
221        }
222    }
223
224    fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option<Bx::DILocation> {
225        let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?;
226        Some(self.cx.dbg_loc(dbg_scope, inlined_at, span))
227    }
228
229    fn adjusted_span_and_dbg_scope(
230        &self,
231        source_info: mir::SourceInfo,
232    ) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
233        let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
234        let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span);
235        Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
236    }
237
238    fn spill_operand_to_stack(
239        operand: OperandRef<'tcx, Bx::Value>,
240        name: Option<String>,
241        bx: &mut Bx,
242    ) -> PlaceRef<'tcx, Bx::Value> {
243        // "Spill" the value onto the stack, for debuginfo,
244        // without forcing non-debuginfo uses of the local
245        // to also load from the stack every single time.
246        // FIXME(#68817) use `llvm.dbg.value` instead,
247        // at least for the cases which LLVM handles correctly.
248        let spill_slot = PlaceRef::alloca(bx, operand.layout);
249        if let Some(name) = name {
250            bx.set_var_name(spill_slot.val.llval, &(name + ".dbg.spill"));
251        }
252        operand.val.store(bx, spill_slot);
253        spill_slot
254    }
255
256    // Indicates that local is set to a new value. The `layout` and `projection` are used to
257    // calculate the offset.
258    pub(crate) fn debug_new_val_to_local(
259        &self,
260        bx: &mut Bx,
261        local: mir::Local,
262        base: PlaceRef<'tcx, Bx::Value>,
263        projection: &[mir::PlaceElem<'tcx>],
264    ) {
265        let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
266        if !full_debug_info {
267            return;
268        }
269
270        let vars = match &self.per_local_var_debug_info {
271            Some(per_local) => &per_local[local],
272            None => return,
273        };
274
275        let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
276            calculate_debuginfo_offset(bx, projection, base.layout);
277        for var in vars.iter() {
278            let Some(dbg_var) = var.dbg_var else {
279                continue;
280            };
281            let Some(dbg_loc) = self.dbg_loc(var.source_info) else {
282                continue;
283            };
284            bx.dbg_var_value(
285                dbg_var,
286                dbg_loc,
287                base.val.llval,
288                direct_offset,
289                &indirect_offsets,
290                &var.fragment,
291            );
292        }
293    }
294
295    pub(crate) fn debug_poison_to_local(&self, bx: &mut Bx, local: mir::Local) {
296        let ty = self.monomorphize(self.mir.local_decls[local].ty);
297        let layout = bx.cx().layout_of(ty);
298        let to_backend_ty = bx.cx().immediate_backend_type(layout);
299        let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
300        self.debug_new_val_to_local(bx, local, place_ref, &[]);
301    }
302
303    /// Apply debuginfo and/or name, after creating the `alloca` for a local,
304    /// or initializing the local with an operand (whichever applies).
305    pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
306        let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
307
308        let vars = match &self.per_local_var_debug_info {
309            Some(per_local) => &per_local[local],
310            None => return,
311        };
312        let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
313        let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
314
315        let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
316            let arg_index = local.index() - 1;
317
318            // Add debuginfo even to unnamed arguments.
319            // FIXME(eddyb) is this really needed?
320            if arg_index == 0 && has_proj() {
321                // Hide closure environments from debuginfo.
322                // FIXME(eddyb) shouldn't `ArgumentVariable` indices
323                // be offset to account for the hidden environment?
324                None
325            } else if whole_local_var.is_some() {
326                // No need to make up anything, there is a `mir::VarDebugInfo`
327                // covering the whole local.
328                // FIXME(eddyb) take `whole_local_var.source_info.scope` into
329                // account, just in case it doesn't use `ArgumentVariable`
330                // (after #67586 gets fixed).
331                None
332            } else {
333                let name = sym::empty;
334                let decl = &self.mir.local_decls[local];
335                let dbg_var = if full_debug_info {
336                    self.adjusted_span_and_dbg_scope(decl.source_info).map(
337                        |(dbg_scope, _, span)| {
338                            // FIXME(eddyb) is this `+ 1` needed at all?
339                            let kind = VariableKind::ArgumentVariable(arg_index + 1);
340
341                            let arg_ty = self.monomorphize(decl.ty);
342
343                            self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
344                        },
345                    )
346                } else {
347                    None
348                };
349
350                Some(PerLocalVarDebugInfo {
351                    name,
352                    source_info: decl.source_info,
353                    dbg_var,
354                    fragment: None,
355                    projection: ty::List::empty(),
356                })
357            }
358        } else {
359            None
360        };
361
362        let local_ref = &self.locals[local];
363
364        let name = if bx.sess().fewer_names() {
365            None
366        } else {
367            Some(match whole_local_var.or_else(|| fallback_var.clone()) {
368                Some(var) if var.name != sym::empty => var.name.to_string(),
369                _ => format!("{local:?}"),
370            })
371        };
372
373        if let Some(name) = &name {
374            match local_ref {
375                LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
376                    bx.set_var_name(place.val.llval, name);
377                }
378                LocalRef::Operand(operand) => match operand.val {
379                    OperandValue::Ref(PlaceValue { llval: x, .. }) | OperandValue::Immediate(x) => {
380                        bx.set_var_name(x, name);
381                    }
382                    OperandValue::Pair(a, b) => {
383                        // FIXME(eddyb) these are scalar components,
384                        // maybe extract the high-level fields?
385                        bx.set_var_name(a, &(name.clone() + ".0"));
386                        bx.set_var_name(b, &(name.clone() + ".1"));
387                    }
388                    OperandValue::ZeroSized => {
389                        // These never have a value to talk about
390                    }
391                },
392                LocalRef::PendingOperand => {}
393            }
394        }
395
396        if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
397            return;
398        }
399
400        let base = match local_ref {
401            LocalRef::PendingOperand => return,
402
403            LocalRef::Operand(operand) => {
404                // Don't spill operands onto the stack in naked functions.
405                // See: https://github.com/rust-lang/rust/issues/42779
406                let attrs = bx.tcx().codegen_instance_attrs(self.instance.def);
407                if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
408                    return;
409                }
410
411                // Don't spill `<vscale x N x i1>` for `N != 16`:
412                //
413                // SVE predicates are only one bit for each byte in an SVE vector (which makes
414                // sense, the predicate only needs to keep track of whether a lane is
415                // enabled/disabled). i.e. a `<vscale x 16 x i8>` vector has a `<vscale x 16 x i1>`
416                // predicate type. `<vscale x 16 x i1>` corresponds to two bytes of storage,
417                // multiplied by the `vscale`, with one bit for each of the sixteen lanes.
418                //
419                // For a vector with fewer elements, such as `svint32_t`/`<vscale x 4 x i32>`,
420                // while only a `<vscale x 4 x i1>` predicate type would be strictly necessary,
421                // relevant intrinsics still take a `svbool_t`/`<vscale x 16 x i1>` - this is
422                // because a `<vscale x 4 x i1>` is only half of a byte (for `vscale=1`), and with
423                // memory being byte-addressable, it's unclear how to store that.
424                //
425                // Due to this, LLVM ultimately decided not to support stores of `<vscale x N x i1>`
426                // for `N != 16`. As for `vscale=1` and `N` fewer than sixteen, partial bytes would
427                // need to be stored (except for `N=8`, but that also isn't supported). `N` can
428                // never be greater than sixteen as that ends up larger than the 128-bit increment
429                // size.
430                //
431                // Internally, with an intrinsic operating on a `svint32_t`/`<vscale x 4 x i32>`
432                // (for example), the intrinsic takes the `svbool_t`/`<vscale x 16 x i1>` predicate
433                // and casts it to a `svbool4_t`/`<vscale x 4 x i1>`. Therefore, it's important that
434                // the `<vscale x 4 x i32>` never spills because that'll cause errors during
435                // instruction selection. Spilling to the stack to create debuginfo for these
436                // intermediate values must be avoided and won't degrade the debugging experience
437                // anyway.
438                if operand.layout.ty.is_scalable_vector()
439                    && bx.sess().target.arch == rustc_target::spec::Arch::AArch64
440                    && let ty::Adt(adt, args) = &operand.layout.ty.kind()
441                    && let Some(marker_type_field) =
442                        adt.non_enum_variant().fields.get(FieldIdx::from_u32(0))
443                {
444                    let marker_type = marker_type_field.ty(bx.tcx(), args);
445                    // i.e. `<vscale x N x i1>` when `N != 16`
446                    if let ty::Slice(element_ty) = marker_type.kind()
447                        && element_ty.is_bool()
448                        && adt.repr().scalable != Some(ScalableElt::ElementCount(16))
449                    {
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(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 || !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(vec![], &self.mir.local_decls);
570        let mut constants = 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(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(_, _) = var_ty_layout.backend_repr {
603                            VariableKind::LocalVariable
604                        } else {
605                            VariableKind::ArgumentVariable(arg_index)
606                        }
607                    } else {
608                        // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
609                        // offset in closures to account for the hidden environment?
610                        VariableKind::ArgumentVariable(arg_index)
611                    }
612                } else {
613                    VariableKind::LocalVariable
614                };
615
616                if let VariableKind::ArgumentVariable(arg_index) = var_kind {
617                    match params_seen.entry((dbg_scope, arg_index)) {
618                        Entry::Occupied(o) => o.get().clone(),
619                        Entry::Vacant(v) => v
620                            .insert(
621                                self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span),
622                            )
623                            .clone(),
624                    }
625                } else {
626                    self.cx.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                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(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}