Skip to content

Commit b1133d3

Browse files
committed
rustc_mir: add debuginfo support to fragment_locals.
1 parent dd79633 commit b1133d3

File tree

14 files changed

+349
-74
lines changed

14 files changed

+349
-74
lines changed

Diff for: src/librustc/mir/mod.rs

+58-4
Original file line numberDiff line numberDiff line change
@@ -948,10 +948,64 @@ pub struct VarDebugInfo<'tcx> {
948948
/// (see `LocalDecl`'s `source_info` field for more details).
949949
pub source_info: SourceInfo,
950950

951-
/// Where the data for this user variable is to be found.
952-
/// NOTE(eddyb) There's an unenforced invariant that this `Place` is
953-
/// based on a `Local`, not a `Static`, and contains no indexing.
954-
pub place: Place<'tcx>,
951+
/// How the contents of this user variable are represented.
952+
pub contents: VarDebugInfoContents<'tcx>,
953+
}
954+
955+
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
956+
pub enum VarDebugInfoContents<'tcx> {
957+
/// The user variable's data is entirely in one `Place`.
958+
// NOTE(eddyb) there's an unenforced invariant that this `Place` is
959+
// contains no indexing (with a non-constant index).
960+
Compact(Place<'tcx>),
961+
962+
/// The user variable's data is split across several fragments,
963+
/// each described by a `VarDebugInfoFragment`.
964+
/// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
965+
/// and LLVM's `DW_OP_LLVM_fragment` for more details on
966+
/// the underlying debuginfo feature this relies on.
967+
Composite {
968+
/// Type of the original user variable.
969+
ty: Ty<'tcx>,
970+
971+
/// All the parts of the original user variable, which ended
972+
/// up in disjoint places, due to optimizations.
973+
fragments: Vec<VarDebugInfoFragment<'tcx>>,
974+
},
975+
}
976+
977+
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
978+
pub struct VarDebugInfoFragment<'tcx> {
979+
/// Where in the composite user variable this fragment is,
980+
/// represented as a "projection" into the composite variable.
981+
/// At lower levels, this corresponds to a byte/bit range.
982+
// NOTE(eddyb) there's an unenforced invariant that this contains
983+
// only `Field`s, and not into `enum` variants or `union`s.
984+
// FIXME(eddyb) support this for `enum`s by either using DWARF's
985+
// more advanced control-flow features (unsupported by LLVM?)
986+
// to match on the discriminant, or by using custom type debuginfo
987+
// with non-overlapping variants for the composite variable.
988+
pub projection: Vec<ProjectionKind>,
989+
990+
/// Where the data for this fragment can be found.
991+
// NOTE(eddyb) There's an unenforced invariant that this `Place` is
992+
// contains no indexing (with a non-constant index).
993+
pub contents: Place<'tcx>,
994+
}
995+
996+
impl Debug for VarDebugInfoFragment<'_> {
997+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
998+
for elem in self.projection.iter() {
999+
match elem {
1000+
ProjectionElem::Field(field, _) => {
1001+
write!(fmt, ".{:?}", field.index())?;
1002+
}
1003+
_ => bug!("unsupported fragment projection `{:?}`", elem),
1004+
}
1005+
}
1006+
1007+
write!(fmt, " => {:?}", self.contents)
1008+
}
9551009
}
9561010

9571011
///////////////////////////////////////////////////////////////////////////

Diff for: src/librustc/mir/visit.rs

+21-6
Original file line numberDiff line numberDiff line change
@@ -739,16 +739,31 @@ macro_rules! make_mir_visitor {
739739
let VarDebugInfo {
740740
name: _,
741741
source_info,
742-
place,
742+
contents,
743743
} = var_debug_info;
744744

745745
self.visit_source_info(source_info);
746746
let location = START_BLOCK.start_location();
747-
self.visit_place(
748-
place,
749-
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
750-
location,
751-
);
747+
match contents {
748+
VarDebugInfoContents::Compact(place) => {
749+
self.visit_place(
750+
place,
751+
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
752+
location,
753+
);
754+
}
755+
VarDebugInfoContents::Composite { ty, fragments } => {
756+
// FIXME(eddyb) use a better `TyContext` here.
757+
self.visit_ty(ty, TyContext::Location(location));
758+
for VarDebugInfoFragment { projection: _, contents } in fragments {
759+
self.visit_place(
760+
contents,
761+
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
762+
location,
763+
);
764+
}
765+
}
766+
}
752767
}
753768

754769
fn super_source_scope(&mut self,

Diff for: src/librustc_codegen_llvm/debuginfo/metadata.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1288,10 +1288,14 @@ fn generator_layout_and_saved_local_names(
12881288

12891289
let state_arg = mir::Local::new(1);
12901290
for var in &body.var_debug_info {
1291-
if var.place.local != state_arg {
1291+
let place = match var.contents {
1292+
mir::VarDebugInfoContents::Compact(place) => place,
1293+
mir::VarDebugInfoContents::Composite { .. } => continue,
1294+
};
1295+
if place.local != state_arg {
12921296
continue;
12931297
}
1294-
match var.place.projection[..] {
1298+
match place.projection[..] {
12951299
[
12961300
// Deref of the `Pin<&mut Self>` state argument.
12971301
mir::ProjectionElem::Field(..),

Diff for: src/librustc_codegen_llvm/debuginfo/mod.rs

+20-9
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use libc::c_uint;
3434
use log::debug;
3535
use std::cell::RefCell;
3636
use std::ffi::CString;
37+
use std::ops::Range;
3738

3839
use rustc::ty::layout::{self, HasTyCtxt, LayoutOf, Size};
3940
use rustc_codegen_ssa::traits::*;
@@ -154,30 +155,40 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> {
154155
variable_alloca: Self::Value,
155156
direct_offset: Size,
156157
indirect_offsets: &[Size],
158+
fragment: Option<Range<Size>>,
157159
span: Span,
158160
) {
159161
assert!(!dbg_context.source_locations_enabled);
160162
let cx = self.cx();
161163

162164
let loc = span_start(cx, span);
163165

164-
// Convert the direct and indirect offsets to address ops.
166+
// Convert the direct/indirect offsets and fragment byte range to DWARF OPs.
165167
let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() };
166168
let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() };
167-
let mut addr_ops = SmallVec::<[_; 8]>::new();
169+
let op_llvm_fragment = || unsafe { llvm::LLVMRustDIBuilderCreateOpLLVMFragment() };
170+
let mut dwarf_ops = SmallVec::<[_; 8]>::new();
168171

169172
if direct_offset.bytes() > 0 {
170-
addr_ops.push(op_plus_uconst());
171-
addr_ops.push(direct_offset.bytes() as i64);
173+
dwarf_ops.push(op_plus_uconst());
174+
dwarf_ops.push(direct_offset.bytes() as i64);
172175
}
173176
for &offset in indirect_offsets {
174-
addr_ops.push(op_deref());
177+
dwarf_ops.push(op_deref());
175178
if offset.bytes() > 0 {
176-
addr_ops.push(op_plus_uconst());
177-
addr_ops.push(offset.bytes() as i64);
179+
dwarf_ops.push(op_plus_uconst());
180+
dwarf_ops.push(offset.bytes() as i64);
178181
}
179182
}
180183

184+
if let Some(fragment) = fragment {
185+
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
186+
// offset and size, both of them in bits.
187+
dwarf_ops.push(op_llvm_fragment());
188+
dwarf_ops.push(fragment.start.bits() as i64);
189+
dwarf_ops.push((fragment.end - fragment.start).bits() as i64);
190+
}
191+
181192
// FIXME(eddyb) maybe this information could be extracted from `var`,
182193
// to avoid having to pass it down in both places?
183194
source_loc::set_debug_location(
@@ -191,8 +202,8 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> {
191202
DIB(cx),
192203
variable_alloca,
193204
dbg_var,
194-
addr_ops.as_ptr(),
195-
addr_ops.len() as c_uint,
205+
dwarf_ops.as_ptr(),
206+
dwarf_ops.len() as c_uint,
196207
debug_loc,
197208
self.llbb(),
198209
);

Diff for: src/librustc_codegen_llvm/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1809,6 +1809,7 @@ extern "C" {
18091809
) -> &'a Value;
18101810
pub fn LLVMRustDIBuilderCreateOpDeref() -> i64;
18111811
pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> i64;
1812+
pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> i64;
18121813

18131814
#[allow(improper_ctypes)]
18141815
pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);

Diff for: src/librustc_codegen_ssa/mir/debuginfo.rs

+84-29
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use rustc::ty;
55
use rustc::ty::layout::{LayoutOf, Size};
66
use rustc_hir::def_id::CrateNum;
77
use rustc_index::vec::IndexVec;
8-
98
use rustc_span::symbol::{kw, Symbol};
109
use rustc_span::{BytePos, Span};
10+
use std::ops::Range;
1111

1212
use super::OperandValue;
1313
use super::{FunctionCx, LocalRef};
@@ -25,14 +25,18 @@ pub enum VariableKind {
2525
}
2626

2727
/// Like `mir::VarDebugInfo`, but within a `mir::Local`.
28-
#[derive(Copy, Clone)]
28+
#[derive(Clone)]
2929
pub struct PerLocalVarDebugInfo<'tcx, D> {
3030
pub name: Symbol,
3131
pub source_info: mir::SourceInfo,
3232

3333
/// `DIVariable` returned by `create_dbg_var`.
3434
pub dbg_var: Option<D>,
3535

36+
/// Byte range in the `dbg_var` covered by this fragment,
37+
/// if this is a fragment of a composite `VarDebugInfo`.
38+
pub fragment: Option<Range<Size>>,
39+
3640
/// `.place.projection` from `mir::VarDebugInfo`.
3741
pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
3842
}
@@ -127,7 +131,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
127131
Some(per_local) => &per_local[local],
128132
None => return,
129133
};
130-
let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
134+
let whole_local_var =
135+
vars.iter().find(|var| var.fragment.is_none() && var.projection.is_empty()).cloned();
131136
let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
132137

133138
let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
@@ -173,6 +178,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
173178
name,
174179
source_info: decl.source_info,
175180
dbg_var,
181+
fragment: None,
176182
projection: ty::List::empty(),
177183
})
178184
}
@@ -183,7 +189,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
183189
let local_ref = &self.locals[local];
184190

185191
if !bx.sess().fewer_names() {
186-
let name = match whole_local_var.or(fallback_var) {
192+
let name = match whole_local_var.or(fallback_var.clone()) {
187193
Some(var) if var.name != kw::Invalid => var.name.to_string(),
188194
_ => format!("{:?}", local),
189195
};
@@ -221,7 +227,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
221227
_ => return,
222228
};
223229

224-
let vars = vars.iter().copied().chain(fallback_var);
230+
let vars = vars.iter().cloned().chain(fallback_var);
225231

226232
for var in vars {
227233
let mut layout = base.layout;
@@ -270,6 +276,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
270276
base.llval,
271277
direct_offset,
272278
&indirect_offsets,
279+
var.fragment,
273280
span,
274281
);
275282
}
@@ -302,25 +309,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
302309
} else {
303310
(None, var.source_info.span)
304311
};
312+
let (var_ty, var_kind) = match var.contents {
313+
mir::VarDebugInfoContents::Compact(place) => (
314+
self.monomorphized_place_ty(place.as_ref()),
315+
if self.mir.local_kind(place.local) == mir::LocalKind::Arg
316+
&& place.projection.is_empty()
317+
{
318+
// FIXME(eddyb, #67586) take `var.source_info.scope` into
319+
// account to avoid using `ArgumentVariable` more than once
320+
// per argument local.
321+
322+
let arg_index = place.local.index() - 1;
323+
324+
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
325+
// offset in closures to account for the hidden environment?
326+
// Also, is this `+ 1` needed at all?
327+
VariableKind::ArgumentVariable(arg_index + 1)
328+
} else {
329+
VariableKind::LocalVariable
330+
},
331+
),
332+
mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
333+
(self.monomorphize(&ty), VariableKind::LocalVariable)
334+
}
335+
};
305336
let dbg_var = scope.map(|scope| {
306-
let place = var.place;
307-
let var_ty = self.monomorphized_place_ty(place.as_ref());
308-
let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
309-
&& place.projection.is_empty()
310-
{
311-
// FIXME(eddyb, #67586) take `var.source_info.scope` into
312-
// account to avoid using `ArgumentVariable` more than once
313-
// per argument local.
314-
315-
let arg_index = place.local.index() - 1;
316-
317-
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
318-
// offset in closures to account for the hidden environment?
319-
// Also, is this `+ 1` needed at all?
320-
VariableKind::ArgumentVariable(arg_index + 1)
321-
} else {
322-
VariableKind::LocalVariable
323-
};
324337
self.cx.create_dbg_var(
325338
self.debug_context.as_ref().unwrap(),
326339
var.name,
@@ -331,12 +344,54 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
331344
)
332345
});
333346

334-
per_local[var.place.local].push(PerLocalVarDebugInfo {
335-
name: var.name,
336-
source_info: var.source_info,
337-
dbg_var,
338-
projection: var.place.projection,
339-
});
347+
match &var.contents {
348+
mir::VarDebugInfoContents::Compact(place) => {
349+
per_local[place.local].push(PerLocalVarDebugInfo {
350+
name: var.name,
351+
source_info: var.source_info,
352+
dbg_var,
353+
fragment: None,
354+
projection: place.projection,
355+
});
356+
}
357+
mir::VarDebugInfoContents::Composite { ty: _, fragments } => {
358+
let var_layout = self.cx.layout_of(var_ty);
359+
for fragment in fragments {
360+
let mut fragment_start = Size::ZERO;
361+
let mut fragment_layout = var_layout;
362+
363+
for elem in &fragment.projection {
364+
match *elem {
365+
mir::ProjectionElem::Field(field, _) => {
366+
let i = field.index();
367+
fragment_start += fragment_layout.fields.offset(i);
368+
fragment_layout = fragment_layout.field(self.cx, i);
369+
}
370+
_ => span_bug!(
371+
var.source_info.span,
372+
"unsupported fragment projection `{:?}`",
373+
elem,
374+
),
375+
}
376+
}
377+
378+
let place = fragment.contents;
379+
per_local[place.local].push(PerLocalVarDebugInfo {
380+
name: var.name,
381+
source_info: var.source_info,
382+
dbg_var,
383+
fragment: if fragment_layout.size == var_layout.size {
384+
// Fragment covers entire variable, so as far as
385+
// DWARF is concerned, it's not really a fragment.
386+
None
387+
} else {
388+
Some(fragment_start..fragment_start + fragment_layout.size)
389+
},
390+
projection: place.projection,
391+
});
392+
}
393+
}
394+
}
340395
}
341396
Some(per_local)
342397
}

0 commit comments

Comments
 (0)