Skip to content

Commit

Permalink
rustc_mir: add debuginfo support to fragment_locals.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Jan 29, 2020
1 parent dd79633 commit b1133d3
Show file tree
Hide file tree
Showing 14 changed files with 349 additions and 74 deletions.
62 changes: 58 additions & 4 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,10 +948,64 @@ pub struct VarDebugInfo<'tcx> {
/// (see `LocalDecl`'s `source_info` field for more details).
pub source_info: SourceInfo,

/// Where the data for this user variable is to be found.
/// NOTE(eddyb) There's an unenforced invariant that this `Place` is
/// based on a `Local`, not a `Static`, and contains no indexing.
pub place: Place<'tcx>,
/// How the contents of this user variable are represented.
pub contents: VarDebugInfoContents<'tcx>,
}

#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
pub enum VarDebugInfoContents<'tcx> {
/// The user variable's data is entirely in one `Place`.
// NOTE(eddyb) there's an unenforced invariant that this `Place` is
// contains no indexing (with a non-constant index).
Compact(Place<'tcx>),

/// The user variable's data is split across several fragments,
/// each described by a `VarDebugInfoFragment`.
/// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
/// and LLVM's `DW_OP_LLVM_fragment` for more details on
/// the underlying debuginfo feature this relies on.
Composite {
/// Type of the original user variable.
ty: Ty<'tcx>,

/// All the parts of the original user variable, which ended
/// up in disjoint places, due to optimizations.
fragments: Vec<VarDebugInfoFragment<'tcx>>,
},
}

#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
pub struct VarDebugInfoFragment<'tcx> {
/// Where in the composite user variable this fragment is,
/// represented as a "projection" into the composite variable.
/// At lower levels, this corresponds to a byte/bit range.
// NOTE(eddyb) there's an unenforced invariant that this contains
// only `Field`s, and not into `enum` variants or `union`s.
// FIXME(eddyb) support this for `enum`s by either using DWARF's
// more advanced control-flow features (unsupported by LLVM?)
// to match on the discriminant, or by using custom type debuginfo
// with non-overlapping variants for the composite variable.
pub projection: Vec<ProjectionKind>,

/// Where the data for this fragment can be found.
// NOTE(eddyb) There's an unenforced invariant that this `Place` is
// contains no indexing (with a non-constant index).
pub contents: Place<'tcx>,
}

impl Debug for VarDebugInfoFragment<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for elem in self.projection.iter() {
match elem {
ProjectionElem::Field(field, _) => {
write!(fmt, ".{:?}", field.index())?;
}
_ => bug!("unsupported fragment projection `{:?}`", elem),
}
}

write!(fmt, " => {:?}", self.contents)
}
}

///////////////////////////////////////////////////////////////////////////
Expand Down
27 changes: 21 additions & 6 deletions src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,16 +739,31 @@ macro_rules! make_mir_visitor {
let VarDebugInfo {
name: _,
source_info,
place,
contents,
} = var_debug_info;

self.visit_source_info(source_info);
let location = START_BLOCK.start_location();
self.visit_place(
place,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location,
);
match contents {
VarDebugInfoContents::Compact(place) => {
self.visit_place(
place,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location,
);
}
VarDebugInfoContents::Composite { ty, fragments } => {
// FIXME(eddyb) use a better `TyContext` here.
self.visit_ty(ty, TyContext::Location(location));
for VarDebugInfoFragment { projection: _, contents } in fragments {
self.visit_place(
contents,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location,
);
}
}
}
}

fn super_source_scope(&mut self,
Expand Down
8 changes: 6 additions & 2 deletions src/librustc_codegen_llvm/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1288,10 +1288,14 @@ fn generator_layout_and_saved_local_names(

let state_arg = mir::Local::new(1);
for var in &body.var_debug_info {
if var.place.local != state_arg {
let place = match var.contents {
mir::VarDebugInfoContents::Compact(place) => place,
mir::VarDebugInfoContents::Composite { .. } => continue,
};
if place.local != state_arg {
continue;
}
match var.place.projection[..] {
match place.projection[..] {
[
// Deref of the `Pin<&mut Self>` state argument.
mir::ProjectionElem::Field(..),
Expand Down
29 changes: 20 additions & 9 deletions src/librustc_codegen_llvm/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use libc::c_uint;
use log::debug;
use std::cell::RefCell;
use std::ffi::CString;
use std::ops::Range;

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

let loc = span_start(cx, span);

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

if direct_offset.bytes() > 0 {
addr_ops.push(op_plus_uconst());
addr_ops.push(direct_offset.bytes() as i64);
dwarf_ops.push(op_plus_uconst());
dwarf_ops.push(direct_offset.bytes() as i64);
}
for &offset in indirect_offsets {
addr_ops.push(op_deref());
dwarf_ops.push(op_deref());
if offset.bytes() > 0 {
addr_ops.push(op_plus_uconst());
addr_ops.push(offset.bytes() as i64);
dwarf_ops.push(op_plus_uconst());
dwarf_ops.push(offset.bytes() as i64);
}
}

if let Some(fragment) = fragment {
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
// offset and size, both of them in bits.
dwarf_ops.push(op_llvm_fragment());
dwarf_ops.push(fragment.start.bits() as i64);
dwarf_ops.push((fragment.end - fragment.start).bits() as i64);
}

// FIXME(eddyb) maybe this information could be extracted from `var`,
// to avoid having to pass it down in both places?
source_loc::set_debug_location(
Expand All @@ -191,8 +202,8 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> {
DIB(cx),
variable_alloca,
dbg_var,
addr_ops.as_ptr(),
addr_ops.len() as c_uint,
dwarf_ops.as_ptr(),
dwarf_ops.len() as c_uint,
debug_loc,
self.llbb(),
);
Expand Down
1 change: 1 addition & 0 deletions src/librustc_codegen_llvm/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1809,6 +1809,7 @@ extern "C" {
) -> &'a Value;
pub fn LLVMRustDIBuilderCreateOpDeref() -> i64;
pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> i64;
pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> i64;

#[allow(improper_ctypes)]
pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);
Expand Down
113 changes: 84 additions & 29 deletions src/librustc_codegen_ssa/mir/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use rustc::ty;
use rustc::ty::layout::{LayoutOf, Size};
use rustc_hir::def_id::CrateNum;
use rustc_index::vec::IndexVec;

use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, Span};
use std::ops::Range;

use super::OperandValue;
use super::{FunctionCx, LocalRef};
Expand All @@ -25,14 +25,18 @@ pub enum VariableKind {
}

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

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

/// Byte range in the `dbg_var` covered by this fragment,
/// if this is a fragment of a composite `VarDebugInfo`.
pub fragment: Option<Range<Size>>,

/// `.place.projection` from `mir::VarDebugInfo`.
pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
}
Expand Down Expand Up @@ -127,7 +131,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(per_local) => &per_local[local],
None => return,
};
let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
let whole_local_var =
vars.iter().find(|var| var.fragment.is_none() && var.projection.is_empty()).cloned();
let has_proj = || vars.iter().any(|var| !var.projection.is_empty());

let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
Expand Down Expand Up @@ -173,6 +178,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
name,
source_info: decl.source_info,
dbg_var,
fragment: None,
projection: ty::List::empty(),
})
}
Expand All @@ -183,7 +189,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let local_ref = &self.locals[local];

if !bx.sess().fewer_names() {
let name = match whole_local_var.or(fallback_var) {
let name = match whole_local_var.or(fallback_var.clone()) {
Some(var) if var.name != kw::Invalid => var.name.to_string(),
_ => format!("{:?}", local),
};
Expand Down Expand Up @@ -221,7 +227,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
_ => return,
};

let vars = vars.iter().copied().chain(fallback_var);
let vars = vars.iter().cloned().chain(fallback_var);

for var in vars {
let mut layout = base.layout;
Expand Down Expand Up @@ -270,6 +276,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
base.llval,
direct_offset,
&indirect_offsets,
var.fragment,
span,
);
}
Expand Down Expand Up @@ -302,25 +309,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} else {
(None, var.source_info.span)
};
let (var_ty, var_kind) = match var.contents {
mir::VarDebugInfoContents::Compact(place) => (
self.monomorphized_place_ty(place.as_ref()),
if self.mir.local_kind(place.local) == mir::LocalKind::Arg
&& place.projection.is_empty()
{
// FIXME(eddyb, #67586) take `var.source_info.scope` into
// account to avoid using `ArgumentVariable` more than once
// per argument local.

let arg_index = place.local.index() - 1;

// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
// offset in closures to account for the hidden environment?
// Also, is this `+ 1` needed at all?
VariableKind::ArgumentVariable(arg_index + 1)
} else {
VariableKind::LocalVariable
},
),
mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
(self.monomorphize(&ty), VariableKind::LocalVariable)
}
};
let dbg_var = scope.map(|scope| {
let place = var.place;
let var_ty = self.monomorphized_place_ty(place.as_ref());
let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
&& place.projection.is_empty()
{
// FIXME(eddyb, #67586) take `var.source_info.scope` into
// account to avoid using `ArgumentVariable` more than once
// per argument local.

let arg_index = place.local.index() - 1;

// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
// offset in closures to account for the hidden environment?
// Also, is this `+ 1` needed at all?
VariableKind::ArgumentVariable(arg_index + 1)
} else {
VariableKind::LocalVariable
};
self.cx.create_dbg_var(
self.debug_context.as_ref().unwrap(),
var.name,
Expand All @@ -331,12 +344,54 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
)
});

per_local[var.place.local].push(PerLocalVarDebugInfo {
name: var.name,
source_info: var.source_info,
dbg_var,
projection: var.place.projection,
});
match &var.contents {
mir::VarDebugInfoContents::Compact(place) => {
per_local[place.local].push(PerLocalVarDebugInfo {
name: var.name,
source_info: var.source_info,
dbg_var,
fragment: None,
projection: place.projection,
});
}
mir::VarDebugInfoContents::Composite { ty: _, fragments } => {
let var_layout = self.cx.layout_of(var_ty);
for fragment in fragments {
let mut fragment_start = Size::ZERO;
let mut fragment_layout = var_layout;

for elem in &fragment.projection {
match *elem {
mir::ProjectionElem::Field(field, _) => {
let i = field.index();
fragment_start += fragment_layout.fields.offset(i);
fragment_layout = fragment_layout.field(self.cx, i);
}
_ => span_bug!(
var.source_info.span,
"unsupported fragment projection `{:?}`",
elem,
),
}
}

let place = fragment.contents;
per_local[place.local].push(PerLocalVarDebugInfo {
name: var.name,
source_info: var.source_info,
dbg_var,
fragment: if fragment_layout.size == var_layout.size {
// Fragment covers entire variable, so as far as
// DWARF is concerned, it's not really a fragment.
None
} else {
Some(fragment_start..fragment_start + fragment_layout.size)
},
projection: place.projection,
});
}
}
}
}
Some(per_local)
}
Expand Down
Loading

0 comments on commit b1133d3

Please sign in to comment.