Skip to content

Commit da90b26

Browse files
committed
Auto merge of #52991 - nikomatsakis:nll-escaping-into-return, r=<try>
[WIP] avoid computing liveness for locals that escape into statics Fixes #52713 I poked at this on the plane and I think it's working -- but I want to do a bit more investigation and double check. The idea is to identify those local variables where the entire value will "escape" into the return -- for them, we don't need to compute liveness, since we know that the outlives relations from the return type will force those regions to be equal to free regions. This should help with html5ever in particular. r? @pnkfelix cc @lqd
2 parents 03da14b + 341a07c commit da90b26

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)