2 use rustc_index::vec::IndexVec;
3 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
6 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
7 use rustc_session::config::DebugInfo;
8 use rustc_span::symbol::{kw, Symbol};
9 use rustc_span::{BytePos, Span};
10 use rustc_target::abi::Abi;
11 use rustc_target::abi::Size;
13 use super::operand::{OperandRef, OperandValue};
14 use super::place::PlaceRef;
15 use super::{FunctionCx, LocalRef};
19 pub struct FunctionDebugContext<S, L> {
20 pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
23 #[derive(Copy, Clone)]
24 pub enum VariableKind {
25 ArgumentVariable(usize /*index*/),
29 /// Like `mir::VarDebugInfo`, but within a `mir::Local`.
31 pub struct PerLocalVarDebugInfo<'tcx, D> {
33 pub source_info: mir::SourceInfo,
35 /// `DIVariable` returned by `create_dbg_var`.
36 pub dbg_var: Option<D>,
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>>,
42 /// `.place.projection` from `mir::VarDebugInfo`.
43 pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
46 #[derive(Clone, Copy, Debug)]
47 pub struct DebugScope<S, L> {
50 /// Call site location, if this scope was inlined from another function.
51 pub inlined_at: Option<L>,
53 // Start and end offsets of the file to which this DIScope belongs.
54 // These are used to quickly determine whether some span refers to the same file.
55 pub file_start_pos: BytePos,
56 pub file_end_pos: BytePos,
59 impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
60 /// DILocations inherit source file name from the parent DIScope. Due to macro expansions
61 /// it may so happen that the current span belongs to a different file than the DIScope
62 /// corresponding to span's containing source scope. If so, we need to create a DIScope
63 /// "extension" into that file.
64 pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>(
70 if pos < self.file_start_pos || pos >= self.file_end_pos {
71 let sm = cx.sess().source_map();
72 cx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file)
79 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
80 pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
81 bx.set_span(source_info.span);
82 if let Some(dbg_loc) = self.dbg_loc(source_info) {
83 bx.set_dbg_loc(dbg_loc);
87 fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option<Bx::DILocation> {
88 let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?;
89 Some(self.cx.dbg_loc(dbg_scope, inlined_at, span))
92 fn adjusted_span_and_dbg_scope(
94 source_info: mir::SourceInfo,
95 ) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
96 let span = self.adjust_span_for_debugging(source_info.span);
97 let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
98 Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
101 /// In order to have a good line stepping behavior in debugger, we overwrite debug
102 /// locations of macro expansions with that of the outermost expansion site (when the macro is
103 /// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
104 fn adjust_span_for_debugging(&self, mut span: Span) -> Span {
105 // Bail out if debug info emission is not enabled.
106 if self.debug_context.is_none() {
110 if self.cx.tcx().should_collapse_debuginfo(span) {
111 // Walk up the macro expansion chain until we reach a non-expanded span.
112 // We also stop at the function body level because no line stepping can occur
113 // at the level above that.
114 // Use span of the outermost expansion site, while keeping the original lexical scope.
115 span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt());
121 fn spill_operand_to_stack(
122 operand: &OperandRef<'tcx, Bx::Value>,
123 name: Option<String>,
125 ) -> PlaceRef<'tcx, Bx::Value> {
126 // "Spill" the value onto the stack, for debuginfo,
127 // without forcing non-debuginfo uses of the local
128 // to also load from the stack every single time.
129 // FIXME(#68817) use `llvm.dbg.value` instead,
130 // at least for the cases which LLVM handles correctly.
131 let spill_slot = PlaceRef::alloca(bx, operand.layout);
132 if let Some(name) = name {
133 bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill"));
135 operand.val.store(bx, spill_slot);
139 /// Apply debuginfo and/or name, after creating the `alloca` for a local,
140 /// or initializing the local with an operand (whichever applies).
141 pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
142 let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
144 // FIXME(eddyb) maybe name the return place as `_0` or `return`?
145 if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable()
150 let vars = match &self.per_local_var_debug_info {
151 Some(per_local) => &per_local[local],
154 let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
155 let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
157 let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
158 let arg_index = local.index() - 1;
160 // Add debuginfo even to unnamed arguments.
161 // FIXME(eddyb) is this really needed?
162 if arg_index == 0 && has_proj() {
163 // Hide closure environments from debuginfo.
164 // FIXME(eddyb) shouldn't `ArgumentVariable` indices
165 // be offset to account for the hidden environment?
167 } else if whole_local_var.is_some() {
168 // No need to make up anything, there is a `mir::VarDebugInfo`
169 // covering the whole local.
170 // FIXME(eddyb) take `whole_local_var.source_info.scope` into
171 // account, just in case it doesn't use `ArgumentVariable`
172 // (after #67586 gets fixed).
175 let name = kw::Empty;
176 let decl = &self.mir.local_decls[local];
177 let dbg_var = if full_debug_info {
178 self.adjusted_span_and_dbg_scope(decl.source_info).map(
179 |(dbg_scope, _, span)| {
180 // FIXME(eddyb) is this `+ 1` needed at all?
181 let kind = VariableKind::ArgumentVariable(arg_index + 1);
183 let arg_ty = self.monomorphize(decl.ty);
185 self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
192 Some(PerLocalVarDebugInfo {
194 source_info: decl.source_info,
197 projection: ty::List::empty(),
204 let local_ref = &self.locals[local];
206 let name = if bx.sess().fewer_names() {
209 Some(match whole_local_var.or(fallback_var.clone()) {
210 Some(var) if var.name != kw::Empty => var.name.to_string(),
211 _ => format!("{:?}", local),
215 if let Some(name) = &name {
217 LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
218 bx.set_var_name(place.llval, name);
220 LocalRef::Operand(Some(operand)) => match operand.val {
221 OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
222 bx.set_var_name(x, name);
224 OperandValue::Pair(a, b) => {
225 // FIXME(eddyb) these are scalar components,
226 // maybe extract the high-level fields?
227 bx.set_var_name(a, &(name.clone() + ".0"));
228 bx.set_var_name(b, &(name.clone() + ".1"));
231 LocalRef::Operand(None) => {}
235 if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
239 let base = match local_ref {
240 LocalRef::Operand(None) => return,
242 LocalRef::Operand(Some(operand)) => {
243 // Don't spill operands onto the stack in naked functions.
244 // See: https://github.com/rust-lang/rust/issues/42779
245 let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id());
246 if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
250 Self::spill_operand_to_stack(operand, name, bx)
253 LocalRef::Place(place) => *place,
255 // FIXME(eddyb) add debuginfo for unsized places too.
256 LocalRef::UnsizedPlace(_) => return,
259 let vars = vars.iter().cloned().chain(fallback_var);
262 let Some(dbg_var) = var.dbg_var else { continue };
263 let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
265 let mut direct_offset = Size::ZERO;
266 // FIXME(eddyb) use smallvec here.
267 let mut indirect_offsets = vec![];
268 let mut place = base;
270 for elem in &var.projection[..] {
272 mir::ProjectionElem::Deref => {
273 indirect_offsets.push(Size::ZERO);
274 place = bx.load_operand(place).deref(bx.cx());
276 mir::ProjectionElem::Field(field, _) => {
277 let i = field.index();
278 let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
279 *offset += place.layout.fields.offset(i);
280 place = place.project_field(bx, i);
282 mir::ProjectionElem::Downcast(_, variant) => {
283 place = place.project_downcast(bx, variant);
286 var.source_info.span,
287 "unsupported var debuginfo place `{:?}`",
288 mir::Place { local, projection: var.projection },
293 // When targeting MSVC, create extra allocas for arguments instead of pointing multiple
294 // dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
295 // not DWARF and LLVM doesn't support translating the resulting
296 // [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
297 // Creating extra allocas on the stack makes the resulting debug info simple enough
298 // that LLVM can generate correct CodeView records and thus the values appear in the
299 // debugger. (#83709)
300 let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
301 && self.mir.local_kind(local) == mir::LocalKind::Arg
302 // LLVM can handle simple things but anything more complex than just a direct
303 // offset or one indirect offset of 0 is too complex for it to generate CV records
305 && (direct_offset != Size::ZERO
306 || !matches!(&indirect_offsets[..], [Size::ZERO] | []));
308 if should_create_individual_allocas {
309 // Create a variable which will be a pointer to the actual value
310 let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut {
311 mutbl: mir::Mutability::Mut,
314 let ptr_layout = bx.layout_of(ptr_ty);
315 let alloca = PlaceRef::alloca(bx, ptr_layout);
316 bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill"));
318 // Write the pointer to the variable
319 bx.store(place.llval, alloca.llval, alloca.align);
321 // Point the debug info to `*alloca` for the current variable
322 bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None);
336 pub fn debug_introduce_locals(&self, bx: &mut Bx) {
337 if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
338 for local in self.locals.indices() {
339 self.debug_introduce_local(bx, local);
344 /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
345 pub fn compute_per_local_var_debug_info(
348 ) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
349 let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
351 let target_is_msvc = self.cx.sess().target.is_like_msvc;
353 if !full_debug_info && self.cx.sess().fewer_names() {
357 let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
358 for var in &self.mir.var_debug_info {
359 let dbg_scope_and_span = if full_debug_info {
360 self.adjusted_span_and_dbg_scope(var.source_info)
365 let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
366 let (var_ty, var_kind) = match var.value {
367 mir::VarDebugInfoContents::Place(place) => {
368 let var_ty = self.monomorphized_place_ty(place.as_ref());
369 let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
370 && place.projection.is_empty()
371 && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
373 let arg_index = place.local.index() - 1;
375 // ScalarPair parameters are spilled to the stack so they need to
376 // be marked as a `LocalVariable` for MSVC debuggers to visualize
377 // their data correctly. (See #81894 & #88625)
378 let var_ty_layout = self.cx.layout_of(var_ty);
379 if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
380 VariableKind::LocalVariable
382 VariableKind::ArgumentVariable(arg_index + 1)
385 // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
386 // offset in closures to account for the hidden environment?
387 // Also, is this `+ 1` needed at all?
388 VariableKind::ArgumentVariable(arg_index + 1)
391 VariableKind::LocalVariable
395 mir::VarDebugInfoContents::Const(c) => {
396 let ty = self.monomorphize(c.ty());
397 (ty, VariableKind::LocalVariable)
399 mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
400 let ty = self.monomorphize(ty);
401 (ty, VariableKind::LocalVariable)
405 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
409 mir::VarDebugInfoContents::Place(place) => {
410 per_local[place.local].push(PerLocalVarDebugInfo {
412 source_info: var.source_info,
415 projection: place.projection,
418 mir::VarDebugInfoContents::Const(c) => {
419 if let Some(dbg_var) = dbg_var {
420 let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
422 if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) {
423 let base = Self::spill_operand_to_stack(
425 Some(var.name.to_string()),
429 bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
433 mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
434 let var_ty = self.monomorphize(ty);
435 let var_layout = self.cx.layout_of(var_ty);
436 for fragment in fragments {
437 let mut fragment_start = Size::ZERO;
438 let mut fragment_layout = var_layout;
440 for elem in &fragment.projection {
442 mir::ProjectionElem::Field(field, _) => {
443 let i = field.index();
444 fragment_start += fragment_layout.fields.offset(i);
445 fragment_layout = fragment_layout.field(self.cx, i);
448 var.source_info.span,
449 "unsupported fragment projection `{:?}`",
455 let place = fragment.contents;
456 per_local[place.local].push(PerLocalVarDebugInfo {
458 source_info: var.source_info,
460 fragment: if fragment_layout.size == var_layout.size {
461 // Fragment covers entire variable, so as far as
462 // DWARF is concerned, it's not really a fragment.
465 Some(fragment_start..fragment_start + fragment_layout.size)
467 projection: place.projection,