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