From 533002d3a1f7f95f167323174205b57564b576a6 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 6 Apr 2021 13:34:17 -0400 Subject: [PATCH] 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. --- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 54 +++++++++++++------ compiler/rustc_codegen_ssa/src/mir/place.rs | 12 +++++ .../var-captured-in-nested-closure.rs | 49 +++++++++++++++++ .../var-captured-in-stack-closure.rs | 42 +++++++++++++++ 4 files changed, 142 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index a3f20abc82d..6bb20545f07 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -6,7 +6,7 @@ use rustc_middle::ty; use rustc_session::config::DebugInfo; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Span}; -use rustc_target::abi::{LayoutOf, Size}; +use rustc_target::abi::Size; use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; @@ -265,33 +265,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None => continue, }; - let mut layout = base.layout; let mut direct_offset = Size::ZERO; // FIXME(eddyb) use smallvec here. let mut indirect_offsets = vec![]; + let mut place = base; for elem in &var.projection[..] { match *elem { mir::ProjectionElem::Deref => { indirect_offsets.push(Size::ZERO); - layout = bx.cx().layout_of( - layout - .ty - .builtin_deref(true) - .unwrap_or_else(|| { - span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty) - }) - .ty, - ); + place = place.project_deref(bx); } mir::ProjectionElem::Field(field, _) => { let i = field.index(); let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset); - *offset += layout.fields.offset(i); - layout = layout.field(bx.cx(), i); + *offset += place.layout.fields.offset(i); + place = place.project_field(bx, i); } mir::ProjectionElem::Downcast(_, variant) => { - layout = layout.for_variant(bx.cx(), variant); + place = place.project_downcast(bx, variant); } _ => span_bug!( 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); + } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 66d9d1a1e0c..a9e7ebf6d43 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -402,6 +402,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { downcast } + pub fn project_deref>(&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>(&self, bx: &mut Bx) { bx.lifetime_start(self.llval, self.layout.size); } diff --git a/src/test/debuginfo/var-captured-in-nested-closure.rs b/src/test/debuginfo/var-captured-in-nested-closure.rs index 8ab6d141731..695cdc4f41f 100644 --- a/src/test/debuginfo/var-captured-in-nested-closure.rs +++ b/src/test/debuginfo/var-captured-in-nested-closure.rs @@ -83,6 +83,55 @@ // lldbr-check:(isize) closure_local = 8 // 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)] #![feature(box_syntax)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/var-captured-in-stack-closure.rs b/src/test/debuginfo/var-captured-in-stack-closure.rs index f53f8aaa670..1bbb79c37a4 100644 --- a/src/test/debuginfo/var-captured-in-stack-closure.rs +++ b/src/test/debuginfo/var-captured-in-stack-closure.rs @@ -73,6 +73,48 @@ // lldbg-check:[...]$9 = 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)] #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)]