Skip to content

Commit cc3a0a4

Browse files
committed
Support borrowed locals in DestinationPropagation.
1 parent ce6daf3 commit cc3a0a4

23 files changed

+511
-381
lines changed

compiler/rustc_middle/src/mir/visit.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,3 +1475,20 @@ impl PlaceContext {
14751475
}
14761476
}
14771477
}
1478+
1479+
/// Small utility to visit places and locals without manually implementing a full visitor.
1480+
pub struct VisitPlacesWith<F>(pub F);
1481+
1482+
impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith<F>
1483+
where
1484+
F: FnMut(Place<'tcx>, PlaceContext),
1485+
{
1486+
fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) {
1487+
(self.0)(local.into(), ctxt);
1488+
}
1489+
1490+
fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) {
1491+
(self.0)(*place, ctxt);
1492+
self.visit_projection(place.as_ref(), ctxt, location);
1493+
}
1494+
}

compiler/rustc_mir_dataflow/src/impls/liveness.rs

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use rustc_index::bit_set::DenseBitSet;
2-
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
2+
use rustc_middle::mir::visit::{
3+
MutatingUseContext, NonMutatingUseContext, PlaceContext, VisitPlacesWith, Visitor,
4+
};
35
use rustc_middle::mir::{
4-
self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges,
6+
self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges, TerminatorKind,
57
};
68

79
use crate::{Analysis, Backward, GenKill};
@@ -23,9 +25,13 @@ use crate::{Analysis, Backward, GenKill};
2325
/// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals
2426
/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs
2527
/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
26-
pub struct MaybeLiveLocals;
28+
pub struct MaybeLiveLocals<F>(pub F);
2729

28-
impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
30+
impl<'tcx, F, I> Analysis<'tcx> for MaybeLiveLocals<F>
31+
where
32+
F: Fn(Location) -> I,
33+
I: Iterator<Item = Local>,
34+
{
2935
type Domain = DenseBitSet<Local>;
3036
type Direction = Backward;
3137

@@ -40,6 +46,26 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
4046
// No variables are live until we observe a use
4147
}
4248

49+
fn apply_early_statement_effect(
50+
&mut self,
51+
state: &mut Self::Domain,
52+
statement: &mir::Statement<'tcx>,
53+
location: Location,
54+
) {
55+
let mut accesses_indirect = false;
56+
VisitPlacesWith(|place: Place<'tcx>, _: PlaceContext| {
57+
accesses_indirect |= place.is_indirect();
58+
})
59+
.visit_statement(statement, location);
60+
if accesses_indirect {
61+
// We do not track what happens to the addresses of borrowed locals. This means
62+
// that this indirect read/write may be to any borrowed local.
63+
for local in (self.0)(location) {
64+
state.gen_(local);
65+
}
66+
}
67+
}
68+
4369
fn apply_primary_statement_effect(
4470
&mut self,
4571
state: &mut Self::Domain,
@@ -49,6 +75,44 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
4975
TransferFunction(state).visit_statement(statement, location);
5076
}
5177

78+
fn apply_early_terminator_effect<'mir>(
79+
&mut self,
80+
state: &mut Self::Domain,
81+
terminator: &'mir mir::Terminator<'tcx>,
82+
location: Location,
83+
) {
84+
let may_access_borrowed_locals = match terminator.kind {
85+
TerminatorKind::CoroutineDrop
86+
| TerminatorKind::FalseEdge { .. }
87+
| TerminatorKind::FalseUnwind { .. }
88+
| TerminatorKind::Goto { .. }
89+
| TerminatorKind::Return
90+
| TerminatorKind::Unreachable
91+
| TerminatorKind::UnwindResume
92+
| TerminatorKind::UnwindTerminate(_) => false,
93+
94+
TerminatorKind::Assert { cond: ref operand, .. }
95+
| TerminatorKind::SwitchInt { discr: ref operand, .. } => {
96+
operand.place().is_some_and(|place| place.is_indirect())
97+
}
98+
99+
// Those terminators may call arbitrary code.
100+
TerminatorKind::Call { .. }
101+
| TerminatorKind::Drop { .. }
102+
| TerminatorKind::InlineAsm { .. }
103+
| TerminatorKind::TailCall { .. }
104+
| TerminatorKind::Yield { .. } => true,
105+
};
106+
if may_access_borrowed_locals {
107+
// We do not track what happens to the addresses of borrowed locals. This means that this
108+
// terminator may know the address of any borrowed local. And it may do anything with it
109+
// including reading and writing to it.
110+
for local in (self.0)(location) {
111+
state.gen_(local);
112+
}
113+
}
114+
}
115+
52116
fn apply_primary_terminator_effect<'mir>(
53117
&mut self,
54118
state: &mut Self::Domain,
@@ -155,17 +219,17 @@ impl DefUse {
155219
match context {
156220
PlaceContext::NonUse(_) => DefUse::NonUse,
157221

222+
// Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use.
223+
_ if place.is_indirect() => DefUse::Use,
224+
158225
PlaceContext::MutatingUse(
159226
MutatingUseContext::Call
160227
| MutatingUseContext::Yield
161228
| MutatingUseContext::AsmOutput
162229
| MutatingUseContext::Store
163230
| MutatingUseContext::Deinit,
164231
) => {
165-
// Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use.
166-
if place.is_indirect() {
167-
DefUse::Use
168-
} else if place.projection.is_empty() {
232+
if place.projection.is_empty() {
169233
DefUse::Def
170234
} else {
171235
DefUse::PartialWrite
@@ -174,9 +238,7 @@ impl DefUse {
174238

175239
// Setting the discriminant is not a use because it does no reading, but it is also not
176240
// a def because it does not overwrite the whole place
177-
PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant) => {
178-
if place.is_indirect() { DefUse::Use } else { DefUse::PartialWrite }
179-
}
241+
PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant) => DefUse::PartialWrite,
180242

181243
// All other contexts are uses...
182244
PlaceContext::MutatingUse(

compiler/rustc_mir_dataflow/src/rustc_peek.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
5353
}
5454

5555
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
56-
let flow_liveness =
57-
MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
56+
let flow_liveness = MaybeLiveLocals(|_| std::iter::empty())
57+
.iterate_to_fixpoint(tcx, body, None)
58+
.into_results_cursor(body);
5859
sanity_check_via_rustc_peek(tcx, flow_liveness);
5960
}
6061

@@ -245,7 +246,11 @@ where
245246
}
246247
}
247248

248-
impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
249+
impl<'tcx, F, I> RustcPeekAt<'tcx> for MaybeLiveLocals<F>
250+
where
251+
F: Fn(Location) -> I,
252+
I: Iterator<Item = Local>,
253+
{
249254
fn peek_at(
250255
&self,
251256
tcx: TyCtxt<'tcx>,

compiler/rustc_mir_transform/src/coroutine.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,8 +708,9 @@ fn locals_live_across_suspend_points<'tcx>(
708708
);
709709

710710
// Calculate the liveness of MIR locals ignoring borrows.
711-
let mut liveness =
712-
MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("coroutine")).into_results_cursor(body);
711+
let mut liveness = MaybeLiveLocals(|_| std::iter::empty())
712+
.iterate_to_fixpoint(tcx, body, Some("coroutine"))
713+
.into_results_cursor(body);
713714

714715
let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks);
715716
let mut live_locals_at_suspension_points = Vec::new();

0 commit comments

Comments
 (0)