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, kw};
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_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
259 let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
260
261 let vars = match &self.per_local_var_debug_info {
262 Some(per_local) => &per_local[local],
263 None => return,
264 };
265 let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
266 let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
267
268 let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
269 let arg_index = local.index() - 1;
270
271 if arg_index == 0 && has_proj() {
274 None
278 } else if whole_local_var.is_some() {
279 None
285 } else {
286 let name = kw::Empty;
287 let decl = &self.mir.local_decls[local];
288 let dbg_var = if full_debug_info {
289 self.adjusted_span_and_dbg_scope(decl.source_info).map(
290 |(dbg_scope, _, span)| {
291 let kind = VariableKind::ArgumentVariable(arg_index + 1);
293
294 let arg_ty = self.monomorphize(decl.ty);
295
296 self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
297 },
298 )
299 } else {
300 None
301 };
302
303 Some(PerLocalVarDebugInfo {
304 name,
305 source_info: decl.source_info,
306 dbg_var,
307 fragment: None,
308 projection: ty::List::empty(),
309 })
310 }
311 } else {
312 None
313 };
314
315 let local_ref = &self.locals[local];
316
317 let name = if bx.sess().fewer_names() {
318 None
319 } else {
320 Some(match whole_local_var.or(fallback_var.clone()) {
321 Some(var) if var.name != kw::Empty => var.name.to_string(),
322 _ => format!("{local:?}"),
323 })
324 };
325
326 if let Some(name) = &name {
327 match local_ref {
328 LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
329 bx.set_var_name(place.val.llval, name);
330 }
331 LocalRef::Operand(operand) => match operand.val {
332 OperandValue::Ref(PlaceValue { llval: x, .. }) | OperandValue::Immediate(x) => {
333 bx.set_var_name(x, name);
334 }
335 OperandValue::Pair(a, b) => {
336 bx.set_var_name(a, &(name.clone() + ".0"));
339 bx.set_var_name(b, &(name.clone() + ".1"));
340 }
341 OperandValue::ZeroSized => {
342 }
344 },
345 LocalRef::PendingOperand => {}
346 }
347 }
348
349 if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
350 return;
351 }
352
353 let base = match local_ref {
354 LocalRef::PendingOperand => return,
355
356 LocalRef::Operand(operand) => {
357 let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id());
360 if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
361 return;
362 }
363
364 Self::spill_operand_to_stack(*operand, name, bx)
365 }
366
367 LocalRef::Place(place) => *place,
368
369 LocalRef::UnsizedPlace(_) => return,
371 };
372
373 let vars = vars.iter().cloned().chain(fallback_var);
374
375 for var in vars {
376 self.debug_introduce_local_as_var(bx, local, base, var);
377 }
378 }
379
380 fn debug_introduce_local_as_var(
381 &self,
382 bx: &mut Bx,
383 local: mir::Local,
384 base: PlaceRef<'tcx, Bx::Value>,
385 var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
386 ) {
387 let Some(dbg_var) = var.dbg_var else { return };
388 let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
389
390 let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
391 calculate_debuginfo_offset(bx, var.projection, base.layout);
392
393 let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
401 && self.mir.local_kind(local) == mir::LocalKind::Arg
402 && (direct_offset != Size::ZERO || !matches!(&indirect_offsets[..], [Size::ZERO] | []));
406
407 if should_create_individual_allocas {
408 let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
409 calculate_debuginfo_offset(bx, var.projection, base);
410
411 let ptr_ty = Ty::new_mut_ptr(bx.tcx(), place.layout.ty);
413 let ptr_layout = bx.layout_of(ptr_ty);
414 let alloca = PlaceRef::alloca(bx, ptr_layout);
415 bx.set_var_name(alloca.val.llval, &(var.name.to_string() + ".dbg.spill"));
416
417 bx.store_to_place(place.val.llval, alloca.val);
419
420 bx.dbg_var_addr(
422 dbg_var,
423 dbg_loc,
424 alloca.val.llval,
425 Size::ZERO,
426 &[Size::ZERO],
427 var.fragment,
428 );
429 } else {
430 bx.dbg_var_addr(
431 dbg_var,
432 dbg_loc,
433 base.val.llval,
434 direct_offset,
435 &indirect_offsets,
436 var.fragment,
437 );
438 }
439 }
440
441 pub(crate) fn debug_introduce_locals(
442 &self,
443 bx: &mut Bx,
444 consts: Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
445 ) {
446 if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
447 for local in self.locals.indices() {
448 self.debug_introduce_local(bx, local);
449 }
450
451 for ConstDebugInfo { name, source_info, operand, dbg_var, dbg_loc, fragment, .. } in
452 consts.into_iter()
453 {
454 self.set_debug_loc(bx, source_info);
455 let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
456 bx.clear_dbg_loc();
457
458 bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment);
459 }
460 }
461 }
462
463 pub(crate) fn compute_per_local_var_debug_info(
465 &self,
466 bx: &mut Bx,
467 ) -> Option<(
468 PerLocalVarDebugInfoIndexVec<'tcx, Bx::DIVariable>,
469 Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
470 )> {
471 let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
472
473 let target_is_msvc = self.cx.sess().target.is_like_msvc;
474
475 if !full_debug_info && self.cx.sess().fewer_names() {
476 return None;
477 }
478
479 let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
480 let mut constants = vec![];
481 let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default();
482 for var in &self.mir.var_debug_info {
483 let dbg_scope_and_span = if full_debug_info {
484 self.adjusted_span_and_dbg_scope(var.source_info)
485 } else {
486 None
487 };
488
489 let var_ty = if let Some(ref fragment) = var.composite {
490 self.monomorphize(fragment.ty)
491 } else {
492 match var.value {
493 mir::VarDebugInfoContents::Place(place) => {
494 self.monomorphized_place_ty(place.as_ref())
495 }
496 mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
497 }
498 };
499
500 let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
501 let var_kind = if let Some(arg_index) = var.argument_index
502 && var.composite.is_none()
503 && let mir::VarDebugInfoContents::Place(place) = var.value
504 && place.projection.is_empty()
505 {
506 let arg_index = arg_index as usize;
507 if target_is_msvc {
508 let var_ty_layout = self.cx.layout_of(var_ty);
512 if let BackendRepr::ScalarPair(_, _) = var_ty_layout.backend_repr {
513 VariableKind::LocalVariable
514 } else {
515 VariableKind::ArgumentVariable(arg_index)
516 }
517 } else {
518 VariableKind::ArgumentVariable(arg_index)
521 }
522 } else {
523 VariableKind::LocalVariable
524 };
525
526 if let VariableKind::ArgumentVariable(arg_index) = var_kind {
527 match params_seen.entry((dbg_scope, arg_index)) {
528 Entry::Occupied(o) => o.get().clone(),
529 Entry::Vacant(v) => v
530 .insert(
531 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span),
532 )
533 .clone(),
534 }
535 } else {
536 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
537 }
538 });
539
540 let fragment = if let Some(ref fragment) = var.composite {
541 let var_layout = self.cx.layout_of(var_ty);
542
543 let DebugInfoOffset { direct_offset, indirect_offsets, result: fragment_layout } =
544 calculate_debuginfo_offset(bx, &fragment.projection, var_layout);
545 assert!(indirect_offsets.is_empty());
546
547 if fragment_layout.size == Size::ZERO {
548 continue;
551 } else if fragment_layout.size == var_layout.size {
552 None
555 } else {
556 Some(direct_offset..direct_offset + fragment_layout.size)
557 }
558 } else {
559 None
560 };
561
562 match var.value {
563 mir::VarDebugInfoContents::Place(place) => {
564 per_local[place.local].push(PerLocalVarDebugInfo {
565 name: var.name,
566 source_info: var.source_info,
567 dbg_var,
568 fragment,
569 projection: place.projection,
570 });
571 }
572 mir::VarDebugInfoContents::Const(c) => {
573 if let Some(dbg_var) = dbg_var {
574 let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
575
576 let operand = self.eval_mir_constant_to_operand(bx, &c);
577 constants.push(ConstDebugInfo {
578 name: var.name.to_string(),
579 source_info: var.source_info,
580 operand,
581 dbg_var,
582 dbg_loc,
583 fragment,
584 _phantom: PhantomData,
585 });
586 }
587 }
588 }
589 }
590 Some((per_local, constants))
591 }
592}