2022-05-23 07:27:44 +00:00
|
|
|
use crate::MirPass;
|
2023-01-21 21:53:26 +00:00
|
|
|
use rustc_index::bit_set::{BitSet, GrowableBitSet};
|
2022-05-23 07:27:44 +00:00
|
|
|
use rustc_index::vec::IndexVec;
|
2023-02-04 14:39:42 +00:00
|
|
|
use rustc_middle::mir::patch::MirPatch;
|
2022-05-23 07:27:44 +00:00
|
|
|
use rustc_middle::mir::visit::*;
|
|
|
|
use rustc_middle::mir::*;
|
2023-02-05 13:35:33 +00:00
|
|
|
use rustc_middle::ty::{Ty, TyCtxt};
|
2023-02-05 12:08:42 +00:00
|
|
|
use rustc_mir_dataflow::value_analysis::{excluded_locals, iter_fields};
|
2022-05-23 07:27:44 +00:00
|
|
|
|
|
|
|
pub struct ScalarReplacementOfAggregates;
|
|
|
|
|
|
|
|
impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates {
|
|
|
|
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
|
2022-11-08 17:38:21 +00:00
|
|
|
sess.mir_opt_level() >= 3
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
|
2023-02-04 14:39:42 +00:00
|
|
|
#[instrument(level = "debug", skip(self, tcx, body))]
|
2022-05-23 07:27:44 +00:00
|
|
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
2023-02-04 14:39:42 +00:00
|
|
|
debug!(def_id = ?body.source.def_id());
|
2023-02-05 12:08:42 +00:00
|
|
|
let mut excluded = excluded_locals(body);
|
|
|
|
loop {
|
|
|
|
debug!(?excluded);
|
|
|
|
let escaping = escaping_locals(&excluded, body);
|
|
|
|
debug!(?escaping);
|
|
|
|
let replacements = compute_flattening(tcx, body, escaping);
|
|
|
|
debug!(?replacements);
|
|
|
|
let all_dead_locals = replace_flattened_locals(tcx, body, replacements);
|
2023-02-05 13:35:33 +00:00
|
|
|
if !all_dead_locals.is_empty() {
|
2023-01-21 21:53:26 +00:00
|
|
|
excluded.union(&all_dead_locals);
|
|
|
|
excluded = {
|
|
|
|
let mut growable = GrowableBitSet::from(excluded);
|
|
|
|
growable.ensure(body.local_decls.len());
|
|
|
|
growable.into()
|
|
|
|
};
|
2023-02-05 12:08:42 +00:00
|
|
|
} else {
|
2023-02-05 13:35:33 +00:00
|
|
|
break;
|
2023-02-05 12:08:42 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Identify all locals that are not eligible for SROA.
|
|
|
|
///
|
|
|
|
/// There are 3 cases:
|
2023-02-05 12:08:42 +00:00
|
|
|
/// - the aggregated local is used or passed to other code (function parameters and arguments);
|
2022-05-23 07:27:44 +00:00
|
|
|
/// - the locals is a union or an enum;
|
|
|
|
/// - the local's address is taken, and thus the relative addresses of the fields are observable to
|
|
|
|
/// client code.
|
2023-01-21 21:53:26 +00:00
|
|
|
fn escaping_locals(excluded: &BitSet<Local>, body: &Body<'_>) -> BitSet<Local> {
|
2022-05-23 07:27:44 +00:00
|
|
|
let mut set = BitSet::new_empty(body.local_decls.len());
|
|
|
|
set.insert_range(RETURN_PLACE..=Local::from_usize(body.arg_count));
|
|
|
|
for (local, decl) in body.local_decls().iter_enumerated() {
|
2023-01-21 21:53:26 +00:00
|
|
|
if decl.ty.is_union() || decl.ty.is_enum() || excluded.contains(local) {
|
2022-05-23 07:27:44 +00:00
|
|
|
set.insert(local);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut visitor = EscapeVisitor { set };
|
|
|
|
visitor.visit_body(body);
|
|
|
|
return visitor.set;
|
|
|
|
|
|
|
|
struct EscapeVisitor {
|
|
|
|
set: BitSet<Local>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> Visitor<'tcx> for EscapeVisitor {
|
|
|
|
fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) {
|
|
|
|
self.set.insert(local);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
|
|
|
|
// Mirror the implementation in PreFlattenVisitor.
|
|
|
|
if let &[PlaceElem::Field(..), ..] = &place.projection[..] {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
self.super_place(place, context, location);
|
|
|
|
}
|
|
|
|
|
2023-02-04 14:39:42 +00:00
|
|
|
fn visit_assign(
|
|
|
|
&mut self,
|
|
|
|
lvalue: &Place<'tcx>,
|
|
|
|
rvalue: &Rvalue<'tcx>,
|
|
|
|
location: Location,
|
|
|
|
) {
|
2023-02-05 09:31:27 +00:00
|
|
|
if lvalue.as_local().is_some() {
|
|
|
|
match rvalue {
|
|
|
|
// Aggregate assignments are expanded in run_pass.
|
|
|
|
Rvalue::Aggregate(..) | Rvalue::Use(..) => {
|
|
|
|
self.visit_rvalue(rvalue, location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2023-02-04 14:39:42 +00:00
|
|
|
}
|
|
|
|
self.super_assign(lvalue, rvalue, location)
|
|
|
|
}
|
|
|
|
|
2022-05-23 07:27:44 +00:00
|
|
|
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
2023-02-04 14:39:42 +00:00
|
|
|
match statement.kind {
|
2022-05-23 07:27:44 +00:00
|
|
|
// Storage statements are expanded in run_pass.
|
2023-02-04 14:39:42 +00:00
|
|
|
StatementKind::StorageLive(..)
|
|
|
|
| StatementKind::StorageDead(..)
|
|
|
|
| StatementKind::Deinit(..) => return,
|
|
|
|
_ => self.super_statement(statement, location),
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We ignore anything that happens in debuginfo, since we expand it using
|
|
|
|
// `VarDebugInfoContents::Composite`.
|
|
|
|
fn visit_var_debug_info(&mut self, _: &VarDebugInfo<'tcx>) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default, Debug)]
|
|
|
|
struct ReplacementMap<'tcx> {
|
2023-02-05 11:37:44 +00:00
|
|
|
/// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
|
|
|
|
/// and deinit statement and debuginfo.
|
2023-02-05 13:35:33 +00:00
|
|
|
fragments: IndexVec<Local, Option<IndexVec<Field, Option<(Ty<'tcx>, Local)>>>>,
|
2023-02-05 11:37:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> ReplacementMap<'tcx> {
|
2023-02-05 13:35:33 +00:00
|
|
|
fn replace_place(&self, tcx: TyCtxt<'tcx>, place: PlaceRef<'tcx>) -> Option<Place<'tcx>> {
|
|
|
|
let &[PlaceElem::Field(f, _), ref rest @ ..] = place.projection else { return None; };
|
|
|
|
let fields = self.fragments[place.local].as_ref()?;
|
|
|
|
let (_, new_local) = fields[f]?;
|
|
|
|
Some(Place { local: new_local, projection: tcx.intern_place_elems(&rest) })
|
2023-02-05 11:37:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn place_fragments(
|
|
|
|
&self,
|
|
|
|
place: Place<'tcx>,
|
2023-02-05 13:35:33 +00:00
|
|
|
) -> Option<impl Iterator<Item = (Field, Ty<'tcx>, Local)> + '_> {
|
2023-02-05 11:37:44 +00:00
|
|
|
let local = place.as_local()?;
|
2023-02-05 13:35:33 +00:00
|
|
|
let fields = self.fragments[local].as_ref()?;
|
|
|
|
Some(fields.iter_enumerated().filter_map(|(field, &opt_ty_local)| {
|
|
|
|
let (ty, local) = opt_ty_local?;
|
|
|
|
Some((field, ty, local))
|
|
|
|
}))
|
2023-02-05 11:37:44 +00:00
|
|
|
}
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute the replacement of flattened places into locals.
|
|
|
|
///
|
|
|
|
/// For each eligible place, we assign a new local to each accessed field.
|
|
|
|
/// The replacement will be done later in `ReplacementVisitor`.
|
|
|
|
fn compute_flattening<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &mut Body<'tcx>,
|
|
|
|
escaping: BitSet<Local>,
|
|
|
|
) -> ReplacementMap<'tcx> {
|
2023-02-05 13:35:33 +00:00
|
|
|
let mut fragments = IndexVec::from_elem(None, &body.local_decls);
|
2022-05-23 07:27:44 +00:00
|
|
|
|
2023-02-05 11:37:44 +00:00
|
|
|
for local in body.local_decls.indices() {
|
|
|
|
if escaping.contains(local) {
|
|
|
|
continue;
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
2023-02-05 11:37:44 +00:00
|
|
|
let decl = body.local_decls[local].clone();
|
|
|
|
let ty = decl.ty;
|
|
|
|
iter_fields(ty, tcx, |variant, field, field_ty| {
|
|
|
|
if variant.is_some() {
|
|
|
|
// Downcasts are currently not supported.
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
let new_local =
|
|
|
|
body.local_decls.push(LocalDecl { ty: field_ty, user_ty: None, ..decl.clone() });
|
2023-02-05 13:35:33 +00:00
|
|
|
fragments.get_or_insert_with(local, IndexVec::new).insert(field, (field_ty, new_local));
|
2023-02-05 11:37:44 +00:00
|
|
|
});
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
2023-02-05 13:35:33 +00:00
|
|
|
ReplacementMap { fragments }
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform the replacement computed by `compute_flattening`.
|
|
|
|
fn replace_flattened_locals<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &mut Body<'tcx>,
|
|
|
|
replacements: ReplacementMap<'tcx>,
|
2023-02-05 12:08:42 +00:00
|
|
|
) -> BitSet<Local> {
|
2023-01-21 21:53:26 +00:00
|
|
|
let mut all_dead_locals = BitSet::new_empty(replacements.fragments.len());
|
2023-02-05 13:35:33 +00:00
|
|
|
for (local, replacements) in replacements.fragments.iter_enumerated() {
|
|
|
|
if replacements.is_some() {
|
|
|
|
all_dead_locals.insert(local);
|
|
|
|
}
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
debug!(?all_dead_locals);
|
|
|
|
if all_dead_locals.is_empty() {
|
2023-02-05 12:08:42 +00:00
|
|
|
return all_dead_locals;
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut visitor = ReplacementVisitor {
|
|
|
|
tcx,
|
|
|
|
local_decls: &body.local_decls,
|
2023-02-05 13:35:33 +00:00
|
|
|
replacements: &replacements,
|
2022-05-23 07:27:44 +00:00
|
|
|
all_dead_locals,
|
2023-02-04 14:39:42 +00:00
|
|
|
patch: MirPatch::new(body),
|
2022-05-23 07:27:44 +00:00
|
|
|
};
|
|
|
|
for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
|
|
|
|
visitor.visit_basic_block_data(bb, data);
|
|
|
|
}
|
|
|
|
for scope in &mut body.source_scopes {
|
|
|
|
visitor.visit_source_scope_data(scope);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
2023-02-05 12:08:42 +00:00
|
|
|
let ReplacementVisitor { patch, all_dead_locals, .. } = visitor;
|
|
|
|
patch.apply(body);
|
|
|
|
all_dead_locals
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ReplacementVisitor<'tcx, 'll> {
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
/// This is only used to compute the type for `VarDebugInfoContents::Composite`.
|
|
|
|
local_decls: &'ll LocalDecls<'tcx>,
|
|
|
|
/// Work to do.
|
2023-02-05 13:35:33 +00:00
|
|
|
replacements: &'ll ReplacementMap<'tcx>,
|
2022-05-23 07:27:44 +00:00
|
|
|
/// This is used to check that we are not leaving references to replaced locals behind.
|
|
|
|
all_dead_locals: BitSet<Local>,
|
2023-02-04 14:39:42 +00:00
|
|
|
patch: MirPatch<'tcx>,
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
|
2023-02-05 13:35:33 +00:00
|
|
|
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),
|
|
|
|
});
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
2023-02-05 13:35:33 +00:00
|
|
|
Some(fragments)
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
|
|
|
|
fn tcx(&self) -> TyCtxt<'tcx> {
|
|
|
|
self.tcx
|
|
|
|
}
|
|
|
|
|
2023-02-05 13:35:33 +00:00
|
|
|
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
|
|
|
|
if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
|
|
|
|
*place = repl
|
|
|
|
} else {
|
|
|
|
self.super_place(place, context, location)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-05 11:37:44 +00:00
|
|
|
#[instrument(level = "trace", skip(self))]
|
2022-05-23 07:27:44 +00:00
|
|
|
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
|
2023-02-04 14:39:42 +00:00
|
|
|
match statement.kind {
|
2023-02-05 13:35:33 +00:00
|
|
|
// Duplicate storage and deinit statements, as they pretty much apply to all fields.
|
2023-02-04 14:39:42 +00:00
|
|
|
StatementKind::StorageLive(l) => {
|
2023-02-05 13:35:33 +00:00
|
|
|
if let Some(final_locals) = self.replacements.place_fragments(l.into()) {
|
|
|
|
for (_, _, fl) in final_locals {
|
2023-02-04 14:39:42 +00:00
|
|
|
self.patch.add_statement(location, StatementKind::StorageLive(fl));
|
|
|
|
}
|
|
|
|
statement.make_nop();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
StatementKind::StorageDead(l) => {
|
2023-02-05 13:35:33 +00:00
|
|
|
if let Some(final_locals) = self.replacements.place_fragments(l.into()) {
|
|
|
|
for (_, _, fl) in final_locals {
|
2023-02-04 14:39:42 +00:00
|
|
|
self.patch.add_statement(location, StatementKind::StorageDead(fl));
|
|
|
|
}
|
|
|
|
statement.make_nop();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2023-02-05 10:40:21 +00:00
|
|
|
StatementKind::Deinit(box place) => {
|
2023-02-05 11:37:44 +00:00
|
|
|
if let Some(final_locals) = self.replacements.place_fragments(place) {
|
2023-02-05 13:35:33 +00:00
|
|
|
for (_, _, fl) in final_locals {
|
2023-02-05 10:40:21 +00:00
|
|
|
self.patch
|
|
|
|
.add_statement(location, StatementKind::Deinit(Box::new(fl.into())));
|
2023-02-04 14:39:42 +00:00
|
|
|
}
|
|
|
|
statement.make_nop();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-05 13:35:33 +00:00
|
|
|
// We have `a = Struct { 0: x, 1: y, .. }`.
|
|
|
|
// We replace it by
|
|
|
|
// ```
|
|
|
|
// a_0 = x
|
|
|
|
// a_1 = y
|
|
|
|
// ...
|
|
|
|
// ```
|
|
|
|
StatementKind::Assign(box (place, Rvalue::Aggregate(_, ref mut operands))) => {
|
|
|
|
if let Some(local) = place.as_local()
|
|
|
|
&& let Some(final_locals) = &self.replacements.fragments[local]
|
|
|
|
{
|
|
|
|
// This is ok as we delete the statement later.
|
|
|
|
let operands = std::mem::take(operands);
|
|
|
|
for (&opt_ty_local, mut operand) in final_locals.iter().zip(operands) {
|
|
|
|
if let Some((_, new_local)) = opt_ty_local {
|
|
|
|
// Replace mentions of SROA'd locals that appear in the operand.
|
|
|
|
self.visit_operand(&mut operand, location);
|
|
|
|
|
|
|
|
let rvalue = Rvalue::Use(operand);
|
|
|
|
self.patch.add_statement(
|
|
|
|
location,
|
|
|
|
StatementKind::Assign(Box::new((new_local.into(), rvalue))),
|
|
|
|
);
|
|
|
|
}
|
2023-02-04 14:39:42 +00:00
|
|
|
}
|
|
|
|
statement.make_nop();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-05 13:35:33 +00:00
|
|
|
// We have `a = some constant`
|
|
|
|
// We add the projections.
|
|
|
|
// ```
|
|
|
|
// a_0 = a.0
|
|
|
|
// a_1 = a.1
|
|
|
|
// ...
|
|
|
|
// ```
|
|
|
|
// ConstProp will pick up the pieces and replace them by actual constants.
|
2023-02-05 10:40:21 +00:00
|
|
|
StatementKind::Assign(box (place, Rvalue::Use(Operand::Constant(_)))) => {
|
2023-02-05 11:37:44 +00:00
|
|
|
if let Some(final_locals) = self.replacements.place_fragments(place) {
|
2023-02-09 17:27:31 +00:00
|
|
|
// Put the deaggregated statements *after* the original one.
|
|
|
|
let location = location.successor_within_block();
|
2023-02-05 13:35:33 +00:00
|
|
|
for (field, ty, new_local) in final_locals {
|
|
|
|
let rplace = self.tcx.mk_place_field(place, field, ty);
|
|
|
|
let rvalue = Rvalue::Use(Operand::Move(rplace));
|
2023-02-05 09:31:27 +00:00
|
|
|
self.patch.add_statement(
|
|
|
|
location,
|
2023-02-05 13:35:33 +00:00
|
|
|
StatementKind::Assign(Box::new((new_local.into(), rvalue))),
|
2023-02-05 09:31:27 +00:00
|
|
|
);
|
|
|
|
}
|
2023-02-05 13:35:33 +00:00
|
|
|
// We still need `place.local` to exist, so don't make it nop.
|
2023-02-05 09:31:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-05 13:35:33 +00:00
|
|
|
// We have `a = move? place`
|
|
|
|
// We replace it by
|
|
|
|
// ```
|
|
|
|
// a_0 = move? place.0
|
|
|
|
// a_1 = move? place.1
|
|
|
|
// ...
|
|
|
|
// ```
|
2023-02-05 10:40:21 +00:00
|
|
|
StatementKind::Assign(box (lhs, Rvalue::Use(ref op))) => {
|
2023-02-05 13:35:33 +00:00
|
|
|
let (rplace, copy) = match *op {
|
2023-02-05 09:31:27 +00:00
|
|
|
Operand::Copy(rplace) => (rplace, true),
|
|
|
|
Operand::Move(rplace) => (rplace, false),
|
|
|
|
Operand::Constant(_) => bug!(),
|
|
|
|
};
|
2023-02-05 11:37:44 +00:00
|
|
|
if let Some(final_locals) = self.replacements.place_fragments(lhs) {
|
2023-02-05 13:35:33 +00:00
|
|
|
for (field, ty, new_local) in final_locals {
|
|
|
|
let rplace = self.tcx.mk_place_field(rplace, field, ty);
|
2023-02-05 11:37:44 +00:00
|
|
|
debug!(?rplace);
|
2023-02-05 13:35:33 +00:00
|
|
|
let rplace = self
|
|
|
|
.replacements
|
|
|
|
.replace_place(self.tcx, rplace.as_ref())
|
|
|
|
.unwrap_or(rplace);
|
2023-02-05 11:37:44 +00:00
|
|
|
debug!(?rplace);
|
2023-02-05 09:31:27 +00:00
|
|
|
let rvalue = if copy {
|
|
|
|
Rvalue::Use(Operand::Copy(rplace))
|
|
|
|
} else {
|
|
|
|
Rvalue::Use(Operand::Move(rplace))
|
|
|
|
};
|
|
|
|
self.patch.add_statement(
|
|
|
|
location,
|
2023-02-05 13:35:33 +00:00
|
|
|
StatementKind::Assign(Box::new((new_local.into(), rvalue))),
|
2023-02-05 09:31:27 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
statement.make_nop();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-04 14:39:42 +00:00
|
|
|
_ => {}
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
self.super_statement(statement, location)
|
|
|
|
}
|
|
|
|
|
2023-02-05 12:08:42 +00:00
|
|
|
#[instrument(level = "trace", skip(self))]
|
2022-05-23 07:27:44 +00:00
|
|
|
fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
|
|
|
|
match &mut var_debug_info.value {
|
|
|
|
VarDebugInfoContents::Place(ref mut place) => {
|
2023-02-05 13:35:33 +00:00
|
|
|
if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
|
2022-05-23 07:27:44 +00:00
|
|
|
*place = repl;
|
2023-02-05 13:35:33 +00:00
|
|
|
} else if let Some(local) = place.as_local()
|
|
|
|
&& let Some(fragments) = self.gather_debug_info_fragments(local)
|
2023-02-05 11:37:44 +00:00
|
|
|
{
|
2022-05-23 07:27:44 +00:00
|
|
|
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();
|
2023-02-05 12:08:42 +00:00
|
|
|
debug!(?fragments);
|
2022-05-23 07:27:44 +00:00
|
|
|
fragments
|
|
|
|
.drain_filter(|fragment| {
|
2023-02-05 13:35:33 +00:00
|
|
|
if let Some(repl) =
|
|
|
|
self.replacements.replace_place(self.tcx, fragment.contents.as_ref())
|
|
|
|
{
|
2022-05-23 07:27:44 +00:00
|
|
|
fragment.contents = repl;
|
2023-02-05 12:08:42 +00:00
|
|
|
false
|
2023-02-05 13:35:33 +00:00
|
|
|
} else if let Some(local) = fragment.contents.as_local()
|
|
|
|
&& let Some(frg) = self.gather_debug_info_fragments(local)
|
2023-02-05 09:31:27 +00:00
|
|
|
{
|
2022-05-23 07:27:44 +00:00
|
|
|
new_fragments.extend(frg.into_iter().map(|mut f| {
|
|
|
|
f.projection.splice(0..0, fragment.projection.iter().copied());
|
|
|
|
f
|
|
|
|
}));
|
|
|
|
true
|
2023-02-05 12:08:42 +00:00
|
|
|
} else {
|
|
|
|
false
|
2022-05-23 07:27:44 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.for_each(drop);
|
2023-02-05 12:08:42 +00:00
|
|
|
debug!(?fragments);
|
|
|
|
debug!(?new_fragments);
|
2022-05-23 07:27:44 +00:00
|
|
|
fragments.extend(new_fragments);
|
|
|
|
}
|
|
|
|
VarDebugInfoContents::Const(_) => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
|
|
|
|
assert!(!self.all_dead_locals.contains(*local));
|
|
|
|
}
|
|
|
|
}
|