Skip to content

Commit 3b4e1fe

Browse files
committed
Do not check StorageLive dominates address-taking.
1 parent 3268f2e commit 3b4e1fe

File tree

2 files changed

+75
-41
lines changed

2 files changed

+75
-41
lines changed

compiler/rustc_mir_transform/src/ref_prop.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_mir_dataflow::impls::MaybeStorageDead;
88
use rustc_mir_dataflow::storage::always_storage_live_locals;
99
use rustc_mir_dataflow::Analysis;
1010

11-
use crate::ssa::SsaLocals;
11+
use crate::ssa::{SsaLocals, StorageLiveLocals};
1212
use crate::MirPass;
1313

1414
/// Propagate references using SSA analysis.
@@ -39,7 +39,7 @@ use crate::MirPass;
3939
/// - all projections in `PROJECTIONS` have a stable offset (no dereference and no indexing).
4040
///
4141
/// If `PLACE` is a direct projection of a local, we consider it as constant if:
42-
/// - the local is always live, or it has a single `StorageLive` that dominates all uses;
42+
/// - the local is always live, or it has a single `StorageLive`;
4343
/// - all projections have a stable offset.
4444
///
4545
/// # Liveness
@@ -110,9 +110,13 @@ fn compute_replacement<'tcx>(
110110
body: &Body<'tcx>,
111111
ssa: &SsaLocals,
112112
) -> Replacer<'tcx> {
113+
let always_live_locals = always_storage_live_locals(body);
114+
115+
// Compute which locals have a single `StorageLive` statement ever.
116+
let storage_live = StorageLiveLocals::new(body, &always_live_locals);
117+
113118
// Compute `MaybeStorageDead` dataflow to check that we only replace when the pointee is
114119
// definitely live.
115-
let always_live_locals = always_storage_live_locals(body);
116120
let mut maybe_dead = MaybeStorageDead::new(always_live_locals)
117121
.into_engine(tcx, body)
118122
.iterate_to_fixpoint()
@@ -126,6 +130,38 @@ fn compute_replacement<'tcx>(
126130

127131
let fully_replacable_locals = fully_replacable_locals(ssa);
128132

133+
// Returns true iff we can use `place` as a pointee.
134+
//
135+
// Note that we only need to verify that there is a single `StorageLive` statement, and we do
136+
// not need to verify that it dominates all uses of that local.
137+
//
138+
// Consider the three statements:
139+
// SL : StorageLive(a)
140+
// DEF: b = &raw? mut? a
141+
// USE: stuff that uses *b
142+
//
143+
// First, we recall that DEF is checked to dominate USE. Now imagine for the sake of
144+
// contradiction there is a DEF -> SL -> USE path. Consider two cases:
145+
//
146+
// - DEF dominates SL. We always have UB the first time control flow reaches DEF,
147+
// because the storage of `a` is dead. Since DEF dominates USE, that means we cannot
148+
// reach USE and so our optimization is ok.
149+
//
150+
// - DEF does not dominate SL. Then there is a `START_BLOCK -> SL` path not including DEF.
151+
// But we can extend this path to USE, meaning there is also a `START_BLOCK -> USE` path not
152+
// including DEF. This violates the DEF dominates USE condition, and so is impossible.
153+
let is_constant_place = |place: Place<'_>| {
154+
// We only allow `Deref` as the first projection, to avoid surprises.
155+
if place.projection.first() == Some(&PlaceElem::Deref) {
156+
// `place == (*some_local).xxx`, it is constant only if `some_local` is constant.
157+
// We approximate constness using SSAness.
158+
ssa.is_ssa(place.local) && place.projection[1..].iter().all(PlaceElem::is_stable_offset)
159+
} else {
160+
storage_live.has_single_storage(place.local)
161+
&& place.projection[..].iter().all(PlaceElem::is_stable_offset)
162+
}
163+
};
164+
129165
let mut can_perform_opt = |target: Place<'tcx>, loc: Location| {
130166
maybe_dead.seek_after_primary_effect(loc);
131167
let maybe_dead = maybe_dead.contains(target.local);
@@ -194,7 +230,7 @@ fn compute_replacement<'tcx>(
194230
place = target.project_deeper(&place.projection[1..], tcx);
195231
}
196232
assert_ne!(place.local, local);
197-
if ssa.is_constant_place(place) {
233+
if is_constant_place(place) {
198234
targets[local] = Value::Pointer(place, ty.is_mutable_ptr());
199235
}
200236
}

compiler/rustc_mir_transform/src/ssa.rs

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@
44
//!
55
//! As a consequence of rule 2, we consider that borrowed locals are not SSA, even if they are
66
//! `Freeze`, as we do not track that the assignment dominates all uses of the borrow.
7-
//!
8-
//! We say a local has a stable address if its address has SSA-like properties:
9-
//! 1/ It has a single `StorageLive` statement, or none at all (always-live);
10-
//! 2/ This `StorageLive` statement dominates all statements that take this local's address.
11-
//!
12-
//! We do not discard borrowed locals from this analysis, as we cannot take their address' address.
137
148
use either::Either;
159
use rustc_data_structures::graph::dominators::Dominators;
@@ -18,7 +12,6 @@ use rustc_index::{IndexSlice, IndexVec};
1812
use rustc_middle::middle::resolve_bound_vars::Set1;
1913
use rustc_middle::mir::visit::*;
2014
use rustc_middle::mir::*;
21-
use rustc_mir_dataflow::storage::always_storage_live_locals;
2215

2316
#[derive(Debug)]
2417
pub struct SsaLocals {
@@ -33,9 +26,6 @@ pub struct SsaLocals {
3326
/// Number of "direct" uses of each local, ie. uses that are not dereferences.
3427
/// We ignore non-uses (Storage statements, debuginfo).
3528
direct_uses: IndexVec<Local, u32>,
36-
/// Set of "StorageLive" statements for each local. When the "StorageLive" statement does not
37-
/// dominate all uses of the local, we mark it as `Set1::Many`.
38-
storage_live: IndexVec<Local, Set1<LocationExtended>>,
3929
}
4030

4131
/// We often encounter MIR bodies with 1 or 2 basic blocks. In those cases, it's unnecessary to
@@ -83,18 +73,12 @@ impl SsaLocals {
8373
let dominators = SmallDominators { inner: dominators };
8474

8575
let direct_uses = IndexVec::from_elem(0, &body.local_decls);
86-
let storage_live = IndexVec::from_elem(Set1::Empty, &body.local_decls);
87-
let mut visitor =
88-
SsaVisitor { assignments, assignment_order, dominators, direct_uses, storage_live };
76+
let mut visitor = SsaVisitor { assignments, assignment_order, dominators, direct_uses };
8977

9078
for local in body.args_iter() {
9179
visitor.assignments[local] = Set1::One(LocationExtended::Arg);
9280
}
9381

94-
for local in always_storage_live_locals(body).iter() {
95-
visitor.storage_live[local] = Set1::One(LocationExtended::Arg);
96-
}
97-
9882
if body.basic_blocks.len() > 2 {
9983
for (bb, data) in traversal::reverse_postorder(body) {
10084
visitor.visit_basic_block_data(bb, data);
@@ -111,7 +95,6 @@ impl SsaLocals {
11195

11296
debug!(?visitor.assignments);
11397
debug!(?visitor.direct_uses);
114-
debug!(?visitor.storage_live);
11598

11699
visitor
117100
.assignment_order
@@ -124,7 +107,6 @@ impl SsaLocals {
124107
assignments: visitor.assignments,
125108
assignment_order: visitor.assignment_order,
126109
direct_uses: visitor.direct_uses,
127-
storage_live: visitor.storage_live,
128110
copy_classes,
129111
}
130112
}
@@ -141,19 +123,6 @@ impl SsaLocals {
141123
matches!(self.assignments[local], Set1::One(_))
142124
}
143125

144-
/// Returns true iff we can use `p` as a pointee.
145-
pub fn is_constant_place(&self, p: Place<'_>) -> bool {
146-
// We only allow `Deref` as the first projection, to avoid surprises.
147-
if p.projection.first() == Some(&PlaceElem::Deref) {
148-
// `p == (*some_local).xxx`, it is constant only if `some_local` is constant.
149-
// We approximate constness using SSAness.
150-
self.is_ssa(p.local) && p.projection[1..].iter().all(PlaceElem::is_stable_offset)
151-
} else {
152-
matches!(self.storage_live[p.local], Set1::One(_))
153-
&& p.projection[..].iter().all(PlaceElem::is_stable_offset)
154-
}
155-
}
156-
157126
/// Return the number of uses if a local that are not "Deref".
158127
pub fn num_direct_uses(&self, local: Local) -> u32 {
159128
self.direct_uses[local]
@@ -233,7 +202,6 @@ struct SsaVisitor {
233202
assignments: IndexVec<Local, Set1<LocationExtended>>,
234203
assignment_order: Vec<Local>,
235204
direct_uses: IndexVec<Local, u32>,
236-
storage_live: IndexVec<Local, Set1<LocationExtended>>,
237205
}
238206

239207
impl<'tcx> Visitor<'tcx> for SsaVisitor {
@@ -259,15 +227,11 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor {
259227
)
260228
| PlaceContext::MutatingUse(_) => {
261229
self.assignments[local] = Set1::Many;
262-
self.dominators.check_dominates(&mut self.storage_live[local], loc);
263230
}
264231
PlaceContext::NonMutatingUse(_) => {
265232
self.dominators.check_dominates(&mut self.assignments[local], loc);
266233
self.direct_uses[local] += 1;
267234
}
268-
PlaceContext::NonUse(NonUseContext::StorageLive) => {
269-
self.storage_live[local].insert(LocationExtended::Plain(loc));
270-
}
271235
PlaceContext::NonUse(_) => {}
272236
}
273237
}
@@ -335,3 +299,37 @@ fn compute_copy_classes(ssa: &mut SsaVisitor, body: &Body<'_>) -> IndexVec<Local
335299

336300
copies
337301
}
302+
303+
#[derive(Debug)]
304+
pub(crate) struct StorageLiveLocals {
305+
/// Set of "StorageLive" statements for each local. When the "StorageLive" statement does not
306+
/// dominate all address-taking uses of the local, we mark it as `Set1::Many`.
307+
storage_live: IndexVec<Local, Set1<LocationExtended>>,
308+
}
309+
310+
impl StorageLiveLocals {
311+
pub(crate) fn new(
312+
body: &Body<'_>,
313+
always_storage_live_locals: &BitSet<Local>,
314+
) -> StorageLiveLocals {
315+
let mut storage_live = IndexVec::from_elem(Set1::Empty, &body.local_decls);
316+
for local in always_storage_live_locals.iter() {
317+
storage_live[local] = Set1::One(LocationExtended::Arg);
318+
}
319+
for (block, bbdata) in body.basic_blocks.iter_enumerated() {
320+
for (statement_index, statement) in bbdata.statements.iter().enumerate() {
321+
if let StatementKind::StorageLive(local) = statement.kind {
322+
storage_live[local]
323+
.insert(LocationExtended::Plain(Location { block, statement_index }));
324+
}
325+
}
326+
}
327+
debug!(?storage_live);
328+
StorageLiveLocals { storage_live }
329+
}
330+
331+
#[inline]
332+
pub(crate) fn has_single_storage(&self, local: Local) -> bool {
333+
matches!(self.storage_live[local], Set1::One(_))
334+
}
335+
}

0 commit comments

Comments
 (0)