Skip to content

Commit 6e0303e

Browse files
committed
mir-opt: Eliminate dead ref statements
1 parent 7cd9505 commit 6e0303e

File tree

56 files changed

+1538
-488
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1538
-488
lines changed

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1146,7 +1146,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
11461146
let opt_assignment_rhs_span =
11471147
self.find_assignments(local).first().map(|&location| {
11481148
if let Some(mir::Statement {
1149-
source_info: _,
11501149
kind:
11511150
mir::StatementKind::Assign(box (
11521151
_,

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
55
use std::borrow::Cow;
66
use std::fmt::{self, Debug, Formatter};
7-
use std::iter;
87
use std::ops::{Index, IndexMut};
8+
use std::{iter, mem};
99

1010
pub use basic_blocks::{BasicBlocks, SwitchTargetValue};
1111
use either::Either;
@@ -1342,6 +1342,10 @@ pub struct BasicBlockData<'tcx> {
13421342
/// List of statements in this block.
13431343
pub statements: Vec<Statement<'tcx>>,
13441344

1345+
/// All debuginfos happen before the statement.
1346+
/// Put debuginfos here when the last statement is eliminated.
1347+
pub after_last_stmt_debuginfos: Vec<StmtDebugInfo<'tcx>>,
1348+
13451349
/// Terminator for this block.
13461350
///
13471351
/// N.B., this should generally ONLY be `None` during construction.
@@ -1369,7 +1373,12 @@ impl<'tcx> BasicBlockData<'tcx> {
13691373
terminator: Option<Terminator<'tcx>>,
13701374
is_cleanup: bool,
13711375
) -> BasicBlockData<'tcx> {
1372-
BasicBlockData { statements, terminator, is_cleanup }
1376+
BasicBlockData {
1377+
statements,
1378+
after_last_stmt_debuginfos: Vec::new(),
1379+
terminator,
1380+
is_cleanup,
1381+
}
13731382
}
13741383

13751384
/// Accessor for terminator.
@@ -1404,6 +1413,37 @@ impl<'tcx> BasicBlockData<'tcx> {
14041413
self.terminator().successors()
14051414
}
14061415
}
1416+
1417+
pub fn retain_statements<F>(&mut self, mut f: F)
1418+
where
1419+
F: FnMut(&Statement<'tcx>) -> bool,
1420+
{
1421+
// Fast path without debugging information
1422+
if self.statements.iter().all(|stmt| stmt.debuginfos.is_empty()) {
1423+
self.statements.retain_mut(|stmt| f(stmt));
1424+
return;
1425+
}
1426+
let mut debuginfos = Vec::new();
1427+
self.statements.retain_mut(|stmt| {
1428+
let retain = f(stmt);
1429+
if retain {
1430+
if !debuginfos.is_empty() {
1431+
if !stmt.debuginfos.is_empty() {
1432+
debuginfos.append(&mut stmt.debuginfos);
1433+
}
1434+
mem::swap(&mut debuginfos, &mut stmt.debuginfos);
1435+
}
1436+
} else {
1437+
if !stmt.debuginfos.is_empty() {
1438+
debuginfos.append(&mut stmt.debuginfos);
1439+
}
1440+
}
1441+
retain
1442+
});
1443+
if !debuginfos.is_empty() {
1444+
self.after_last_stmt_debuginfos.append(&mut debuginfos);
1445+
}
1446+
}
14071447
}
14081448

14091449
///////////////////////////////////////////////////////////////////////////
@@ -1708,10 +1748,10 @@ mod size_asserts {
17081748

17091749
use super::*;
17101750
// tidy-alphabetical-start
1711-
static_assert_size!(BasicBlockData<'_>, 128);
1751+
static_assert_size!(BasicBlockData<'_>, 152);
17121752
static_assert_size!(LocalDecl<'_>, 40);
17131753
static_assert_size!(SourceScopeData<'_>, 64);
1714-
static_assert_size!(Statement<'_>, 32);
1754+
static_assert_size!(Statement<'_>, 56);
17151755
static_assert_size!(Terminator<'_>, 96);
17161756
static_assert_size!(VarDebugInfo<'_>, 88);
17171757
// tidy-alphabetical-end

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,9 @@ where
778778
let mut current_location = Location { block, statement_index: 0 };
779779
for statement in &data.statements {
780780
extra_data(PassWhere::BeforeLocation(current_location), w)?;
781+
for debuginfo in statement.debuginfos.iter() {
782+
writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?}")?;
783+
}
781784
let indented_body = format!("{INDENT}{INDENT}{statement:?};");
782785
if options.include_extra_comments {
783786
writeln!(
@@ -812,6 +815,9 @@ where
812815

813816
// Terminator at the bottom.
814817
extra_data(PassWhere::BeforeLocation(current_location), w)?;
818+
for debuginfo in data.after_last_stmt_debuginfos.iter() {
819+
writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?}")?;
820+
}
815821
if data.terminator.is_some() {
816822
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
817823
if options.include_extra_comments {
@@ -891,6 +897,16 @@ impl Debug for Statement<'_> {
891897
}
892898
}
893899

900+
impl Debug for StmtDebugInfo<'_> {
901+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
902+
match self {
903+
StmtDebugInfo::AssignRef(local, place) => {
904+
write!(fmt, "{local:?} = &{place:?}")
905+
}
906+
}
907+
}
908+
}
909+
894910
impl Display for NonDivergingIntrinsic<'_> {
895911
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
896912
match self {

compiler/rustc_middle/src/mir/statement.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,33 @@ use crate::ty::CoroutineArgsExt;
1515
pub struct Statement<'tcx> {
1616
pub source_info: SourceInfo,
1717
pub kind: StatementKind<'tcx>,
18+
pub debuginfos: Vec<StmtDebugInfo<'tcx>>,
1819
}
1920

2021
impl<'tcx> Statement<'tcx> {
2122
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
2223
/// invalidating statement indices in `Location`s.
23-
pub fn make_nop(&mut self) {
24-
self.kind = StatementKind::Nop
24+
pub fn make_nop(&mut self, drop_debuginfo: bool) {
25+
if matches!(self.kind, StatementKind::Nop) {
26+
return;
27+
}
28+
let replaced_stmt = std::mem::replace(&mut self.kind, StatementKind::Nop);
29+
if !drop_debuginfo {
30+
match replaced_stmt {
31+
StatementKind::Assign(box (place, Rvalue::Ref(_, _, ref_place)))
32+
if let Some(local) = place.as_local() =>
33+
{
34+
self.debuginfos.push(StmtDebugInfo::AssignRef(local, ref_place));
35+
}
36+
_ => {
37+
bug!("debuginfo is not yet supported.")
38+
}
39+
}
40+
}
2541
}
2642

2743
pub fn new(source_info: SourceInfo, kind: StatementKind<'tcx>) -> Self {
28-
Statement { source_info, kind }
44+
Statement { source_info, kind, debuginfos: Vec::new() }
2945
}
3046
}
3147

@@ -939,3 +955,8 @@ impl RawPtrKind {
939955
}
940956
}
941957
}
958+
959+
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
960+
pub enum StmtDebugInfo<'tcx> {
961+
AssignRef(Local, Place<'tcx>),
962+
}

compiler/rustc_middle/src/mir/visit.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,22 @@ macro_rules! make_mir_visitor {
9595
self.super_source_scope_data(scope_data);
9696
}
9797

98+
fn visit_statement_debuginfos(
99+
&mut self,
100+
stmt_debuginfo: & $($mutability)? [StmtDebugInfo<'tcx>],
101+
location: Location
102+
) {
103+
self.super_statement_debuginfos(stmt_debuginfo, location);
104+
}
105+
106+
fn visit_statement_debuginfo(
107+
&mut self,
108+
stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>,
109+
location: Location
110+
) {
111+
self.super_statement_debuginfo(stmt_debuginfo, location);
112+
}
113+
98114
fn visit_statement(
99115
&mut self,
100116
statement: & $($mutability)? Statement<'tcx>,
@@ -301,6 +317,7 @@ macro_rules! make_mir_visitor {
301317
{
302318
let BasicBlockData {
303319
statements,
320+
after_last_stmt_debuginfos,
304321
terminator,
305322
is_cleanup: _
306323
} = data;
@@ -312,8 +329,9 @@ macro_rules! make_mir_visitor {
312329
index += 1;
313330
}
314331

332+
let location = Location { block, statement_index: index };
333+
self.visit_statement_debuginfos(after_last_stmt_debuginfos, location);
315334
if let Some(terminator) = terminator {
316-
let location = Location { block, statement_index: index };
317335
self.visit_terminator(terminator, location);
318336
}
319337
}
@@ -376,14 +394,46 @@ macro_rules! make_mir_visitor {
376394
}
377395
}
378396

397+
fn super_statement_debuginfos(
398+
&mut self,
399+
stmt_debuginfo: & $($mutability)? [StmtDebugInfo<'tcx>],
400+
location: Location
401+
) {
402+
for debuginfo in stmt_debuginfo {
403+
self.visit_statement_debuginfo(debuginfo, location);
404+
}
405+
}
406+
407+
fn super_statement_debuginfo(
408+
&mut self,
409+
stmt_debuginfo: & $($mutability)? StmtDebugInfo<'tcx>,
410+
location: Location
411+
) {
412+
match stmt_debuginfo {
413+
StmtDebugInfo::AssignRef(local, place) => {
414+
self.visit_local(
415+
$(& $mutability)? *local,
416+
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
417+
location
418+
);
419+
self.visit_place(
420+
place,
421+
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
422+
location
423+
);
424+
},
425+
}
426+
}
427+
379428
fn super_statement(
380429
&mut self,
381430
statement: & $($mutability)? Statement<'tcx>,
382431
location: Location
383432
) {
384-
let Statement { source_info, kind } = statement;
433+
let Statement { source_info, kind, debuginfos } = statement;
385434

386435
self.visit_source_info(source_info);
436+
self.visit_statement_debuginfos(debuginfos, location);
387437
match kind {
388438
StatementKind::Assign(box (place, rvalue)) => {
389439
self.visit_assign(place, rvalue, location);

compiler/rustc_mir_dataflow/src/debuginfo.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ use rustc_middle::mir::*;
55
/// Return the set of locals that appear in debuginfo.
66
pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet<Local> {
77
let mut visitor = DebuginfoLocals(DenseBitSet::new_empty(body.local_decls.len()));
8-
for debuginfo in body.var_debug_info.iter() {
9-
visitor.visit_var_debug_info(debuginfo);
10-
}
8+
visitor.visit_body(body);
119
visitor.0
1210
}
1311

1412
struct DebuginfoLocals(DenseBitSet<Local>);
1513

1614
impl Visitor<'_> for DebuginfoLocals {
17-
fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) {
18-
self.0.insert(local);
15+
fn visit_local(&mut self, local: Local, place_context: PlaceContext, _: Location) {
16+
if place_context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
17+
self.0.insert(local);
18+
}
1919
}
2020
}

compiler/rustc_mir_dataflow/src/impls/liveness.rs

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,54 @@ impl DefUse {
205205
/// All of the caveats of `MaybeLiveLocals` apply.
206206
pub struct MaybeTransitiveLiveLocals<'a> {
207207
always_live: &'a DenseBitSet<Local>,
208+
debuginfo_locals: &'a DenseBitSet<Local>,
208209
}
209210

210211
impl<'a> MaybeTransitiveLiveLocals<'a> {
211212
/// The `always_alive` set is the set of locals to which all stores should unconditionally be
212213
/// considered live.
213214
///
214215
/// This should include at least all locals that are ever borrowed.
215-
pub fn new(always_live: &'a DenseBitSet<Local>) -> Self {
216-
MaybeTransitiveLiveLocals { always_live }
216+
pub fn new(
217+
always_live: &'a DenseBitSet<Local>,
218+
debuginfo_locals: &'a DenseBitSet<Local>,
219+
) -> Self {
220+
MaybeTransitiveLiveLocals { always_live, debuginfo_locals }
221+
}
222+
223+
pub fn can_be_removed_if_dead<'tcx>(
224+
stmt_kind: &StatementKind<'tcx>,
225+
always_live: &DenseBitSet<Local>,
226+
debuginfo_locals: &'a DenseBitSet<Local>,
227+
) -> Option<Place<'tcx>> {
228+
// Compute the place that we are storing to, if any
229+
let destination = match stmt_kind {
230+
StatementKind::Assign(box (place, rvalue)) => (rvalue.is_safe_to_remove()
231+
&& (!debuginfo_locals.contains(place.local)
232+
|| (place.as_local().is_some() && matches!(rvalue, mir::Rvalue::Ref(..)))))
233+
.then_some(*place),
234+
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
235+
(!debuginfo_locals.contains(place.local)).then_some(**place)
236+
}
237+
StatementKind::FakeRead(_)
238+
| StatementKind::StorageLive(_)
239+
| StatementKind::StorageDead(_)
240+
| StatementKind::Retag(..)
241+
| StatementKind::AscribeUserType(..)
242+
| StatementKind::PlaceMention(..)
243+
| StatementKind::Coverage(..)
244+
| StatementKind::Intrinsic(..)
245+
| StatementKind::ConstEvalCounter
246+
| StatementKind::BackwardIncompatibleDropHint { .. }
247+
| StatementKind::Nop => None,
248+
};
249+
if let Some(destination) = destination
250+
&& !destination.is_indirect()
251+
&& !always_live.contains(destination.local)
252+
{
253+
return Some(destination);
254+
}
255+
None
217256
}
218257
}
219258

@@ -238,32 +277,15 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
238277
statement: &mir::Statement<'tcx>,
239278
location: Location,
240279
) {
241-
// Compute the place that we are storing to, if any
242-
let destination = match &statement.kind {
243-
StatementKind::Assign(assign) => assign.1.is_safe_to_remove().then_some(assign.0),
244-
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
245-
Some(**place)
246-
}
247-
StatementKind::FakeRead(_)
248-
| StatementKind::StorageLive(_)
249-
| StatementKind::StorageDead(_)
250-
| StatementKind::Retag(..)
251-
| StatementKind::AscribeUserType(..)
252-
| StatementKind::PlaceMention(..)
253-
| StatementKind::Coverage(..)
254-
| StatementKind::Intrinsic(..)
255-
| StatementKind::ConstEvalCounter
256-
| StatementKind::BackwardIncompatibleDropHint { .. }
257-
| StatementKind::Nop => None,
258-
};
259-
if let Some(destination) = destination {
260-
if !destination.is_indirect()
261-
&& !state.contains(destination.local)
262-
&& !self.always_live.contains(destination.local)
263-
{
264-
// This store is dead
265-
return;
266-
}
280+
if let Some(destination) =
281+
Self::can_be_removed_if_dead(&statement.kind, &self.always_live, &self.debuginfo_locals)
282+
&& !state.contains(destination.local)
283+
// FIXME: We can eliminate the statement, but we'll need the statements it depends on
284+
// for debuginfos. We need a way to handle this.
285+
&& !self.debuginfo_locals.contains(destination.local)
286+
{
287+
// This store is dead
288+
return;
267289
}
268290
TransferFunction(state).visit_statement(statement, location);
269291
}

compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck {
3636
CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. },
3737
)
3838
| StatementKind::FakeRead(..)
39-
| StatementKind::BackwardIncompatibleDropHint { .. } => statement.make_nop(),
39+
| StatementKind::BackwardIncompatibleDropHint { .. } => {
40+
statement.make_nop(true)
41+
}
4042
StatementKind::Assign(box (
4143
_,
4244
Rvalue::Cast(

0 commit comments

Comments
 (0)