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