Fix closed over variables not available in debuginfo for Windows MSVC
The issue was that the resulting debuginfo was too complex for LLVM to translate into CodeView records correctly. As a result, it simply ignored the debuginfo which meant Windows debuggers could not display any closed over variables when stepping inside a closure. This fixes that by spilling additional variables to the stack so that the resulting debuginfo is simple (just `*my_variable.dbg.spill`) and LLVM can generate the correct CV records.
This commit is contained in:
parent
e9cdcccfa8
commit
533002d3a1
4 changed files with 142 additions and 15 deletions
|
@ -6,7 +6,7 @@ use rustc_middle::ty;
|
||||||
use rustc_session::config::DebugInfo;
|
use rustc_session::config::DebugInfo;
|
||||||
use rustc_span::symbol::{kw, Symbol};
|
use rustc_span::symbol::{kw, Symbol};
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, Span};
|
||||||
use rustc_target::abi::{LayoutOf, Size};
|
use rustc_target::abi::Size;
|
||||||
|
|
||||||
use super::operand::{OperandRef, OperandValue};
|
use super::operand::{OperandRef, OperandValue};
|
||||||
use super::place::PlaceRef;
|
use super::place::PlaceRef;
|
||||||
|
@ -265,33 +265,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut layout = base.layout;
|
|
||||||
let mut direct_offset = Size::ZERO;
|
let mut direct_offset = Size::ZERO;
|
||||||
// FIXME(eddyb) use smallvec here.
|
// FIXME(eddyb) use smallvec here.
|
||||||
let mut indirect_offsets = vec![];
|
let mut indirect_offsets = vec![];
|
||||||
|
let mut place = base;
|
||||||
|
|
||||||
for elem in &var.projection[..] {
|
for elem in &var.projection[..] {
|
||||||
match *elem {
|
match *elem {
|
||||||
mir::ProjectionElem::Deref => {
|
mir::ProjectionElem::Deref => {
|
||||||
indirect_offsets.push(Size::ZERO);
|
indirect_offsets.push(Size::ZERO);
|
||||||
layout = bx.cx().layout_of(
|
place = place.project_deref(bx);
|
||||||
layout
|
|
||||||
.ty
|
|
||||||
.builtin_deref(true)
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty)
|
|
||||||
})
|
|
||||||
.ty,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
mir::ProjectionElem::Field(field, _) => {
|
mir::ProjectionElem::Field(field, _) => {
|
||||||
let i = field.index();
|
let i = field.index();
|
||||||
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
|
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
|
||||||
*offset += layout.fields.offset(i);
|
*offset += place.layout.fields.offset(i);
|
||||||
layout = layout.field(bx.cx(), i);
|
place = place.project_field(bx, i);
|
||||||
}
|
}
|
||||||
mir::ProjectionElem::Downcast(_, variant) => {
|
mir::ProjectionElem::Downcast(_, variant) => {
|
||||||
layout = layout.for_variant(bx.cx(), variant);
|
place = place.project_downcast(bx, variant);
|
||||||
}
|
}
|
||||||
_ => span_bug!(
|
_ => span_bug!(
|
||||||
var.source_info.span,
|
var.source_info.span,
|
||||||
|
@ -301,7 +293,39 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
|
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
|
||||||
|
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
|
||||||
|
// not DWARF and LLVM doesn't support translating the resulting
|
||||||
|
// [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
|
||||||
|
// Creating extra allocas on the stack makes the resulting debug info simple enough
|
||||||
|
// that LLVM can generate correct CodeView records and thus the values appear in the
|
||||||
|
// debugger. (#83709)
|
||||||
|
let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
|
||||||
|
&& self.mir.local_kind(local) == mir::LocalKind::Arg
|
||||||
|
// LLVM can handle simple things but anything more complex than just a direct
|
||||||
|
// offset or one indirect offset of 0 is too complex for it to generate CV records
|
||||||
|
// correctly.
|
||||||
|
&& (direct_offset != Size::ZERO
|
||||||
|
|| !matches!(&indirect_offsets[..], [Size::ZERO] | []));
|
||||||
|
|
||||||
|
if should_create_individual_allocas {
|
||||||
|
// Create a variable which will be a pointer to the actual value
|
||||||
|
let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut {
|
||||||
|
mutbl: mir::Mutability::Mut,
|
||||||
|
ty: place.layout.ty,
|
||||||
|
}));
|
||||||
|
let ptr_layout = bx.layout_of(ptr_ty);
|
||||||
|
let alloca = PlaceRef::alloca(bx, ptr_layout);
|
||||||
|
bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill"));
|
||||||
|
|
||||||
|
// Write the pointer to the variable
|
||||||
|
bx.store(place.llval, alloca.llval, alloca.align);
|
||||||
|
|
||||||
|
// Point the debug info to `*alloca` for the current variable
|
||||||
|
bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]);
|
||||||
|
} else {
|
||||||
|
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -402,6 +402,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||||
downcast
|
downcast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn project_deref<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) -> Self {
|
||||||
|
let target_ty = self.layout.ty.builtin_deref(true).expect("failed to deref");
|
||||||
|
let layout = bx.layout_of(target_ty.ty);
|
||||||
|
|
||||||
|
PlaceRef {
|
||||||
|
llval: bx.load(self.llval, self.align),
|
||||||
|
llextra: None,
|
||||||
|
layout,
|
||||||
|
align: layout.align.abi,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) {
|
pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) {
|
||||||
bx.lifetime_start(self.llval, self.layout.size);
|
bx.lifetime_start(self.llval, self.layout.size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,55 @@
|
||||||
// lldbr-check:(isize) closure_local = 8
|
// lldbr-check:(isize) closure_local = 8
|
||||||
// lldb-command:continue
|
// lldb-command:continue
|
||||||
|
|
||||||
|
|
||||||
|
// === CDB TESTS ===================================================================================
|
||||||
|
|
||||||
|
// cdb-command: g
|
||||||
|
|
||||||
|
// cdb-command: dx variable
|
||||||
|
// cdb-check:variable : 1 [Type: [...]]
|
||||||
|
// cdb-command: dx constant
|
||||||
|
// cdb-check:constant : 2 [Type: [...]]
|
||||||
|
// cdb-command: dx a_struct
|
||||||
|
// cdb-check:a_struct [Type: var_captured_in_nested_closure::Struct]
|
||||||
|
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
|
||||||
|
// cdb-command: dx struct_ref
|
||||||
|
// cdb-check:struct_ref : 0x[...] [Type: var_captured_in_nested_closure::Struct *]
|
||||||
|
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
|
||||||
|
// cdb-command: dx owned
|
||||||
|
// cdb-check:owned : 0x[...] : 6 [Type: [...] *]
|
||||||
|
// cdb-check: 6 [Type: [...]]
|
||||||
|
// cdb-command: dx closure_local
|
||||||
|
// cdb-check:closure_local : 8 [Type: [...]]
|
||||||
|
// cdb-command: dx nested_closure
|
||||||
|
// cdb-check:nested_closure [Type: var_captured_in_nested_closure::main::{{closure}}::closure-0]
|
||||||
|
|
||||||
|
// cdb-command: g
|
||||||
|
|
||||||
|
// cdb-command: dx variable
|
||||||
|
// cdb-check:variable : 1 [Type: [...]]
|
||||||
|
// cdb-command: dx constant
|
||||||
|
// cdb-check:constant : 2 [Type: [...]]
|
||||||
|
// cdb-command: dx a_struct
|
||||||
|
// cdb-check:a_struct [Type: var_captured_in_nested_closure::Struct]
|
||||||
|
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
|
||||||
|
// cdb-command: dx struct_ref
|
||||||
|
// cdb-check:struct_ref : 0x[...] [Type: var_captured_in_nested_closure::Struct *]
|
||||||
|
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
|
||||||
|
// cdb-command: dx owned
|
||||||
|
// cdb-check:owned : 0x[...] : 6 [Type: [...] *]
|
||||||
|
// cdb-check: 6 [Type: [...]]
|
||||||
|
// cdb-command: dx closure_local
|
||||||
|
// cdb-check:closure_local : 8 [Type: [...]]
|
||||||
|
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
#![feature(omit_gdb_pretty_printer_section)]
|
#![feature(omit_gdb_pretty_printer_section)]
|
||||||
|
|
|
@ -73,6 +73,48 @@
|
||||||
// lldbg-check:[...]$9 = 6
|
// lldbg-check:[...]$9 = 6
|
||||||
// lldbr-check:(isize) *owned = 6
|
// lldbr-check:(isize) *owned = 6
|
||||||
|
|
||||||
|
|
||||||
|
// === CDB TESTS ===================================================================================
|
||||||
|
|
||||||
|
// cdb-command: g
|
||||||
|
|
||||||
|
// cdb-command: dx variable
|
||||||
|
// cdb-check:variable : 1 [Type: [...]]
|
||||||
|
// cdb-command: dx constant
|
||||||
|
// cdb-check:constant : 2 [Type: [...]]
|
||||||
|
// cdb-command: dx a_struct
|
||||||
|
// cdb-check:a_struct [Type: var_captured_in_stack_closure::Struct]
|
||||||
|
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
|
||||||
|
// cdb-command: dx struct_ref
|
||||||
|
// cdb-check:struct_ref : 0x[...] [Type: var_captured_in_stack_closure::Struct *]
|
||||||
|
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
|
||||||
|
// cdb-command: dx owned
|
||||||
|
// cdb-check:owned : 0x[...] : 6 [Type: [...] *]
|
||||||
|
|
||||||
|
|
||||||
|
// cdb-command: g
|
||||||
|
|
||||||
|
// cdb-command: dx variable
|
||||||
|
// cdb-check:variable : 2 [Type: [...]]
|
||||||
|
// cdb-command: dx constant
|
||||||
|
// cdb-check:constant : 2 [Type: [...]]
|
||||||
|
// cdb-command: dx a_struct
|
||||||
|
// cdb-check:a_struct [Type: var_captured_in_stack_closure::Struct]
|
||||||
|
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
|
||||||
|
// cdb-command: dx struct_ref
|
||||||
|
// cdb-check:struct_ref : 0x[...] [Type: var_captured_in_stack_closure::Struct *]
|
||||||
|
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
|
||||||
|
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
|
||||||
|
// cdb-command: dx owned
|
||||||
|
// cdb-check:owned : 0x[...] : 6 [Type: [...] *]
|
||||||
|
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
#![feature(omit_gdb_pretty_printer_section)]
|
#![feature(omit_gdb_pretty_printer_section)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue