]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use crate::traits::*; |
dfeec247 | 2 | use rustc_index::vec::IndexVec; |
3dfed10e | 3 | use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; |
ba9703b0 XL |
4 | use rustc_middle::mir; |
5 | use rustc_middle::ty; | |
f2b60f7d | 6 | use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; |
ba9703b0 | 7 | use rustc_session::config::DebugInfo; |
74b04a01 | 8 | use rustc_span::symbol::{kw, Symbol}; |
dfeec247 | 9 | use rustc_span::{BytePos, Span}; |
c295e0f8 | 10 | use rustc_target::abi::Abi; |
cdc7bbd5 | 11 | use rustc_target::abi::Size; |
e74abb32 | 12 | |
fc512014 | 13 | use super::operand::{OperandRef, OperandValue}; |
74b04a01 | 14 | use super::place::PlaceRef; |
dfeec247 | 15 | use super::{FunctionCx, LocalRef}; |
e74abb32 | 16 | |
29967ef6 XL |
17 | pub struct FunctionDebugContext<S, L> { |
18 | pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>, | |
e74abb32 XL |
19 | } |
20 | ||
21 | #[derive(Copy, Clone)] | |
22 | pub enum VariableKind { | |
23 | ArgumentVariable(usize /*index*/), | |
24 | LocalVariable, | |
25 | } | |
26 | ||
74b04a01 XL |
27 | /// Like `mir::VarDebugInfo`, but within a `mir::Local`. |
28 | #[derive(Copy, Clone)] | |
29 | pub struct PerLocalVarDebugInfo<'tcx, D> { | |
30 | pub name: Symbol, | |
31 | pub source_info: mir::SourceInfo, | |
32 | ||
33 | /// `DIVariable` returned by `create_dbg_var`. | |
34 | pub dbg_var: Option<D>, | |
35 | ||
36 | /// `.place.projection` from `mir::VarDebugInfo`. | |
37 | pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>, | |
38 | } | |
39 | ||
e74abb32 | 40 | #[derive(Clone, Copy, Debug)] |
29967ef6 | 41 | pub struct DebugScope<S, L> { |
04454e1e | 42 | pub dbg_scope: S, |
29967ef6 XL |
43 | |
44 | /// Call site location, if this scope was inlined from another function. | |
45 | pub inlined_at: Option<L>, | |
46 | ||
e74abb32 XL |
47 | // Start and end offsets of the file to which this DIScope belongs. |
48 | // These are used to quickly determine whether some span refers to the same file. | |
49 | pub file_start_pos: BytePos, | |
50 | pub file_end_pos: BytePos, | |
51 | } | |
52 | ||
29967ef6 XL |
53 | impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> { |
54 | /// DILocations inherit source file name from the parent DIScope. Due to macro expansions | |
55 | /// it may so happen that the current span belongs to a different file than the DIScope | |
56 | /// corresponding to span's containing source scope. If so, we need to create a DIScope | |
57 | /// "extension" into that file. | |
58 | pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>( | |
59 | &self, | |
60 | cx: &Cx, | |
61 | span: Span, | |
62 | ) -> S { | |
29967ef6 XL |
63 | let pos = span.lo(); |
64 | if pos < self.file_start_pos || pos >= self.file_end_pos { | |
65 | let sm = cx.sess().source_map(); | |
04454e1e | 66 | cx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file) |
29967ef6 | 67 | } else { |
04454e1e | 68 | self.dbg_scope |
29967ef6 | 69 | } |
e74abb32 XL |
70 | } |
71 | } | |
72 | ||
73 | impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | |
74b04a01 | 74 | pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) { |
29967ef6 XL |
75 | bx.set_span(source_info.span); |
76 | if let Some(dbg_loc) = self.dbg_loc(source_info) { | |
77 | bx.set_dbg_loc(dbg_loc); | |
e74abb32 XL |
78 | } |
79 | } | |
80 | ||
29967ef6 XL |
81 | fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option<Bx::DILocation> { |
82 | let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?; | |
83 | Some(self.cx.dbg_loc(dbg_scope, inlined_at, span)) | |
84 | } | |
85 | ||
86 | fn adjusted_span_and_dbg_scope( | |
87 | &self, | |
88 | source_info: mir::SourceInfo, | |
89 | ) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> { | |
90 | let span = self.adjust_span_for_debugging(source_info.span); | |
91 | let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]; | |
92 | Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span)) | |
93 | } | |
94 | ||
95 | /// In order to have a good line stepping behavior in debugger, we overwrite debug | |
f2b60f7d FG |
96 | /// locations of macro expansions with that of the outermost expansion site (when the macro is |
97 | /// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). | |
29967ef6 | 98 | fn adjust_span_for_debugging(&self, mut span: Span) -> Span { |
e74abb32 | 99 | // Bail out if debug info emission is not enabled. |
29967ef6 XL |
100 | if self.debug_context.is_none() { |
101 | return span; | |
e74abb32 XL |
102 | } |
103 | ||
f2b60f7d | 104 | if self.cx.tcx().should_collapse_debuginfo(span) { |
e74abb32 XL |
105 | // Walk up the macro expansion chain until we reach a non-expanded span. |
106 | // We also stop at the function body level because no line stepping can occur | |
107 | // at the level above that. | |
e74abb32 | 108 | // Use span of the outermost expansion site, while keeping the original lexical scope. |
29967ef6 | 109 | span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt()); |
e74abb32 | 110 | } |
e74abb32 | 111 | |
29967ef6 | 112 | span |
e74abb32 XL |
113 | } |
114 | ||
fc512014 XL |
115 | fn spill_operand_to_stack( |
116 | operand: &OperandRef<'tcx, Bx::Value>, | |
117 | name: Option<String>, | |
118 | bx: &mut Bx, | |
119 | ) -> PlaceRef<'tcx, Bx::Value> { | |
120 | // "Spill" the value onto the stack, for debuginfo, | |
121 | // without forcing non-debuginfo uses of the local | |
122 | // to also load from the stack every single time. | |
123 | // FIXME(#68817) use `llvm.dbg.value` instead, | |
124 | // at least for the cases which LLVM handles correctly. | |
125 | let spill_slot = PlaceRef::alloca(bx, operand.layout); | |
126 | if let Some(name) = name { | |
127 | bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill")); | |
128 | } | |
129 | operand.val.store(bx, spill_slot); | |
130 | spill_slot | |
131 | } | |
132 | ||
e74abb32 XL |
133 | /// Apply debuginfo and/or name, after creating the `alloca` for a local, |
134 | /// or initializing the local with an operand (whichever applies). | |
e74abb32 | 135 | pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { |
74b04a01 XL |
136 | let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; |
137 | ||
e74abb32 | 138 | // FIXME(eddyb) maybe name the return place as `_0` or `return`? |
f9f354fc XL |
139 | if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable() |
140 | { | |
e74abb32 XL |
141 | return; |
142 | } | |
143 | ||
144 | let vars = match &self.per_local_var_debug_info { | |
145 | Some(per_local) => &per_local[local], | |
146 | None => return, | |
147 | }; | |
74b04a01 XL |
148 | let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied(); |
149 | let has_proj = || vars.iter().any(|var| !var.projection.is_empty()); | |
e74abb32 | 150 | |
74b04a01 | 151 | let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg { |
e74abb32 XL |
152 | let arg_index = local.index() - 1; |
153 | ||
154 | // Add debuginfo even to unnamed arguments. | |
155 | // FIXME(eddyb) is this really needed? | |
74b04a01 | 156 | if arg_index == 0 && has_proj() { |
e74abb32 XL |
157 | // Hide closure environments from debuginfo. |
158 | // FIXME(eddyb) shouldn't `ArgumentVariable` indices | |
159 | // be offset to account for the hidden environment? | |
160 | None | |
74b04a01 XL |
161 | } else if whole_local_var.is_some() { |
162 | // No need to make up anything, there is a `mir::VarDebugInfo` | |
163 | // covering the whole local. | |
164 | // FIXME(eddyb) take `whole_local_var.source_info.scope` into | |
165 | // account, just in case it doesn't use `ArgumentVariable` | |
166 | // (after #67586 gets fixed). | |
167 | None | |
e74abb32 | 168 | } else { |
5869c6ff | 169 | let name = kw::Empty; |
74b04a01 | 170 | let decl = &self.mir.local_decls[local]; |
29967ef6 XL |
171 | let dbg_var = if full_debug_info { |
172 | self.adjusted_span_and_dbg_scope(decl.source_info).map( | |
173 | |(dbg_scope, _, span)| { | |
174 | // FIXME(eddyb) is this `+ 1` needed at all? | |
175 | let kind = VariableKind::ArgumentVariable(arg_index + 1); | |
176 | ||
fc512014 | 177 | let arg_ty = self.monomorphize(decl.ty); |
29967ef6 XL |
178 | |
179 | self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span) | |
180 | }, | |
181 | ) | |
74b04a01 | 182 | } else { |
29967ef6 | 183 | None |
74b04a01 | 184 | }; |
74b04a01 XL |
185 | |
186 | Some(PerLocalVarDebugInfo { | |
187 | name, | |
188 | source_info: decl.source_info, | |
189 | dbg_var, | |
190 | projection: ty::List::empty(), | |
e74abb32 | 191 | }) |
74b04a01 | 192 | } |
e74abb32 | 193 | } else { |
74b04a01 | 194 | None |
e74abb32 XL |
195 | }; |
196 | ||
197 | let local_ref = &self.locals[local]; | |
198 | ||
74b04a01 XL |
199 | let name = if bx.sess().fewer_names() { |
200 | None | |
201 | } else { | |
202 | Some(match whole_local_var.or(fallback_var) { | |
5869c6ff | 203 | Some(var) if var.name != kw::Empty => var.name.to_string(), |
e74abb32 | 204 | _ => format!("{:?}", local), |
74b04a01 XL |
205 | }) |
206 | }; | |
207 | ||
208 | if let Some(name) = &name { | |
e74abb32 | 209 | match local_ref { |
dfeec247 | 210 | LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => { |
74b04a01 | 211 | bx.set_var_name(place.llval, name); |
e74abb32 XL |
212 | } |
213 | LocalRef::Operand(Some(operand)) => match operand.val { | |
dfeec247 | 214 | OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => { |
74b04a01 | 215 | bx.set_var_name(x, name); |
e74abb32 XL |
216 | } |
217 | OperandValue::Pair(a, b) => { | |
218 | // FIXME(eddyb) these are scalar components, | |
219 | // maybe extract the high-level fields? | |
220 | bx.set_var_name(a, &(name.clone() + ".0")); | |
74b04a01 | 221 | bx.set_var_name(b, &(name.clone() + ".1")); |
e74abb32 | 222 | } |
dfeec247 | 223 | }, |
e74abb32 XL |
224 | LocalRef::Operand(None) => {} |
225 | } | |
226 | } | |
227 | ||
74b04a01 | 228 | if !full_debug_info || vars.is_empty() && fallback_var.is_none() { |
e74abb32 XL |
229 | return; |
230 | } | |
231 | ||
e74abb32 | 232 | let base = match local_ref { |
74b04a01 XL |
233 | LocalRef::Operand(None) => return, |
234 | ||
235 | LocalRef::Operand(Some(operand)) => { | |
3dfed10e XL |
236 | // Don't spill operands onto the stack in naked functions. |
237 | // See: https://github.com/rust-lang/rust/issues/42779 | |
238 | let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id()); | |
239 | if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { | |
240 | return; | |
241 | } | |
242 | ||
fc512014 | 243 | Self::spill_operand_to_stack(operand, name, bx) |
74b04a01 XL |
244 | } |
245 | ||
246 | LocalRef::Place(place) => *place, | |
247 | ||
248 | // FIXME(eddyb) add debuginfo for unsized places too. | |
249 | LocalRef::UnsizedPlace(_) => return, | |
e74abb32 XL |
250 | }; |
251 | ||
74b04a01 | 252 | let vars = vars.iter().copied().chain(fallback_var); |
e74abb32 XL |
253 | |
254 | for var in vars { | |
5e7ed085 FG |
255 | let Some(dbg_var) = var.dbg_var else { continue }; |
256 | let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue }; | |
29967ef6 | 257 | |
e74abb32 XL |
258 | let mut direct_offset = Size::ZERO; |
259 | // FIXME(eddyb) use smallvec here. | |
260 | let mut indirect_offsets = vec![]; | |
cdc7bbd5 | 261 | let mut place = base; |
e74abb32 | 262 | |
74b04a01 | 263 | for elem in &var.projection[..] { |
e74abb32 XL |
264 | match *elem { |
265 | mir::ProjectionElem::Deref => { | |
266 | indirect_offsets.push(Size::ZERO); | |
136023e0 | 267 | place = bx.load_operand(place).deref(bx.cx()); |
e74abb32 XL |
268 | } |
269 | mir::ProjectionElem::Field(field, _) => { | |
270 | let i = field.index(); | |
dfeec247 | 271 | let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset); |
cdc7bbd5 XL |
272 | *offset += place.layout.fields.offset(i); |
273 | place = place.project_field(bx, i); | |
e74abb32 XL |
274 | } |
275 | mir::ProjectionElem::Downcast(_, variant) => { | |
cdc7bbd5 | 276 | place = place.project_downcast(bx, variant); |
e74abb32 XL |
277 | } |
278 | _ => span_bug!( | |
279 | var.source_info.span, | |
280 | "unsupported var debuginfo place `{:?}`", | |
74b04a01 | 281 | mir::Place { local, projection: var.projection }, |
e74abb32 XL |
282 | ), |
283 | } | |
284 | } | |
285 | ||
cdc7bbd5 XL |
286 | // When targeting MSVC, create extra allocas for arguments instead of pointing multiple |
287 | // dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records | |
288 | // not DWARF and LLVM doesn't support translating the resulting | |
289 | // [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView. | |
290 | // Creating extra allocas on the stack makes the resulting debug info simple enough | |
291 | // that LLVM can generate correct CodeView records and thus the values appear in the | |
292 | // debugger. (#83709) | |
293 | let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc | |
294 | && self.mir.local_kind(local) == mir::LocalKind::Arg | |
295 | // LLVM can handle simple things but anything more complex than just a direct | |
296 | // offset or one indirect offset of 0 is too complex for it to generate CV records | |
297 | // correctly. | |
298 | && (direct_offset != Size::ZERO | |
299 | || !matches!(&indirect_offsets[..], [Size::ZERO] | [])); | |
300 | ||
301 | if should_create_individual_allocas { | |
302 | // Create a variable which will be a pointer to the actual value | |
303 | let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut { | |
304 | mutbl: mir::Mutability::Mut, | |
305 | ty: place.layout.ty, | |
306 | })); | |
307 | let ptr_layout = bx.layout_of(ptr_ty); | |
308 | let alloca = PlaceRef::alloca(bx, ptr_layout); | |
309 | bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill")); | |
310 | ||
311 | // Write the pointer to the variable | |
312 | bx.store(place.llval, alloca.llval, alloca.align); | |
313 | ||
314 | // Point the debug info to `*alloca` for the current variable | |
315 | bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]); | |
316 | } else { | |
317 | bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets); | |
318 | } | |
e74abb32 XL |
319 | } |
320 | } | |
321 | ||
322 | pub fn debug_introduce_locals(&self, bx: &mut Bx) { | |
323 | if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() { | |
324 | for local in self.locals.indices() { | |
325 | self.debug_introduce_local(bx, local); | |
326 | } | |
327 | } | |
328 | } | |
e74abb32 | 329 | |
74b04a01 XL |
330 | /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`. |
331 | pub fn compute_per_local_var_debug_info( | |
332 | &self, | |
fc512014 | 333 | bx: &mut Bx, |
74b04a01 XL |
334 | ) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> { |
335 | let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full; | |
336 | ||
6a06907d XL |
337 | let target_is_msvc = self.cx.sess().target.is_like_msvc; |
338 | ||
74b04a01 XL |
339 | if !full_debug_info && self.cx.sess().fewer_names() { |
340 | return None; | |
341 | } | |
342 | ||
343 | let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls); | |
344 | for var in &self.mir.var_debug_info { | |
29967ef6 XL |
345 | let dbg_scope_and_span = if full_debug_info { |
346 | self.adjusted_span_and_dbg_scope(var.source_info) | |
74b04a01 | 347 | } else { |
29967ef6 | 348 | None |
74b04a01 | 349 | }; |
fc512014 | 350 | |
29967ef6 | 351 | let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| { |
fc512014 XL |
352 | let (var_ty, var_kind) = match var.value { |
353 | mir::VarDebugInfoContents::Place(place) => { | |
354 | let var_ty = self.monomorphized_place_ty(place.as_ref()); | |
355 | let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg | |
356 | && place.projection.is_empty() | |
357 | && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE | |
358 | { | |
359 | let arg_index = place.local.index() - 1; | |
6a06907d | 360 | if target_is_msvc { |
c295e0f8 XL |
361 | // ScalarPair parameters are spilled to the stack so they need to |
362 | // be marked as a `LocalVariable` for MSVC debuggers to visualize | |
363 | // their data correctly. (See #81894 & #88625) | |
364 | let var_ty_layout = self.cx.layout_of(var_ty); | |
365 | if let Abi::ScalarPair(_, _) = var_ty_layout.abi { | |
366 | VariableKind::LocalVariable | |
367 | } else { | |
368 | VariableKind::ArgumentVariable(arg_index + 1) | |
6a06907d XL |
369 | } |
370 | } else { | |
371 | // FIXME(eddyb) shouldn't `ArgumentVariable` indices be | |
372 | // offset in closures to account for the hidden environment? | |
373 | // Also, is this `+ 1` needed at all? | |
374 | VariableKind::ArgumentVariable(arg_index + 1) | |
375 | } | |
fc512014 XL |
376 | } else { |
377 | VariableKind::LocalVariable | |
378 | }; | |
379 | (var_ty, var_kind) | |
380 | } | |
381 | mir::VarDebugInfoContents::Const(c) => { | |
6a06907d | 382 | let ty = self.monomorphize(c.ty()); |
fc512014 XL |
383 | (ty, VariableKind::LocalVariable) |
384 | } | |
74b04a01 | 385 | }; |
fc512014 | 386 | |
29967ef6 | 387 | self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span) |
74b04a01 XL |
388 | }); |
389 | ||
fc512014 XL |
390 | match var.value { |
391 | mir::VarDebugInfoContents::Place(place) => { | |
392 | per_local[place.local].push(PerLocalVarDebugInfo { | |
393 | name: var.name, | |
394 | source_info: var.source_info, | |
395 | dbg_var, | |
396 | projection: place.projection, | |
397 | }); | |
398 | } | |
399 | mir::VarDebugInfoContents::Const(c) => { | |
400 | if let Some(dbg_var) = dbg_var { | |
5e7ed085 | 401 | let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue }; |
fc512014 XL |
402 | |
403 | if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) { | |
404 | let base = Self::spill_operand_to_stack( | |
405 | &operand, | |
406 | Some(var.name.to_string()), | |
407 | bx, | |
408 | ); | |
409 | ||
410 | bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]); | |
411 | } | |
412 | } | |
413 | } | |
414 | } | |
e74abb32 | 415 | } |
e74abb32 | 416 | Some(per_local) |
e74abb32 XL |
417 | } |
418 | } |