Skip to content

Commit 619d19c

Browse files
committed
Auto merge of #83941 - wesleywiser:win_dbginfo_closures, r=nagisa
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
2 parents 2e495d2 + 533002d commit 619d19c

File tree

4 files changed

+142
-15
lines changed

4 files changed

+142
-15
lines changed

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+39-15
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_middle::ty;
66
use rustc_session::config::DebugInfo;
77
use rustc_span::symbol::{kw, Symbol};
88
use rustc_span::{BytePos, Span};
9-
use rustc_target::abi::{LayoutOf, Size};
9+
use rustc_target::abi::Size;
1010

1111
use super::operand::{OperandRef, OperandValue};
1212
use super::place::PlaceRef;
@@ -265,33 +265,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
265265
None => continue,
266266
};
267267

268-
let mut layout = base.layout;
269268
let mut direct_offset = Size::ZERO;
270269
// FIXME(eddyb) use smallvec here.
271270
let mut indirect_offsets = vec![];
271+
let mut place = base;
272272

273273
for elem in &var.projection[..] {
274274
match *elem {
275275
mir::ProjectionElem::Deref => {
276276
indirect_offsets.push(Size::ZERO);
277-
layout = bx.cx().layout_of(
278-
layout
279-
.ty
280-
.builtin_deref(true)
281-
.unwrap_or_else(|| {
282-
span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty)
283-
})
284-
.ty,
285-
);
277+
place = place.project_deref(bx);
286278
}
287279
mir::ProjectionElem::Field(field, _) => {
288280
let i = field.index();
289281
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
290-
*offset += layout.fields.offset(i);
291-
layout = layout.field(bx.cx(), i);
282+
*offset += place.layout.fields.offset(i);
283+
place = place.project_field(bx, i);
292284
}
293285
mir::ProjectionElem::Downcast(_, variant) => {
294-
layout = layout.for_variant(bx.cx(), variant);
286+
place = place.project_downcast(bx, variant);
295287
}
296288
_ => span_bug!(
297289
var.source_info.span,
@@ -301,7 +293,39 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
301293
}
302294
}
303295

304-
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
296+
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
297+
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
298+
// not DWARF and LLVM doesn't support translating the resulting
299+
// [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
300+
// Creating extra allocas on the stack makes the resulting debug info simple enough
301+
// that LLVM can generate correct CodeView records and thus the values appear in the
302+
// debugger. (#83709)
303+
let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
304+
&& self.mir.local_kind(local) == mir::LocalKind::Arg
305+
// LLVM can handle simple things but anything more complex than just a direct
306+
// offset or one indirect offset of 0 is too complex for it to generate CV records
307+
// correctly.
308+
&& (direct_offset != Size::ZERO
309+
|| !matches!(&indirect_offsets[..], [Size::ZERO] | []));
310+
311+
if should_create_individual_allocas {
312+
// Create a variable which will be a pointer to the actual value
313+
let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut {
314+
mutbl: mir::Mutability::Mut,
315+
ty: place.layout.ty,
316+
}));
317+
let ptr_layout = bx.layout_of(ptr_ty);
318+
let alloca = PlaceRef::alloca(bx, ptr_layout);
319+
bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill"));
320+
321+
// Write the pointer to the variable
322+
bx.store(place.llval, alloca.llval, alloca.align);
323+
324+
// Point the debug info to `*alloca` for the current variable
325+
bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]);
326+
} else {
327+
bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
328+
}
305329
}
306330
}
307331

compiler/rustc_codegen_ssa/src/mir/place.rs

+12
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
402402
downcast
403403
}
404404

405+
pub fn project_deref<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) -> Self {
406+
let target_ty = self.layout.ty.builtin_deref(true).expect("failed to deref");
407+
let layout = bx.layout_of(target_ty.ty);
408+
409+
PlaceRef {
410+
llval: bx.load(self.llval, self.align),
411+
llextra: None,
412+
layout,
413+
align: layout.align.abi,
414+
}
415+
}
416+
405417
pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) {
406418
bx.lifetime_start(self.llval, self.layout.size);
407419
}

src/test/debuginfo/var-captured-in-nested-closure.rs

+49
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,55 @@
8383
// lldbr-check:(isize) closure_local = 8
8484
// lldb-command:continue
8585

86+
87+
// === CDB TESTS ===================================================================================
88+
89+
// cdb-command: g
90+
91+
// cdb-command: dx variable
92+
// cdb-check:variable : 1 [Type: [...]]
93+
// cdb-command: dx constant
94+
// cdb-check:constant : 2 [Type: [...]]
95+
// cdb-command: dx a_struct
96+
// cdb-check:a_struct [Type: var_captured_in_nested_closure::Struct]
97+
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
98+
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
99+
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
100+
// cdb-command: dx struct_ref
101+
// cdb-check:struct_ref : 0x[...] [Type: var_captured_in_nested_closure::Struct *]
102+
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
103+
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
104+
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
105+
// cdb-command: dx owned
106+
// cdb-check:owned : 0x[...] : 6 [Type: [...] *]
107+
// cdb-check: 6 [Type: [...]]
108+
// cdb-command: dx closure_local
109+
// cdb-check:closure_local : 8 [Type: [...]]
110+
// cdb-command: dx nested_closure
111+
// cdb-check:nested_closure [Type: var_captured_in_nested_closure::main::{{closure}}::closure-0]
112+
113+
// cdb-command: g
114+
115+
// cdb-command: dx variable
116+
// cdb-check:variable : 1 [Type: [...]]
117+
// cdb-command: dx constant
118+
// cdb-check:constant : 2 [Type: [...]]
119+
// cdb-command: dx a_struct
120+
// cdb-check:a_struct [Type: var_captured_in_nested_closure::Struct]
121+
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
122+
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
123+
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
124+
// cdb-command: dx struct_ref
125+
// cdb-check:struct_ref : 0x[...] [Type: var_captured_in_nested_closure::Struct *]
126+
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
127+
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
128+
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
129+
// cdb-command: dx owned
130+
// cdb-check:owned : 0x[...] : 6 [Type: [...] *]
131+
// cdb-check: 6 [Type: [...]]
132+
// cdb-command: dx closure_local
133+
// cdb-check:closure_local : 8 [Type: [...]]
134+
86135
#![allow(unused_variables)]
87136
#![feature(box_syntax)]
88137
#![feature(omit_gdb_pretty_printer_section)]

src/test/debuginfo/var-captured-in-stack-closure.rs

+42
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,48 @@
7373
// lldbg-check:[...]$9 = 6
7474
// lldbr-check:(isize) *owned = 6
7575

76+
77+
// === CDB TESTS ===================================================================================
78+
79+
// cdb-command: g
80+
81+
// cdb-command: dx variable
82+
// cdb-check:variable : 1 [Type: [...]]
83+
// cdb-command: dx constant
84+
// cdb-check:constant : 2 [Type: [...]]
85+
// cdb-command: dx a_struct
86+
// cdb-check:a_struct [Type: var_captured_in_stack_closure::Struct]
87+
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
88+
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
89+
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
90+
// cdb-command: dx struct_ref
91+
// cdb-check:struct_ref : 0x[...] [Type: var_captured_in_stack_closure::Struct *]
92+
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
93+
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
94+
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
95+
// cdb-command: dx owned
96+
// cdb-check:owned : 0x[...] : 6 [Type: [...] *]
97+
98+
99+
// cdb-command: g
100+
101+
// cdb-command: dx variable
102+
// cdb-check:variable : 2 [Type: [...]]
103+
// cdb-command: dx constant
104+
// cdb-check:constant : 2 [Type: [...]]
105+
// cdb-command: dx a_struct
106+
// cdb-check:a_struct [Type: var_captured_in_stack_closure::Struct]
107+
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
108+
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
109+
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
110+
// cdb-command: dx struct_ref
111+
// cdb-check:struct_ref : 0x[...] [Type: var_captured_in_stack_closure::Struct *]
112+
// cdb-check: [+0x[...]] a : -3 [Type: [...]]
113+
// cdb-check: [+0x[...]] b : 4.500000 [Type: [...]]
114+
// cdb-check: [+0x[...]] c : 0x5 [Type: unsigned [...]]
115+
// cdb-command: dx owned
116+
// cdb-check:owned : 0x[...] : 6 [Type: [...] *]
117+
76118
#![feature(box_syntax)]
77119
#![allow(unused_variables)]
78120
#![feature(omit_gdb_pretty_printer_section)]

0 commit comments

Comments
 (0)