Skip to content

Commit

Permalink
Auto merge of #83941 - wesleywiser:win_dbginfo_closures, r=nagisa
Browse files Browse the repository at this point in the history
Improve debuginfo for closures and async functions on 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 or async fn.

This fixes that by creating additional allocas on the stack so that
the resulting debuginfo is simple (just `*my_variable.dbg.spill`) and
LLVM can generate the correct CV records.

I also updated some of our existing tests to run in CDB to cover this case.

Before (closure):

![image](https://user-images.githubusercontent.com/831192/113756857-e6dc4200-96df-11eb-8d6d-b7ed7a84aad5.png)

After (closure):

![image](https://user-images.githubusercontent.com/831192/113757067-2e62ce00-96e0-11eb-89f7-7dc8ab89b1b8.png)

Before (async):

![image](https://user-images.githubusercontent.com/831192/114077916-4e2bfa80-9876-11eb-9f15-e302d1faa652.png)

After (async):

![image](https://user-images.githubusercontent.com/831192/114077677-0d33e600-9876-11eb-8ce3-cac20a9ea94a.png)

Fixes #83709
  • Loading branch information
bors committed Apr 8, 2021
2 parents 2e495d2 + 533002d commit 619d19c
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 15 deletions.
54 changes: 39 additions & 15 deletions compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
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) {
bx.lifetime_start(self.llval, self.layout.size);
}
Expand Down
49 changes: 49 additions & 0 deletions src/test/debuginfo/var-captured-in-nested-closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
42 changes: 42 additions & 0 deletions src/test/debuginfo/var-captured-in-stack-closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down

0 comments on commit 619d19c

Please sign in to comment.