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 pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
23
24 pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
26}
27
28#[derive(Copy, Clone)]
29pub enum VariableKind {
30 ArgumentVariable(usize ),
31 LocalVariable,
32}
33
34#[derive(Clone)]
36pub struct PerLocalVarDebugInfo<'tcx, D> {
37 pub name: Symbol,
38 pub source_info: mir::SourceInfo,
39
40 pub dbg_var: Option<D>,
42
43 pub fragment: Option<Range<Size>>,
46
47 pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
49}
50
51pub 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 pub inlined_at: Option<L>,
68
69 pub file_start_pos: BytePos,
72 pub file_end_pos: BytePos,
73}
74
75impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
76 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 direct_offset: Size,
157 indirect_offsets: Vec<Size>,
160 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 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 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 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 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 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 if arg_index == 0 && has_proj() {
321 None
325 } else if whole_local_var.is_some() {
326 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 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 bx.set_var_name(a, &(name.clone() + ".0"));
386 bx.set_var_name(b, &(name.clone() + ".1"));
387 }
388 OperandValue::ZeroSized => {
389 }
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 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 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 let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
448 && self.mir.local_kind(local) == mir::LocalKind::Arg
449 && (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 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 bx.store_to_place(place.val.llval, alloca.val);
466
467 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 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 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 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 continue;
598 } else if fragment_layout.size == var_layout.size {
599 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}