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