Skip to content

Represent MIR composite debuginfo as projections instead of aggregates #115252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 6, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 77 additions & 78 deletions compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Original file line number Diff line number Diff line change
@@ -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,
});
}
@@ -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,
});
}
}
}
58 changes: 26 additions & 32 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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),
);
}
}
}
194 changes: 97 additions & 97 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -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.
@@ -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,

@@ -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>,

@@ -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

@@ -1575,7 +1561,7 @@ impl<V, T> ProjectionElem<V, T> {
/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
pub type ProjectionKind = ProjectionElem<(), ()>;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct PlaceRef<'tcx> {
pub local: Local,
pub projection: &'tcx [PlaceElem<'tcx>],
@@ -1753,67 +1739,81 @@ impl<'tcx> PlaceRef<'tcx> {

impl Debug for Place<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for elem in self.projection.iter().rev() {
match elem {
ProjectionElem::OpaqueCast(_)
| ProjectionElem::Downcast(_, _)
| ProjectionElem::Field(_, _) => {
write!(fmt, "(").unwrap();
}
ProjectionElem::Deref => {
write!(fmt, "(*").unwrap();
}
ProjectionElem::Index(_)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {}
}
}
self.as_ref().fmt(fmt)
}
}

impl Debug for PlaceRef<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
pre_fmt_projection(self.projection, fmt)?;
write!(fmt, "{:?}", self.local)?;
post_fmt_projection(self.projection, fmt)
}
}

for elem in self.projection.iter() {
match elem {
ProjectionElem::OpaqueCast(ty) => {
write!(fmt, " as {ty})")?;
}
ProjectionElem::Downcast(Some(name), _index) => {
write!(fmt, " as {name})")?;
}
ProjectionElem::Downcast(None, index) => {
write!(fmt, " as variant#{index:?})")?;
}
ProjectionElem::Deref => {
write!(fmt, ")")?;
}
ProjectionElem::Field(field, ty) => {
write!(fmt, ".{:?}: {:?})", field.index(), ty)?;
}
ProjectionElem::Index(ref index) => {
write!(fmt, "[{index:?}]")?;
}
ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
write!(fmt, "[{offset:?} of {min_length:?}]")?;
}
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
write!(fmt, "[-{offset:?} of {min_length:?}]")?;
}
ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
write!(fmt, "[{from:?}:]")?;
}
ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
write!(fmt, "[:-{to:?}]")?;
}
ProjectionElem::Subslice { from, to, from_end: true } => {
write!(fmt, "[{from:?}:-{to:?}]")?;
}
ProjectionElem::Subslice { from, to, from_end: false } => {
write!(fmt, "[{from:?}..{to:?}]")?;
}
fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
for &elem in projection.iter().rev() {
match elem {
ProjectionElem::OpaqueCast(_)
| ProjectionElem::Downcast(_, _)
| ProjectionElem::Field(_, _) => {
write!(fmt, "(").unwrap();
}
ProjectionElem::Deref => {
write!(fmt, "(*").unwrap();
}
ProjectionElem::Index(_)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {}
}
}

Ok(())
Ok(())
}

fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
for &elem in projection.iter() {
match elem {
ProjectionElem::OpaqueCast(ty) => {
write!(fmt, " as {ty})")?;
}
ProjectionElem::Downcast(Some(name), _index) => {
write!(fmt, " as {name})")?;
}
ProjectionElem::Downcast(None, index) => {
write!(fmt, " as variant#{index:?})")?;
}
ProjectionElem::Deref => {
write!(fmt, ")")?;
}
ProjectionElem::Field(field, ty) => {
write!(fmt, ".{:?}: {:?})", field.index(), ty)?;
}
ProjectionElem::Index(ref index) => {
write!(fmt, "[{index:?}]")?;
}
ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
write!(fmt, "[{offset:?} of {min_length:?}]")?;
}
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
write!(fmt, "[-{offset:?} of {min_length:?}]")?;
}
ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
write!(fmt, "[{from:?}:]")?;
}
ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
write!(fmt, "[:-{to:?}]")?;
}
ProjectionElem::Subslice { from, to, from_end: true } => {
write!(fmt, "[{from:?}:-{to:?}]")?;
}
ProjectionElem::Subslice { from, to, from_end: false } => {
write!(fmt, "[{from:?}..{to:?}]")?;
}
}
}

Ok(())
}

///////////////////////////////////////////////////////////////////////////
@@ -3056,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
@@ -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!(
19 changes: 8 additions & 11 deletions compiler/rustc_middle/src/mir/visit.rs
Original file line number Diff line number Diff line change
@@ -838,12 +838,20 @@ macro_rules! make_mir_visitor {
let VarDebugInfo {
name: _,
source_info,
composite,
value,
argument_index: _,
} = var_debug_info;

self.visit_source_info(source_info);
let location = Location::START;
if let Some(box VarDebugInfoFragment { ref $($mutability)? ty, ref $($mutability)? projection }) = composite {
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
for elem in projection {
let ProjectionElem::Field(_, ty) = elem else { bug!() };
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
}
match value {
VarDebugInfoContents::Const(c) => self.visit_constant(c, location),
VarDebugInfoContents::Place(place) =>
@@ -852,17 +860,6 @@ macro_rules! make_mir_visitor {
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location
),
VarDebugInfoContents::Composite { ty, fragments } => {
// FIXME(eddyb) use a better `TyContext` here.
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
for VarDebugInfoFragment { projection: _, contents } in fragments {
self.visit_place(
contents,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location,
);
}
}
}
}

1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/build/custom/parse.rs
Original file line number Diff line number Diff line change
@@ -241,6 +241,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
let dbginfo = VarDebugInfo {
name,
source_info: SourceInfo { span, scope: self.source_scope },
composite: None,
argument_index: None,
value,
};
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
@@ -2287,6 +2287,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
name,
source_info: debug_source_info,
value: VarDebugInfoContents::Place(for_arm_body.into()),
composite: None,
argument_index: None,
});
let locals = if has_guard.0 {
@@ -2306,6 +2307,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
name,
source_info: debug_source_info,
value: VarDebugInfoContents::Place(ref_for_guard.into()),
composite: None,
argument_index: None,
});
LocalsForNode::ForGuard { ref_for_guard, for_arm_body }
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
@@ -823,6 +823,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
name,
source_info: SourceInfo::outermost(captured_place.var_ident.span),
value: VarDebugInfoContents::Place(use_place),
composite: None,
argument_index: None,
});

@@ -852,6 +853,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
name,
source_info,
value: VarDebugInfoContents::Place(arg_local.into()),
composite: None,
argument_index: Some(argument_index as u16 + 1),
});
}
5 changes: 0 additions & 5 deletions compiler/rustc_mir_transform/src/remove_zsts.rs
Original file line number Diff line number Diff line change
@@ -87,11 +87,6 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(place_ty))
}
}
VarDebugInfoContents::Composite { ty, fragments: _ } => {
if self.known_to_be_zst(ty) {
var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(ty))
}
}
}
}

93 changes: 36 additions & 57 deletions compiler/rustc_mir_transform/src/sroa.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::MirPass;
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_index::bit_set::{BitSet, GrowableBitSet};
use rustc_index::IndexVec;
use rustc_middle::mir::patch::MirPatch;
@@ -147,7 +148,7 @@ fn escaping_locals<'tcx>(
}

// We ignore anything that happens in debuginfo, since we expand it using
// `VarDebugInfoContents::Composite`.
// `VarDebugInfoFragment`.
fn visit_var_debug_info(&mut self, _: &VarDebugInfo<'tcx>) {}
}
}
@@ -246,17 +247,15 @@ fn replace_flattened_locals<'tcx>(
for (index, annotation) in body.user_type_annotations.iter_enumerated_mut() {
visitor.visit_user_type_annotation(index, annotation);
}
for var_debug_info in &mut body.var_debug_info {
visitor.visit_var_debug_info(var_debug_info);
}
visitor.expand_var_debug_info(&mut body.var_debug_info);
let ReplacementVisitor { patch, all_dead_locals, .. } = visitor;
patch.apply(body);
all_dead_locals
}

struct ReplacementVisitor<'tcx, 'll> {
tcx: TyCtxt<'tcx>,
/// This is only used to compute the type for `VarDebugInfoContents::Composite`.
/// This is only used to compute the type for `VarDebugInfoFragment`.
local_decls: &'ll LocalDecls<'tcx>,
/// Work to do.
replacements: &'ll ReplacementMap<'tcx>,
@@ -266,16 +265,38 @@ struct ReplacementVisitor<'tcx, 'll> {
}

impl<'tcx> ReplacementVisitor<'tcx, '_> {
fn gather_debug_info_fragments(&self, local: Local) -> Option<Vec<VarDebugInfoFragment<'tcx>>> {
let mut fragments = Vec::new();
let parts = self.replacements.place_fragments(local.into())?;
for (field, ty, replacement_local) in parts {
fragments.push(VarDebugInfoFragment {
projection: vec![PlaceElem::Field(field, ty)],
contents: Place::from(replacement_local),
});
}
Some(fragments)
#[instrument(level = "trace", skip(self))]
fn expand_var_debug_info(&mut self, var_debug_info: &mut Vec<VarDebugInfo<'tcx>>) {
var_debug_info.flat_map_in_place(|mut var_debug_info| {
let place = match var_debug_info.value {
VarDebugInfoContents::Const(_) => return vec![var_debug_info],
VarDebugInfoContents::Place(ref mut place) => place,
};

if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
*place = repl;
return vec![var_debug_info];
}

let Some(parts) = self.replacements.place_fragments(*place) else {
return vec![var_debug_info];
};

let ty = place.ty(self.local_decls, self.tcx).ty;

parts
.map(|(field, field_ty, replacement_local)| {
let mut var_debug_info = var_debug_info.clone();
let composite = var_debug_info.composite.get_or_insert_with(|| {
Box::new(VarDebugInfoFragment { ty, projection: Vec::new() })
});
composite.projection.push(PlaceElem::Field(field, field_ty));

var_debug_info.value = VarDebugInfoContents::Place(replacement_local.into());
var_debug_info
})
.collect()
});
}
}

@@ -422,48 +443,6 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
self.super_statement(statement, location)
}

#[instrument(level = "trace", skip(self))]
fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
match &mut var_debug_info.value {
VarDebugInfoContents::Place(ref mut place) => {
if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
*place = repl;
} else if let Some(local) = place.as_local()
&& let Some(fragments) = self.gather_debug_info_fragments(local)
{
let ty = place.ty(self.local_decls, self.tcx).ty;
var_debug_info.value = VarDebugInfoContents::Composite { ty, fragments };
}
}
VarDebugInfoContents::Composite { ty: _, ref mut fragments } => {
let mut new_fragments = Vec::new();
debug!(?fragments);
fragments.retain_mut(|fragment| {
if let Some(repl) =
self.replacements.replace_place(self.tcx, fragment.contents.as_ref())
{
fragment.contents = repl;
true
} else if let Some(local) = fragment.contents.as_local()
&& let Some(frg) = self.gather_debug_info_fragments(local)
{
new_fragments.extend(frg.into_iter().map(|mut f| {
f.projection.splice(0..0, fragment.projection.iter().copied());
f
}));
false
} else {
true
}
});
debug!(?fragments);
debug!(?new_fragments);
fragments.extend(new_fragments);
}
VarDebugInfoContents::Const(_) => {}
}
}

fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
assert!(!self.all_dead_locals.contains(*local));
}
12 changes: 10 additions & 2 deletions tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
Original file line number Diff line number Diff line change
@@ -33,14 +33,22 @@
let _15: bool;
let _16: u32;
scope 6 {
debug f => (bool, bool, u32){ .0 => _14, .1 => _15, .2 => _16, };
- debug ((f: (bool, bool, u32)).0: bool) => _14;
- debug ((f: (bool, bool, u32)).1: bool) => _15;
- debug ((f: (bool, bool, u32)).2: u32) => _16;
+ debug ((f: (bool, bool, u32)).0: bool) => const true;
+ debug ((f: (bool, bool, u32)).1: bool) => const false;
+ debug ((f: (bool, bool, u32)).2: u32) => const 123_u32;
let _10: std::option::Option<u16>;
scope 7 {
debug o => _10;
let _17: u32;
let _18: u32;
scope 8 {
debug p => Point{ .0 => _17, .1 => _18, };
- debug ((p: Point).0: u32) => _17;
- debug ((p: Point).1: u32) => _18;
+ debug ((p: Point).0: u32) => const 32_u32;
+ debug ((p: Point).1: u32) => const 32_u32;
let _11: u32;
scope 9 {
- debug a => _11;
Original file line number Diff line number Diff line change
@@ -8,19 +8,22 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
let mut _4: usize;
scope 1 (inlined core::slice::<impl [u32]>::get_unchecked_mut::<std::ops::Range<usize>>) {
debug self => _1;
debug index => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
debug ((index: std::ops::Range<usize>).0: usize) => _3;
debug ((index: std::ops::Range<usize>).1: usize) => _4;
let mut _5: *mut [u32];
let mut _13: *mut [u32];
scope 2 {
scope 3 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked_mut) {
debug self => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
debug ((self: std::ops::Range<usize>).0: usize) => _3;
debug ((self: std::ops::Range<usize>).1: usize) => _4;
debug slice => _5;
let mut _7: *mut u32;
let mut _8: *mut u32;
let _15: usize;
let _16: usize;
scope 4 {
debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
debug ((this: std::ops::Range<usize>).0: usize) => _15;
debug ((this: std::ops::Range<usize>).1: usize) => _16;
scope 5 {
let _6: usize;
scope 6 {
@@ -53,7 +56,8 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
}
}
scope 7 (inlined <std::ops::Range<usize> as SliceIndex<[T]>>::get_unchecked_mut::runtime::<u32>) {
debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
debug ((this: std::ops::Range<usize>).0: usize) => _15;
debug ((this: std::ops::Range<usize>).1: usize) => _16;
debug slice => _5;
scope 8 (inlined ptr::mut_ptr::<impl *mut [u32]>::len) {
debug self => _5;
Original file line number Diff line number Diff line change
@@ -8,19 +8,22 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
let mut _4: usize;
scope 1 (inlined core::slice::<impl [u32]>::get_unchecked_mut::<std::ops::Range<usize>>) {
debug self => _1;
debug index => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
debug ((index: std::ops::Range<usize>).0: usize) => _3;
debug ((index: std::ops::Range<usize>).1: usize) => _4;
let mut _5: *mut [u32];
let mut _13: *mut [u32];
scope 2 {
scope 3 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked_mut) {
debug self => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
debug ((self: std::ops::Range<usize>).0: usize) => _3;
debug ((self: std::ops::Range<usize>).1: usize) => _4;
debug slice => _5;
let mut _7: *mut u32;
let mut _8: *mut u32;
let _15: usize;
let _16: usize;
scope 4 {
debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
debug ((this: std::ops::Range<usize>).0: usize) => _15;
debug ((this: std::ops::Range<usize>).1: usize) => _16;
scope 5 {
let _6: usize;
scope 6 {
@@ -53,7 +56,8 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
}
}
scope 7 (inlined <std::ops::Range<usize> as SliceIndex<[T]>>::get_unchecked_mut::runtime::<u32>) {
debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
debug ((this: std::ops::Range<usize>).0: usize) => _15;
debug ((this: std::ops::Range<usize>).1: usize) => _16;
debug slice => _5;
scope 8 (inlined ptr::mut_ptr::<impl *mut [u32]>::len) {
debug self => _5;
Original file line number Diff line number Diff line change
@@ -33,7 +33,8 @@
+ let _32: u32;
scope 1 {
- debug foo => _1;
+ debug foo => Foo<T>{ .0 => _31, .1 => _32, };
+ debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _31;
+ debug ((foo: Foo<T>).1: u32) => _32;
let _5: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
scope 2 {
debug x => _5;
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@
+ let _5: u8;
scope 1 {
- debug y => _1;
+ debug y => (usize, u8){ .0 => _4, .1 => _5, };
+ debug ((y: (usize, u8)).0: usize) => _4;
+ debug ((y: (usize, u8)).1: u8) => _5;
let _2: usize;
scope 2 {
debug t => _2;
Original file line number Diff line number Diff line change
@@ -11,7 +11,10 @@
+ let _14: std::option::Option<isize>;
scope 1 {
- debug y => _2;
+ debug y => Foo{ .0 => _11, .1 => _12, .2 => _13, .3 => _14, };
+ debug ((y: Foo).0: u8) => _11;
+ debug ((y: Foo).1: ()) => _12;
+ debug ((y: Foo).2: &str) => _13;
+ debug ((y: Foo).3: std::option::Option<isize>) => _14;
let _3: u8;
scope 2 {
debug t => _3;
@@ -25,7 +28,10 @@
+ let _10: std::option::Option<isize>;
scope 4 {
- debug z => _5;
+ debug z => Foo{ .0 => _7, .1 => _8, .2 => _9, .3 => _10, };
+ debug ((z: Foo).0: u8) => _7;
+ debug ((z: Foo).1: ()) => _8;
+ debug ((z: Foo).2: &str) => _9;
+ debug ((z: Foo).3: std::option::Option<isize>) => _10;
let _6: ();
scope 5 {
debug a => _6;
Original file line number Diff line number Diff line change
@@ -11,7 +11,10 @@
+ let _8: std::option::Option<isize>;
scope 1 {
- debug y => _2;
+ debug y => Foo{ .0 => _5, .1 => _6, .2 => _7, .3 => _8, };
+ debug ((y: Foo).0: u8) => _5;
+ debug ((y: Foo).1: ()) => _6;
+ debug ((y: Foo).2: &str) => _7;
+ debug ((y: Foo).3: std::option::Option<isize>) => _8;
let _3: u8;
scope 2 {
debug t => _3;