Skip to content

Commit 341a07c

Browse files
nikomatsakislqd
andcommitted
compute union-find of locals flowing into the output of statics
Co-authored-by: lqd <[email protected]> Co-authored-by: nikomatsakis <[email protected]>
1 parent db54765 commit 341a07c

File tree

5 files changed

+203
-24
lines changed

5 files changed

+203
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! A MIR walk gathering a union-crfind of assigned locals, for the purpose of locating the ones
12+
//! escaping into the output.
13+
14+
use rustc::mir::visit::Visitor;
15+
use rustc::mir::*;
16+
17+
use rustc_data_structures::indexed_vec::Idx;
18+
use rustc_data_structures::unify as ut;
19+
20+
crate struct EscapingLocals {
21+
unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
22+
}
23+
24+
impl EscapingLocals {
25+
crate fn compute(mir: &Mir<'_>) -> Self {
26+
let mut visitor = GatherAssignedLocalsVisitor::new();
27+
visitor.visit_mir(mir);
28+
29+
EscapingLocals { unification_table: visitor.unification_table }
30+
}
31+
32+
/// True if `local` is known to escape into static
33+
/// memory.
34+
crate fn escapes_into_return(&mut self, local: Local) -> bool {
35+
let return_place = AssignedLocal::from(RETURN_PLACE);
36+
let other_place = AssignedLocal::from(local);
37+
self.unification_table.unioned(return_place, other_place)
38+
}
39+
}
40+
41+
/// The MIR visitor gathering the union-find of the locals used in
42+
/// assignments.
43+
struct GatherAssignedLocalsVisitor {
44+
unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
45+
}
46+
47+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
48+
struct AssignedLocal(u32);
49+
50+
impl ut::UnifyKey for AssignedLocal {
51+
type Value = ();
52+
53+
fn index(&self) -> u32 {
54+
self.0
55+
}
56+
57+
fn from_index(i: u32) -> AssignedLocal {
58+
AssignedLocal(i)
59+
}
60+
61+
fn tag() -> &'static str {
62+
"AssignedLocal"
63+
}
64+
}
65+
66+
impl From<Local> for AssignedLocal {
67+
fn from(item: Local) -> Self {
68+
// newtype_indexes use usize but are u32s.
69+
assert!(item.index() < ::std::u32::MAX as usize);
70+
AssignedLocal(item.index() as u32)
71+
}
72+
}
73+
74+
impl GatherAssignedLocalsVisitor {
75+
fn new() -> Self {
76+
Self {
77+
unification_table: ut::UnificationTable::new(),
78+
}
79+
}
80+
81+
fn union_locals_if_needed(&mut self, lvalue: Option<Local>, rvalue: Option<Local>) {
82+
if let Some(lvalue) = lvalue {
83+
if let Some(rvalue) = rvalue {
84+
if lvalue != rvalue {
85+
self.unification_table
86+
.union(AssignedLocal::from(lvalue), AssignedLocal::from(rvalue));
87+
}
88+
}
89+
}
90+
}
91+
}
92+
93+
// Returns the potential `Local` associated to this `Place` or `PlaceProjection`
94+
fn find_local_in_place(place: &Place) -> Option<Local> {
95+
match place {
96+
Place::Local(local) => Some(*local),
97+
98+
// If you do e.g. `x = a.f` then only *part* of the type of
99+
// `a` escapes into `x` (the part contained in `f`); if `a`'s
100+
// type has regions that don't appear in `f`, those might not
101+
// escape.
102+
Place::Projection(..) => None,
103+
104+
Place::Static { .. } | Place::Promoted { .. } => None,
105+
}
106+
}
107+
108+
// Returns the potential `Local` in this `Operand`.
109+
fn find_local_in_operand(op: &Operand) -> Option<Local> {
110+
// Conservatively check a subset of `Operand`s we know our
111+
// benchmarks track, for example `html5ever`.
112+
match op {
113+
Operand::Copy(place) | Operand::Move(place) => find_local_in_place(place),
114+
Operand::Constant(_) => None,
115+
}
116+
}
117+
118+
impl<'tcx> Visitor<'tcx> for GatherAssignedLocalsVisitor {
119+
fn visit_mir(&mut self, mir: &Mir<'tcx>) {
120+
// We need as many union-find keys as there are locals
121+
for _ in 0..mir.local_decls.len() {
122+
self.unification_table.new_key(());
123+
}
124+
125+
self.super_mir(mir);
126+
}
127+
128+
fn visit_assign(
129+
&mut self,
130+
block: BasicBlock,
131+
place: &Place<'tcx>,
132+
rvalue: &Rvalue<'tcx>,
133+
location: Location,
134+
) {
135+
let local = find_local_in_place(place);
136+
137+
// Conservatively check a subset of `Rvalue`s we know our
138+
// benchmarks track, for example `html5ever`.
139+
match rvalue {
140+
Rvalue::Use(op) => self.union_locals_if_needed(local, find_local_in_operand(op)),
141+
Rvalue::Ref(_, _, place) => {
142+
self.union_locals_if_needed(local, find_local_in_place(place))
143+
}
144+
145+
Rvalue::Cast(kind, op, _) => match kind {
146+
CastKind::Unsize => self.union_locals_if_needed(local, find_local_in_operand(op)),
147+
_ => (),
148+
},
149+
150+
Rvalue::Aggregate(_, ops) => {
151+
for rvalue in ops.iter().map(find_local_in_operand) {
152+
self.union_locals_if_needed(local, rvalue);
153+
}
154+
}
155+
156+
_ => (),
157+
};
158+
159+
self.super_assign(block, place, rvalue, location);
160+
}
161+
}

src/librustc_mir/borrow_check/nll/liveness_map.rs

+24-12
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
//! liveness code so that it only operates over variables with regions in their
1717
//! types, instead of all variables.
1818
19+
use borrow_check::nll::escaping_locals::EscapingLocals;
20+
use rustc::mir::{Local, Mir};
1921
use rustc::ty::TypeFoldable;
2022
use rustc_data_structures::indexed_vec::IndexVec;
21-
use rustc::mir::{Mir, Local};
2223
use util::liveness::LiveVariableMap;
2324

2425
use rustc_data_structures::indexed_vec::Idx;
@@ -29,14 +30,13 @@ use rustc_data_structures::indexed_vec::Idx;
2930
crate struct NllLivenessMap {
3031
/// For each local variable, contains either None (if the type has no regions)
3132
/// or Some(i) with a suitable index.
32-
pub from_local: IndexVec<Local, Option<LocalWithRegion>>,
33-
/// For each LocalWithRegion, maps back to the original Local index.
34-
pub to_local: IndexVec<LocalWithRegion, Local>,
33+
from_local: IndexVec<Local, Option<LocalWithRegion>>,
3534

35+
/// For each LocalWithRegion, maps back to the original Local index.
36+
to_local: IndexVec<LocalWithRegion, Local>,
3637
}
3738

3839
impl LiveVariableMap for NllLivenessMap {
39-
4040
fn from_local(&self, local: Local) -> Option<Self::LiveVar> {
4141
self.from_local[local]
4242
}
@@ -55,21 +55,33 @@ impl LiveVariableMap for NllLivenessMap {
5555
impl NllLivenessMap {
5656
/// Iterates over the variables in Mir and assigns each Local whose type contains
5757
/// regions a LocalWithRegion index. Returns a map for converting back and forth.
58-
pub fn compute(mir: &Mir) -> Self {
58+
crate fn compute(mir: &Mir<'_>) -> Self {
59+
let mut escaping_locals = EscapingLocals::compute(mir);
60+
5961
let mut to_local = IndexVec::default();
60-
let from_local: IndexVec<Local,Option<_>> = mir
62+
let from_local: IndexVec<Local, Option<_>> = mir
6163
.local_decls
6264
.iter_enumerated()
6365
.map(|(local, local_decl)| {
64-
if local_decl.ty.has_free_regions() {
66+
if escaping_locals.escapes_into_return(local) {
67+
// If the local escapes into the return value,
68+
// then the return value will force all of the
69+
// regions in its type to outlive free regions
70+
// (e.g., `'static`) and hence liveness is not
71+
// needed. This is particularly important for big
72+
// statics.
73+
None
74+
} else if local_decl.ty.has_free_regions() {
6575
Some(to_local.push(local))
76+
} else {
77+
None
6678
}
67-
else {
68-
None
69-
}
7079
}).collect();
7180

72-
Self { from_local, to_local }
81+
Self {
82+
from_local,
83+
to_local,
84+
}
7385
}
7486
}
7587

src/librustc_mir/borrow_check/nll/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ use polonius_engine::{Algorithm, Output};
3939
use util as mir_util;
4040
use util::pretty::{self, ALIGN};
4141

42+
mod constraints;
4243
mod constraint_generation;
44+
mod escaping_locals;
4345
pub mod explain_borrow;
4446
mod facts;
4547
mod invalidation;
@@ -49,8 +51,6 @@ crate mod type_check;
4951
mod universal_regions;
5052
crate mod liveness_map;
5153

52-
mod constraints;
53-
5454
use self::facts::AllFacts;
5555
use self::region_infer::RegionInferenceContext;
5656
use self::universal_regions::UniversalRegions;
@@ -120,6 +120,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
120120
location_table,
121121
borrow_set,
122122
&liveness,
123+
&liveness_map,
123124
&mut all_facts,
124125
flow_inits,
125126
move_data,

src/librustc_mir/borrow_check/nll/type_check/liveness.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,18 @@ pub(super) fn generate<'gcx, 'tcx>(
3737
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
3838
mir: &Mir<'tcx>,
3939
liveness: &LivenessResults<LocalWithRegion>,
40+
liveness_map: &NllLivenessMap,
4041
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
4142
move_data: &MoveData<'tcx>,
4243
) {
4344
let mut generator = TypeLivenessGenerator {
4445
cx,
4546
mir,
4647
liveness,
48+
liveness_map,
4749
flow_inits,
4850
move_data,
4951
drop_data: FxHashMap(),
50-
map: &NllLivenessMap::compute(mir),
5152
};
5253

5354
for bb in mir.basic_blocks().indices() {
@@ -65,10 +66,10 @@ where
6566
cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
6667
mir: &'gen Mir<'tcx>,
6768
liveness: &'gen LivenessResults<LocalWithRegion>,
69+
liveness_map: &'gen NllLivenessMap,
6870
flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
6971
move_data: &'gen MoveData<'tcx>,
7072
drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
71-
map: &'gen NllLivenessMap,
7273
}
7374

7475
struct DropData<'tcx> {
@@ -86,9 +87,9 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
8687

8788
self.liveness
8889
.regular
89-
.simulate_block(self.mir, bb, self.map, |location, live_locals| {
90+
.simulate_block(self.mir, bb, self.liveness_map, |location, live_locals| {
9091
for live_local in live_locals.iter() {
91-
let local = self.map.from_live_var(live_local);
92+
let local = self.liveness_map.from_live_var(live_local);
9293
let live_local_ty = self.mir.local_decls[local].ty;
9394
Self::push_type_live_constraint(&mut self.cx, live_local_ty, location);
9495
}
@@ -97,7 +98,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
9798
let mut all_live_locals: Vec<(Location, Vec<LocalWithRegion>)> = vec![];
9899
self.liveness
99100
.drop
100-
.simulate_block(self.mir, bb, self.map, |location, live_locals| {
101+
.simulate_block(self.mir, bb, self.liveness_map, |location, live_locals| {
101102
all_live_locals.push((location, live_locals.iter().collect()));
102103
});
103104
debug!(
@@ -124,7 +125,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
124125
});
125126
}
126127

127-
let local = self.map.from_live_var(live_local);
128+
let local = self.liveness_map.from_live_var(live_local);
128129
let mpi = self.move_data.rev_lookup.find_local(local);
129130
if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
130131
debug!(
@@ -133,7 +134,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
133134
self.move_data.move_paths[initialized_child]
134135
);
135136

136-
let local = self.map.from_live_var(live_local);
137+
let local = self.liveness_map.from_live_var(live_local);
137138
let live_local_ty = self.mir.local_decls[local].ty;
138139
self.add_drop_live_constraint(live_local, live_local_ty, location);
139140
}

src/librustc_mir/borrow_check/nll/type_check/mod.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ use borrow_check::borrow_set::BorrowSet;
1515
use borrow_check::location::LocationTable;
1616
use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
1717
use borrow_check::nll::facts::AllFacts;
18-
use borrow_check::nll::region_infer::values::{RegionValueElements, LivenessValues};
18+
use borrow_check::nll::liveness_map::NllLivenessMap;
19+
use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements};
1920
use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
20-
use borrow_check::nll::type_check::free_region_relations::{CreateResult, UniversalRegionRelations};
21+
use borrow_check::nll::type_check::free_region_relations::{
22+
CreateResult, UniversalRegionRelations,
23+
};
2124
use borrow_check::nll::universal_regions::UniversalRegions;
2225
use borrow_check::nll::LocalWithRegion;
2326
use borrow_check::nll::ToRegionVid;
@@ -116,6 +119,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
116119
location_table: &LocationTable,
117120
borrow_set: &BorrowSet<'tcx>,
118121
liveness: &LivenessResults<LocalWithRegion>,
122+
liveness_map: &NllLivenessMap,
119123
all_facts: &mut Option<AllFacts>,
120124
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
121125
move_data: &MoveData<'tcx>,
@@ -166,7 +170,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
166170
Some(&mut borrowck_context),
167171
Some(errors_buffer),
168172
|cx| {
169-
liveness::generate(cx, mir, liveness, flow_inits, move_data);
173+
liveness::generate(cx, mir, liveness, liveness_map, flow_inits, move_data);
170174
cx.equate_inputs_and_outputs(
171175
mir,
172176
mir_def_id,

0 commit comments

Comments
 (0)