Skip to content

Commit 308cc76

Browse files
committed
GVN borrowed locals too.
1 parent 5d144e3 commit 308cc76

12 files changed

+111
-121
lines changed

compiler/rustc_mir_transform/src/copy_prop.rs

+11-27
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use rustc_index::IndexSlice;
33
use rustc_middle::mir::visit::*;
44
use rustc_middle::mir::*;
55
use rustc_middle::ty::TyCtxt;
6-
use rustc_mir_dataflow::impls::borrowed_locals;
76

87
use crate::ssa::SsaLocals;
98

@@ -32,8 +31,8 @@ impl<'tcx> MirPass<'tcx> for CopyProp {
3231
}
3332

3433
fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
35-
let borrowed_locals = borrowed_locals(body);
36-
let ssa = SsaLocals::new(body);
34+
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
35+
let ssa = SsaLocals::new(tcx, body, param_env);
3736

3837
let fully_moved = fully_moved_locals(&ssa, body);
3938
debug!(?fully_moved);
@@ -51,7 +50,7 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
5150
tcx,
5251
copy_classes: ssa.copy_classes(),
5352
fully_moved,
54-
borrowed_locals,
53+
borrowed_locals: ssa.borrowed_locals(),
5554
storage_to_remove,
5655
}
5756
.visit_body_preserves_cfg(body);
@@ -101,7 +100,7 @@ struct Replacer<'a, 'tcx> {
101100
tcx: TyCtxt<'tcx>,
102101
fully_moved: BitSet<Local>,
103102
storage_to_remove: BitSet<Local>,
104-
borrowed_locals: BitSet<Local>,
103+
borrowed_locals: &'a BitSet<Local>,
105104
copy_classes: &'a IndexSlice<Local, Local>,
106105
}
107106

@@ -112,6 +111,9 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
112111

113112
fn visit_local(&mut self, local: &mut Local, ctxt: PlaceContext, _: Location) {
114113
let new_local = self.copy_classes[*local];
114+
if self.borrowed_locals.contains(*local) || self.borrowed_locals.contains(new_local) {
115+
return;
116+
}
115117
match ctxt {
116118
// Do not modify the local in storage statements.
117119
PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => {}
@@ -122,32 +124,14 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
122124
}
123125
}
124126

125-
fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) {
127+
fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
126128
if let Some(new_projection) = self.process_projection(place.projection, loc) {
127129
place.projection = self.tcx().mk_place_elems(&new_projection);
128130
}
129131

130-
let observes_address = match ctxt {
131-
PlaceContext::NonMutatingUse(
132-
NonMutatingUseContext::SharedBorrow
133-
| NonMutatingUseContext::FakeBorrow
134-
| NonMutatingUseContext::AddressOf,
135-
) => true,
136-
// For debuginfo, merging locals is ok.
137-
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {
138-
self.borrowed_locals.contains(place.local)
139-
}
140-
_ => false,
141-
};
142-
if observes_address && !place.is_indirect() {
143-
// We observe the address of `place.local`. Do not replace it.
144-
} else {
145-
self.visit_local(
146-
&mut place.local,
147-
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
148-
loc,
149-
)
150-
}
132+
// Any non-mutating use context is ok.
133+
let ctxt = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
134+
self.visit_local(&mut place.local, ctxt, loc)
151135
}
152136

153137
fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) {

compiler/rustc_mir_transform/src/gvn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ impl<'tcx> MirPass<'tcx> for GVN {
121121

122122
fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
123123
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
124-
let ssa = SsaLocals::new(body);
124+
let ssa = SsaLocals::new(tcx, body, param_env);
125125
// Clone dominators as we need them while mutating the body.
126126
let dominators = body.basic_blocks.dominators().clone();
127127

compiler/rustc_mir_transform/src/normalize_array_len.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
2222
}
2323

2424
fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
25-
let ssa = SsaLocals::new(body);
25+
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
26+
let ssa = SsaLocals::new(tcx, body, param_env);
2627

2728
let slice_lengths = compute_slice_length(tcx, &ssa, body);
2829
debug!(?slice_lengths);

compiler/rustc_mir_transform/src/ref_prop.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ impl<'tcx> MirPass<'tcx> for ReferencePropagation {
8282
}
8383

8484
fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
85-
let ssa = SsaLocals::new(body);
85+
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
86+
let ssa = SsaLocals::new(tcx, body, param_env);
8687

8788
let mut replacer = compute_replacement(tcx, body, &ssa);
8889
debug!(?replacer.targets);

compiler/rustc_mir_transform/src/ssa.rs

+43-12
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
//! 1/ They are only assigned-to once, either as a function parameter, or in an assign statement;
33
//! 2/ This single assignment dominates all uses;
44
//!
5-
//! As a consequence of rule 2, we consider that borrowed locals are not SSA, even if they are
6-
//! `Freeze`, as we do not track that the assignment dominates all uses of the borrow.
5+
//! As we do not track indirect assignments, a local that has its address taken (either by
6+
//! AddressOf or by borrowing) is considered non-SSA. However, it is UB to modify through an
7+
//! immutable borrow of a `Freeze` local. Those can still be considered to be SSA.
78
89
use rustc_data_structures::graph::dominators::Dominators;
910
use rustc_index::bit_set::BitSet;
1011
use rustc_index::{IndexSlice, IndexVec};
1112
use rustc_middle::middle::resolve_bound_vars::Set1;
1213
use rustc_middle::mir::visit::*;
1314
use rustc_middle::mir::*;
15+
use rustc_middle::ty::{ParamEnv, TyCtxt};
1416

1517
pub struct SsaLocals {
1618
/// Assignments to each local. This defines whether the local is SSA.
@@ -24,6 +26,8 @@ pub struct SsaLocals {
2426
/// Number of "direct" uses of each local, ie. uses that are not dereferences.
2527
/// We ignore non-uses (Storage statements, debuginfo).
2628
direct_uses: IndexVec<Local, u32>,
29+
/// Set of SSA locals that are immutably borrowed.
30+
borrowed_locals: BitSet<Local>,
2731
}
2832

2933
pub enum AssignedValue<'a, 'tcx> {
@@ -33,15 +37,22 @@ pub enum AssignedValue<'a, 'tcx> {
3337
}
3438

3539
impl SsaLocals {
36-
pub fn new<'tcx>(body: &Body<'tcx>) -> SsaLocals {
40+
pub fn new<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, param_env: ParamEnv<'tcx>) -> SsaLocals {
3741
let assignment_order = Vec::with_capacity(body.local_decls.len());
3842

3943
let assignments = IndexVec::from_elem(Set1::Empty, &body.local_decls);
4044
let dominators = body.basic_blocks.dominators();
4145

4246
let direct_uses = IndexVec::from_elem(0, &body.local_decls);
43-
let mut visitor =
44-
SsaVisitor { body, assignments, assignment_order, dominators, direct_uses };
47+
let borrowed_locals = BitSet::new_empty(body.local_decls.len());
48+
let mut visitor = SsaVisitor {
49+
body,
50+
assignments,
51+
assignment_order,
52+
dominators,
53+
direct_uses,
54+
borrowed_locals,
55+
};
4556

4657
for local in body.args_iter() {
4758
visitor.assignments[local] = Set1::One(DefLocation::Argument);
@@ -58,6 +69,16 @@ impl SsaLocals {
5869
visitor.visit_var_debug_info(var_debug_info);
5970
}
6071

72+
// The immutability of shared borrows only works on `Freeze` locals. If the visitor found
73+
// borrows, we need to check the types. For raw pointers and mutable borrows, the locals
74+
// have already been marked as non-SSA.
75+
debug!(?visitor.borrowed_locals);
76+
for local in visitor.borrowed_locals.iter() {
77+
if !body.local_decls[local].ty.is_freeze(tcx, param_env) {
78+
visitor.assignments[local] = Set1::Many;
79+
}
80+
}
81+
6182
debug!(?visitor.assignments);
6283
debug!(?visitor.direct_uses);
6384

@@ -70,6 +91,7 @@ impl SsaLocals {
7091
assignments: visitor.assignments,
7192
assignment_order: visitor.assignment_order,
7293
direct_uses: visitor.direct_uses,
94+
borrowed_locals: visitor.borrowed_locals,
7395
// This is filled by `compute_copy_classes`.
7496
copy_classes: IndexVec::default(),
7597
};
@@ -174,6 +196,11 @@ impl SsaLocals {
174196
&self.copy_classes
175197
}
176198

199+
/// Set of SSA locals that are immutably borrowed.
200+
pub fn borrowed_locals(&self) -> &BitSet<Local> {
201+
&self.borrowed_locals
202+
}
203+
177204
/// Make a property uniform on a copy equivalence class by removing elements.
178205
pub fn meet_copy_equivalence(&self, property: &mut BitSet<Local>) {
179206
// Consolidate to have a local iff all its copies are.
@@ -208,6 +235,8 @@ struct SsaVisitor<'tcx, 'a> {
208235
assignments: IndexVec<Local, Set1<DefLocation>>,
209236
assignment_order: Vec<Local>,
210237
direct_uses: IndexVec<Local, u32>,
238+
// Track locals that are immutably borrowed, so we can check their type is `Freeze` later.
239+
borrowed_locals: BitSet<Local>,
211240
}
212241

213242
impl SsaVisitor<'_, '_> {
@@ -232,16 +261,18 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'tcx, '_> {
232261
PlaceContext::MutatingUse(MutatingUseContext::Projection)
233262
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => bug!(),
234263
// Anything can happen with raw pointers, so remove them.
235-
// We do not verify that all uses of the borrow dominate the assignment to `local`,
236-
// so we have to remove them too.
237-
PlaceContext::NonMutatingUse(
238-
NonMutatingUseContext::SharedBorrow
239-
| NonMutatingUseContext::FakeBorrow
240-
| NonMutatingUseContext::AddressOf,
241-
)
264+
PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf)
242265
| PlaceContext::MutatingUse(_) => {
243266
self.assignments[local] = Set1::Many;
244267
}
268+
// Immutable borrows are ok, but we need to delay a check that the type is `Freeze`.
269+
PlaceContext::NonMutatingUse(
270+
NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::FakeBorrow,
271+
) => {
272+
self.borrowed_locals.insert(local);
273+
self.check_dominates(local, loc);
274+
self.direct_uses[local] += 1;
275+
}
245276
PlaceContext::NonMutatingUse(_) => {
246277
self.check_dominates(local, loc);
247278
self.direct_uses[local] += 1;

tests/mir-opt/gvn.borrowed.GVN.panic-abort.diff

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
}
1414

1515
bb1: {
16-
_0 = opaque::<u32>(_2) -> [return: bb2, unwind unreachable];
16+
- _0 = opaque::<u32>(_2) -> [return: bb2, unwind unreachable];
17+
+ _0 = opaque::<u32>(_1) -> [return: bb2, unwind unreachable];
1718
}
1819

1920
bb2: {

tests/mir-opt/gvn.borrowed.GVN.panic-unwind.diff

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
}
1414

1515
bb1: {
16-
_0 = opaque::<u32>(_2) -> [return: bb2, unwind continue];
16+
- _0 = opaque::<u32>(_2) -> [return: bb2, unwind continue];
17+
+ _0 = opaque::<u32>(_1) -> [return: bb2, unwind continue];
1718
}
1819

1920
bb2: {

tests/mir-opt/gvn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ fn borrowed(x: u32) {
728728
// CHECK-NEXT: _3 = &_1;
729729
// CHECK-NEXT: _0 = opaque::<&u32>(_3)
730730
// CHECK: bb1: {
731-
// CHECK-NEXT: _0 = opaque::<u32>(_2)
731+
// CHECK-NEXT: _0 = opaque::<u32>(_1)
732732
// CHECK: bb2: {
733733
// CHECK-NEXT: _0 = opaque::<u32>((*_3))
734734
mir!(

0 commit comments

Comments
 (0)