Skip to content

Commit

Permalink
Refactor how MIR represents composite debuginfo.
Browse files Browse the repository at this point in the history
  • Loading branch information
cjgillot committed Sep 5, 2023
1 parent 429a925 commit 26c48e6
Show file tree
Hide file tree
Showing 17 changed files with 223 additions and 244 deletions.
155 changes: 77 additions & 78 deletions compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,54 +484,89 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
None
};

let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
let (var_ty, var_kind) = match var.value {
let var_ty = if let Some(ref fragment) = var.composite {
self.monomorphize(fragment.ty)
} else {
match var.value {
mir::VarDebugInfoContents::Place(place) => {
let var_ty = self.monomorphized_place_ty(place.as_ref());
let var_kind = if let Some(arg_index) = var.argument_index
&& place.projection.is_empty()
{
let arg_index = arg_index as usize;
if target_is_msvc {
// ScalarPair parameters are spilled to the stack so they need to
// be marked as a `LocalVariable` for MSVC debuggers to visualize
// their data correctly. (See #81894 & #88625)
let var_ty_layout = self.cx.layout_of(var_ty);
if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
VariableKind::LocalVariable
} else {
VariableKind::ArgumentVariable(arg_index)
}
} else {
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
// offset in closures to account for the hidden environment?
VariableKind::ArgumentVariable(arg_index)
}
} else {
VariableKind::LocalVariable
};
(var_ty, var_kind)
self.monomorphized_place_ty(place.as_ref())
}
mir::VarDebugInfoContents::Const(c) => {
let ty = self.monomorphize(c.ty());
(ty, VariableKind::LocalVariable)
}
mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
let ty = self.monomorphize(ty);
(ty, VariableKind::LocalVariable)
mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
}
};

let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
let var_kind = if let Some(arg_index) = var.argument_index
&& var.composite.is_none()
&& let mir::VarDebugInfoContents::Place(place) = var.value
&& place.projection.is_empty()
{
let arg_index = arg_index as usize;
if target_is_msvc {
// ScalarPair parameters are spilled to the stack so they need to
// be marked as a `LocalVariable` for MSVC debuggers to visualize
// their data correctly. (See #81894 & #88625)
let var_ty_layout = self.cx.layout_of(var_ty);
if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
VariableKind::LocalVariable
} else {
VariableKind::ArgumentVariable(arg_index)
}
} else {
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
// offset in closures to account for the hidden environment?
VariableKind::ArgumentVariable(arg_index)
}
} else {
VariableKind::LocalVariable
};

self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
});

let fragment = if let Some(ref fragment) = var.composite {
let var_layout = self.cx.layout_of(var_ty);

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,
),
}
}

if fragment_layout.size == Size::ZERO {
// Fragment is a ZST, so does not represent anything. Avoid generating anything
// as this may conflict with a fragment that covers the entire variable.
continue;
} else 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)
}
} else {
None
};

match var.value {
mir::VarDebugInfoContents::Place(place) => {
per_local[place.local].push(PerLocalVarDebugInfo {
name: var.name,
source_info: var.source_info,
dbg_var,
fragment: None,
fragment,
projection: place.projection,
});
}
Expand All @@ -547,51 +582,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx,
);

bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
}
}
}
mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
let var_ty = self.monomorphize(ty);
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,
),
}
bx.dbg_var_addr(
dbg_var,
dbg_loc,
base.llval,
Size::ZERO,
&[],
fragment,
);
}

let place = fragment.contents;
let fragment = if fragment_layout.size == Size::ZERO {
// Fragment is a ZST, so does not represent anything.
continue;
} else 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)
};

per_local[place.local].push(PerLocalVarDebugInfo {
name: var.name,
source_info: var.source_info,
dbg_var,
fragment,
projection: place.projection,
});
}
}
}
Expand Down
58 changes: 26 additions & 32 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ use rustc_index::IndexVec;
use rustc_infer::traits::Reveal;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{
traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
MirPass, MirPhase, NonDivergingIntrinsic, NullOp, Operand, Place, PlaceElem, PlaceRef,
ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
Terminator, TerminatorKind, UnOp, UnwindAction, UnwindTerminateReason, VarDebugInfo,
VarDebugInfoContents, START_BLOCK,
};
use rustc_middle::mir::*;
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals;
Expand Down Expand Up @@ -757,37 +751,37 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}

fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
let check_place = |this: &mut Self, place: Place<'_>| {
if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
this.fail(
if let Some(box VarDebugInfoFragment { ty, ref projection }) = debuginfo.composite {
if ty.is_union() || ty.is_enum() {
self.fail(
START_BLOCK.start_location(),
format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
format!("invalid type {ty:?} in debuginfo for {:?}", debuginfo.name),
);
}
};
if projection.is_empty() {
self.fail(
START_BLOCK.start_location(),
format!("invalid empty projection in debuginfo for {:?}", debuginfo.name),
);
}
if projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
self.fail(
START_BLOCK.start_location(),
format!(
"illegal projection {:?} in debuginfo for {:?}",
projection, debuginfo.name
),
);
}
}
match debuginfo.value {
VarDebugInfoContents::Const(_) => {}
VarDebugInfoContents::Place(place) => {
check_place(self, place);
}
VarDebugInfoContents::Composite { ty, ref fragments } => {
for f in fragments {
check_place(self, f.contents);
if ty.is_union() || ty.is_enum() {
self.fail(
START_BLOCK.start_location(),
format!("invalid type {ty:?} for composite debuginfo"),
);
}
if f.projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
self.fail(
START_BLOCK.start_location(),
format!(
"illegal projection {:?} in debuginfo for {:?}",
f.projection, debuginfo.name
),
);
}
if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
self.fail(
START_BLOCK.start_location(),
format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
);
}
}
}
Expand Down
70 changes: 28 additions & 42 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1028,39 +1028,23 @@ pub enum VarDebugInfoContents<'tcx> {
/// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
Place(Place<'tcx>),
Const(Constant<'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.
/// This cannot contain a union or an enum.
ty: Ty<'tcx>,
/// All the parts of the original user variable, which ended
/// up in disjoint places, due to optimizations.
fragments: Vec<VarDebugInfoFragment<'tcx>>,
},
}

impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match self {
VarDebugInfoContents::Const(c) => write!(fmt, "{c}"),
VarDebugInfoContents::Place(p) => write!(fmt, "{p:?}"),
VarDebugInfoContents::Composite { ty, fragments } => {
write!(fmt, "{ty:?}{{ ")?;
for f in fragments.iter() {
write!(fmt, "{f:?}, ")?;
}
write!(fmt, "}}")
}
}
}
}

#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct VarDebugInfoFragment<'tcx> {
/// Type of the original user variable.
/// This cannot contain a union or an enum.
pub ty: Ty<'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.
Expand All @@ -1071,29 +1055,10 @@ pub struct VarDebugInfoFragment<'tcx> {
// to match on the discriminant, or by using custom type debuginfo
// with non-overlapping variants for the composite variable.
pub projection: Vec<PlaceElem<'tcx>>,

/// Where the data for this fragment can be found.
/// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
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)
}
}

/// Debug information pertaining to a user variable.
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct VarDebugInfo<'tcx> {
pub name: Symbol,

Expand All @@ -1102,6 +1067,13 @@ pub struct VarDebugInfo<'tcx> {
/// (see `LocalDecl`'s `source_info` field for more details).
pub source_info: SourceInfo,

/// 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.
pub composite: Option<Box<VarDebugInfoFragment<'tcx>>>,

/// Where the data for this user variable is to be found.
pub value: VarDebugInfoContents<'tcx>,

Expand All @@ -1111,6 +1083,20 @@ pub struct VarDebugInfo<'tcx> {
pub argument_index: Option<u16>,
}

impl Debug for VarDebugInfo<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
pre_fmt_projection(&projection[..], fmt)?;
write!(fmt, "({}: {})", self.name, ty)?;
post_fmt_projection(&projection[..], fmt)?;
} else {
write!(fmt, "{}", self.name)?;
}

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

///////////////////////////////////////////////////////////////////////////
// BasicBlock

Expand Down Expand Up @@ -3070,6 +3056,6 @@ mod size_asserts {
static_assert_size!(StatementKind<'_>, 16);
static_assert_size!(Terminator<'_>, 104);
static_assert_size!(TerminatorKind<'_>, 88);
static_assert_size!(VarDebugInfo<'_>, 80);
static_assert_size!(VarDebugInfo<'_>, 88);
// tidy-alphabetical-end
}
5 changes: 1 addition & 4 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,10 +554,7 @@ fn write_scope_tree(
continue;
}

let indented_debug_info = format!(
"{0:1$}debug {2} => {3:?};",
INDENT, indent, var_debug_info.name, var_debug_info.value,
);
let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);

if tcx.sess.opts.unstable_opts.mir_include_spans {
writeln!(
Expand Down
Loading

0 comments on commit 26c48e6

Please sign in to comment.