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_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                Self::spill_operand_to_stack(*operand, name, bx)
412            }
413
414            LocalRef::Place(place) => *place,
415
416            // FIXME(eddyb) add debuginfo for unsized places too.
417            LocalRef::UnsizedPlace(_) => return,
418        };
419
420        let vars = vars.iter().cloned().chain(fallback_var);
421
422        for var in vars {
423            self.debug_introduce_local_as_var(bx, local, base, var);
424        }
425    }
426
427    fn debug_introduce_local_as_var(
428        &self,
429        bx: &mut Bx,
430        local: mir::Local,
431        base: PlaceRef<'tcx, Bx::Value>,
432        var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
433    ) {
434        let Some(dbg_var) = var.dbg_var else { return };
435        let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
436
437        let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
438            calculate_debuginfo_offset(bx, var.projection, base.layout);
439
440        // When targeting MSVC, create extra allocas for arguments instead of pointing multiple
441        // dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
442        // not DWARF and LLVM doesn't support translating the resulting
443        // [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
444        // Creating extra allocas on the stack makes the resulting debug info simple enough
445        // that LLVM can generate correct CodeView records and thus the values appear in the
446        // debugger. (#83709)
447        let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
448            && self.mir.local_kind(local) == mir::LocalKind::Arg
449            // LLVM can handle simple things but anything more complex than just a direct
450            // offset or one indirect offset of 0 is too complex for it to generate CV records
451            // correctly.
452            && (direct_offset != Size::ZERO || !matches!(&indirect_offsets[..], [Size::ZERO] | []));
453
454        if should_create_individual_allocas {
455            let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
456                calculate_debuginfo_offset(bx, var.projection, base);
457
458            // Create a variable which will be a pointer to the actual value
459            let ptr_ty = Ty::new_mut_ptr(bx.tcx(), place.layout.ty);
460            let ptr_layout = bx.layout_of(ptr_ty);
461            let alloca = PlaceRef::alloca(bx, ptr_layout);
462            bx.set_var_name(alloca.val.llval, &(var.name.to_string() + ".dbg.spill"));
463
464            // Write the pointer to the variable
465            bx.store_to_place(place.val.llval, alloca.val);
466
467            // Point the debug info to `*alloca` for the current variable
468            bx.dbg_var_addr(
469                dbg_var,
470                dbg_loc,
471                alloca.val.llval,
472                Size::ZERO,
473                &[Size::ZERO],
474                &var.fragment,
475            );
476        } else {
477            bx.dbg_var_addr(
478                dbg_var,
479                dbg_loc,
480                base.val.llval,
481                direct_offset,
482                &indirect_offsets,
483                &var.fragment,
484            );
485        }
486    }
487
488    pub(crate) fn debug_introduce_locals(
489        &self,
490        bx: &mut Bx,
491        consts: Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
492    ) {
493        if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
494            for local in self.locals.indices() {
495                self.debug_introduce_local(bx, local);
496            }
497
498            for ConstDebugInfo { name, source_info, operand, dbg_var, dbg_loc, fragment, .. } in
499                consts.into_iter()
500            {
501                self.set_debug_loc(bx, source_info);
502                let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
503                bx.clear_dbg_loc();
504
505                bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], &fragment);
506            }
507        }
508    }
509
510    /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
511    pub(crate) fn compute_per_local_var_debug_info(
512        &self,
513        bx: &mut Bx,
514    ) -> Option<(
515        PerLocalVarDebugInfoIndexVec<'tcx, Bx::DIVariable>,
516        Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
517    )> {
518        let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
519
520        let target_is_msvc = self.cx.sess().target.is_like_msvc;
521
522        if !full_debug_info && self.cx.sess().fewer_names() {
523            return None;
524        }
525
526        let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
527        let mut constants = vec![];
528        let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default();
529        for var in &self.mir.var_debug_info {
530            let dbg_scope_and_span = if full_debug_info {
531                self.adjusted_span_and_dbg_scope(var.source_info)
532            } else {
533                None
534            };
535
536            let var_ty = if let Some(ref fragment) = var.composite {
537                self.monomorphize(fragment.ty)
538            } else {
539                match var.value {
540                    mir::VarDebugInfoContents::Place(place) => {
541                        self.monomorphized_place_ty(place.as_ref())
542                    }
543                    mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
544                }
545            };
546
547            let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
548                let var_kind = if let Some(arg_index) = var.argument_index
549                    && var.composite.is_none()
550                    && let mir::VarDebugInfoContents::Place(place) = var.value
551                    && place.projection.is_empty()
552                {
553                    let arg_index = arg_index as usize;
554                    if target_is_msvc {
555                        // ScalarPair parameters are spilled to the stack so they need to
556                        // be marked as a `LocalVariable` for MSVC debuggers to visualize
557                        // their data correctly. (See #81894 & #88625)
558                        let var_ty_layout = self.cx.layout_of(var_ty);
559                        if let BackendRepr::ScalarPair(_, _) = var_ty_layout.backend_repr {
560                            VariableKind::LocalVariable
561                        } else {
562                            VariableKind::ArgumentVariable(arg_index)
563                        }
564                    } else {
565                        // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
566                        // offset in closures to account for the hidden environment?
567                        VariableKind::ArgumentVariable(arg_index)
568                    }
569                } else {
570                    VariableKind::LocalVariable
571                };
572
573                if let VariableKind::ArgumentVariable(arg_index) = var_kind {
574                    match params_seen.entry((dbg_scope, arg_index)) {
575                        Entry::Occupied(o) => o.get().clone(),
576                        Entry::Vacant(v) => v
577                            .insert(
578                                self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span),
579                            )
580                            .clone(),
581                    }
582                } else {
583                    self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
584                }
585            });
586
587            let fragment = if let Some(ref fragment) = var.composite {
588                let var_layout = self.cx.layout_of(var_ty);
589
590                let DebugInfoOffset { direct_offset, indirect_offsets, result: fragment_layout } =
591                    calculate_debuginfo_offset(bx, &fragment.projection, var_layout);
592                assert!(indirect_offsets.is_empty());
593
594                if fragment_layout.size == Size::ZERO {
595                    // Fragment is a ZST, so does not represent anything. Avoid generating anything
596                    // as this may conflict with a fragment that covers the entire variable.
597                    continue;
598                } else if fragment_layout.size == var_layout.size {
599                    // Fragment covers entire variable, so as far as
600                    // DWARF is concerned, it's not really a fragment.
601                    None
602                } else {
603                    Some(direct_offset..direct_offset + fragment_layout.size)
604                }
605            } else {
606                None
607            };
608
609            match var.value {
610                mir::VarDebugInfoContents::Place(place) => {
611                    per_local[place.local].push(PerLocalVarDebugInfo {
612                        name: var.name,
613                        source_info: var.source_info,
614                        dbg_var,
615                        fragment,
616                        projection: place.projection,
617                    });
618                }
619                mir::VarDebugInfoContents::Const(c) => {
620                    if let Some(dbg_var) = dbg_var {
621                        let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
622
623                        let operand = self.eval_mir_constant_to_operand(bx, &c);
624                        constants.push(ConstDebugInfo {
625                            name: var.name.to_string(),
626                            source_info: var.source_info,
627                            operand,
628                            dbg_var,
629                            dbg_loc,
630                            fragment,
631                            _phantom: PhantomData,
632                        });
633                    }
634                }
635            }
636        }
637        Some((per_local, constants))
638    }
639}