Skip to content

Commit 0baf2b5

Browse files
committed
Auto merge of #58505 - schomatis:fix/nll/remove-live-var, r=<try>
[NLL] Remove `LiveVar` The `LiveVar` type (and related) made it harder to reason about the code. It seemed as an abstraction that didn't bring any useful concept to the reader (when transitioning from the RFC theory to the actual implementation code). It achieved a compactness in the vectors storing the def/use/drop information that was related only to the `LocalUseMap`. This PR went in the other direction and favored time over memory (but this decision can be easily reverted to the other side without reintroducing `LiveVar`). What this PR aims at is to clarify that there's no significant transformation between the MIR `Local` and the `LiveVar` (now refactored as `live_locals: Vec<Local>`): we're just filtering (not mapping) the entire group of `Local`s into a meaningful subset that we should perform the liveness analysis on. As a side note, there is no guarantee that the liveness analysis is performed only on (what the code calls) "live" variables, if the NLL facts are requested it will be performed on *any* variable so there can't be any assumptions on that regard. (Still, this PR didn't change the general naming convention to reduce the number of changes here and streamline the review process). **Acceptance criteria:** This PR attempts to do only a minor refactoring and not to change the logic so it can't have any performance impact, particularly, it can't lose any of the significant performance improvement achieved in the great work done in #52115. r? @nikomatsakis
2 parents eac0908 + ae5f722 commit 0baf2b5

File tree

7 files changed

+179
-267
lines changed

7 files changed

+179
-267
lines changed

src/librustc_mir/borrow_check/nll/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::borrow_check::borrow_set::BorrowSet;
22
use crate::borrow_check::location::{LocationIndex, LocationTable};
33
use crate::borrow_check::nll::facts::AllFactsExt;
44
use crate::borrow_check::nll::type_check::{MirTypeckResults, MirTypeckRegionConstraints};
5-
use crate::borrow_check::nll::type_check::liveness::liveness_map::NllLivenessMap;
65
use crate::borrow_check::nll::region_infer::values::RegionValueElements;
76
use crate::dataflow::indexes::BorrowIndex;
87
use crate::dataflow::move_paths::MoveData;

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

-94
This file was deleted.

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

+49-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::borrow_check::nll::region_infer::values::{PointIndex, RegionValueElements};
2-
use crate::borrow_check::nll::type_check::liveness::liveness_map::{LiveVar, NllLivenessMap};
3-
use crate::util::liveness::{categorize, DefUse, LiveVariableMap};
2+
use crate::util::liveness::{categorize, DefUse};
43
use rustc::mir::visit::{PlaceContext, Visitor};
54
use rustc::mir::{Local, Location, Mir};
65
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
@@ -9,26 +8,33 @@ use rustc_data_structures::vec_linked_list as vll;
98
/// A map that cross references each local with the locations where it
109
/// is defined (assigned), used, or dropped. Used during liveness
1110
/// computation.
12-
crate struct LocalUseMap<'me> {
13-
liveness_map: &'me NllLivenessMap,
14-
11+
///
12+
/// We keep track only of `Local`s we'll do the liveness analysis later,
13+
/// this means that our internal `IndexVec`s will only be sparsely populated.
14+
/// In the time-memory trade-off between keeping compact vectors with new
15+
/// indexes (and needing to continuously map the `Local` index to its compact
16+
/// counterpart) and having `IndexVec`s that we only use a fraction of, time
17+
/// (and code simplicity) was favored. The rationale is that we only keep
18+
/// a small number of `IndexVec`s throughout the entire analysis while, in
19+
/// contrast, we're accessing each `Local` *many* times.
20+
crate struct LocalUseMap {
1521
/// Head of a linked list of **definitions** of each variable --
1622
/// definition in this context means assignment, e.g., `x` is
1723
/// defined in `x = y` but not `y`; that first def is the head of
1824
/// a linked list that lets you enumerate all places the variable
1925
/// is assigned.
20-
first_def_at: IndexVec<LiveVar, Option<AppearanceIndex>>,
26+
first_def_at: IndexVec<Local, Option<AppearanceIndex>>,
2127

2228
/// Head of a linked list of **uses** of each variable -- use in
2329
/// this context means that the existing value of the variable is
2430
/// read or modified. e.g., `y` is used in `x = y` but not `x`.
2531
/// Note that `DROP(x)` terminators are excluded from this list.
26-
first_use_at: IndexVec<LiveVar, Option<AppearanceIndex>>,
32+
first_use_at: IndexVec<Local, Option<AppearanceIndex>>,
2733

2834
/// Head of a linked list of **drops** of each variable -- these
2935
/// are a special category of uses corresponding to the drop that
3036
/// we add for each local variable.
31-
first_drop_at: IndexVec<LiveVar, Option<AppearanceIndex>>,
37+
first_drop_at: IndexVec<Local, Option<AppearanceIndex>>,
3238

3339
appearances: IndexVec<AppearanceIndex, Appearance>,
3440
}
@@ -50,52 +56,68 @@ impl vll::LinkElem for Appearance {
5056
}
5157
}
5258

53-
impl LocalUseMap<'me> {
59+
impl LocalUseMap {
5460
crate fn build(
55-
liveness_map: &'me NllLivenessMap,
61+
live_locals: &Vec<Local>,
5662
elements: &RegionValueElements,
5763
mir: &Mir<'_>,
5864
) -> Self {
59-
let nones = IndexVec::from_elem_n(None, liveness_map.num_variables());
65+
let nones = IndexVec::from_elem_n(None, mir.local_decls.len());
6066
let mut local_use_map = LocalUseMap {
61-
liveness_map,
6267
first_def_at: nones.clone(),
6368
first_use_at: nones.clone(),
6469
first_drop_at: nones,
6570
appearances: IndexVec::new(),
6671
};
6772

73+
let mut locals_with_use_data: IndexVec<Local, bool> =
74+
IndexVec::from_elem_n(false, mir.local_decls.len());
75+
live_locals
76+
.iter()
77+
.for_each(|&local| locals_with_use_data[local] = true);
78+
6879
LocalUseMapBuild {
6980
local_use_map: &mut local_use_map,
7081
elements,
71-
}.visit_mir(mir);
82+
locals_with_use_data,
83+
}
84+
.visit_mir(mir);
7285

7386
local_use_map
7487
}
7588

76-
crate fn defs(&self, local: LiveVar) -> impl Iterator<Item = PointIndex> + '_ {
89+
crate fn defs(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
7790
vll::iter(self.first_def_at[local], &self.appearances)
7891
.map(move |aa| self.appearances[aa].point_index)
7992
}
8093

81-
crate fn uses(&self, local: LiveVar) -> impl Iterator<Item = PointIndex> + '_ {
94+
crate fn uses(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
8295
vll::iter(self.first_use_at[local], &self.appearances)
8396
.map(move |aa| self.appearances[aa].point_index)
8497
}
8598

86-
crate fn drops(&self, local: LiveVar) -> impl Iterator<Item = PointIndex> + '_ {
99+
crate fn drops(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
87100
vll::iter(self.first_drop_at[local], &self.appearances)
88101
.map(move |aa| self.appearances[aa].point_index)
89102
}
90103
}
91104

92-
struct LocalUseMapBuild<'me, 'map: 'me> {
93-
local_use_map: &'me mut LocalUseMap<'map>,
105+
struct LocalUseMapBuild<'me> {
106+
local_use_map: &'me mut LocalUseMap,
94107
elements: &'me RegionValueElements,
108+
109+
// Vector used in `visit_local` to signal which `Local`s do we need
110+
// def/use/drop information on, constructed from `live_locals` (that
111+
// contains the variables we'll do the liveness analysis for).
112+
// This vector serves optimization purposes only: we could have
113+
// obtained the same information from `live_locals` but we want to
114+
// avoid repeatedly calling `Vec::contains()` (see `LocalUseMap` for
115+
// the rationale on the time-memory trade-off we're favoring here).
116+
locals_with_use_data: IndexVec<Local, bool>,
95117
}
96118

97-
impl LocalUseMapBuild<'_, '_> {
98-
fn insert_def(&mut self, local: LiveVar, location: Location) {
119+
impl LocalUseMapBuild<'_> {
120+
fn insert_def(&mut self, local: Local, location: Location) {
99121
Self::insert(
100122
self.elements,
101123
&mut self.local_use_map.first_def_at[local],
@@ -104,7 +126,7 @@ impl LocalUseMapBuild<'_, '_> {
104126
);
105127
}
106128

107-
fn insert_use(&mut self, local: LiveVar, location: Location) {
129+
fn insert_use(&mut self, local: Local, location: Location) {
108130
Self::insert(
109131
self.elements,
110132
&mut self.local_use_map.first_use_at[local],
@@ -113,7 +135,7 @@ impl LocalUseMapBuild<'_, '_> {
113135
);
114136
}
115137

116-
fn insert_drop(&mut self, local: LiveVar, location: Location) {
138+
fn insert_drop(&mut self, local: Local, location: Location) {
117139
Self::insert(
118140
self.elements,
119141
&mut self.local_use_map.first_drop_at[local],
@@ -137,13 +159,13 @@ impl LocalUseMapBuild<'_, '_> {
137159
}
138160
}
139161

140-
impl Visitor<'tcx> for LocalUseMapBuild<'_, '_> {
162+
impl Visitor<'tcx> for LocalUseMapBuild<'_> {
141163
fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, location: Location) {
142-
if let Some(local_with_region) = self.local_use_map.liveness_map.from_local(local) {
164+
if self.locals_with_use_data[local] {
143165
match categorize(context) {
144-
Some(DefUse::Def) => self.insert_def(local_with_region, location),
145-
Some(DefUse::Use) => self.insert_use(local_with_region, location),
146-
Some(DefUse::Drop) => self.insert_drop(local_with_region, location),
166+
Some(DefUse::Def) => self.insert_def(local, location),
167+
Some(DefUse::Use) => self.insert_use(local, location),
168+
Some(DefUse::Drop) => self.insert_drop(local, location),
147169
_ => (),
148170
}
149171
}

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

+70-15
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
use crate::borrow_check::location::LocationTable;
2-
use crate::borrow_check::nll::region_infer::values::RegionValueElements;
32
use crate::borrow_check::nll::constraints::ConstraintSet;
4-
use crate::borrow_check::nll::NllLivenessMap;
3+
use crate::borrow_check::nll::facts::{AllFacts, AllFactsExt};
4+
use crate::borrow_check::nll::region_infer::values::RegionValueElements;
55
use crate::borrow_check::nll::universal_regions::UniversalRegions;
6+
use crate::borrow_check::nll::ToRegionVid;
67
use crate::dataflow::move_paths::MoveData;
7-
use crate::dataflow::MaybeInitializedPlaces;
88
use crate::dataflow::FlowAtLocation;
9-
use rustc::mir::Mir;
10-
use rustc::ty::RegionVid;
9+
use crate::dataflow::MaybeInitializedPlaces;
10+
use rustc::mir::{Local, Mir};
11+
use rustc::ty::{RegionVid, TyCtxt};
1112
use rustc_data_structures::fx::FxHashSet;
1213
use std::rc::Rc;
1314

1415
use super::TypeChecker;
1516

16-
crate mod liveness_map;
1717
mod local_use_map;
1818
mod trace;
1919

@@ -34,16 +34,71 @@ pub(super) fn generate<'gcx, 'tcx>(
3434
location_table: &LocationTable,
3535
) {
3636
debug!("liveness::generate");
37-
let free_regions = {
38-
let borrowck_context = typeck.borrowck_context.as_ref().unwrap();
39-
regions_that_outlive_free_regions(
40-
typeck.infcx.num_region_vars(),
41-
&borrowck_context.universal_regions,
42-
&borrowck_context.constraints.outlives_constraints,
43-
)
37+
38+
let live_locals: Vec<Local> = if AllFacts::enabled(typeck.tcx()) {
39+
// If "dump facts from NLL analysis" was requested perform
40+
// the liveness analysis for all `Local`s. This case opens
41+
// the possibility of the variables being analyzed in `trace`
42+
// to be *any* `Local`, not just the "live" ones, so we can't
43+
// make any assumptions past this point as to the characteristics
44+
// of the `live_locals`.
45+
// FIXME: Review "live" terminology past this point, we should
46+
// not be naming the `Local`s as live.
47+
mir.local_decls.indices().collect()
48+
} else {
49+
let free_regions = {
50+
let borrowck_context = typeck.borrowck_context.as_ref().unwrap();
51+
regions_that_outlive_free_regions(
52+
typeck.infcx.num_region_vars(),
53+
&borrowck_context.universal_regions,
54+
&borrowck_context.constraints.outlives_constraints,
55+
)
56+
};
57+
compute_live_locals(typeck.tcx(), &free_regions, mir)
4458
};
45-
let liveness_map = NllLivenessMap::compute(typeck.tcx(), &free_regions, mir);
46-
trace::trace(typeck, mir, elements, flow_inits, move_data, &liveness_map, location_table);
59+
60+
if !live_locals.is_empty() {
61+
trace::trace(
62+
typeck,
63+
mir,
64+
elements,
65+
flow_inits,
66+
move_data,
67+
live_locals,
68+
location_table,
69+
);
70+
}
71+
}
72+
73+
// The purpose of `compute_live_locals` is to define the subset of `Local`
74+
// variables for which we need to do a liveness computation. We only need
75+
// to compute whether a variable `X` is live if that variable contains
76+
// some region `R` in its type where `R` is not known to outlive a free
77+
// region (i.e., where `R` may be valid for just a subset of the fn body).
78+
fn compute_live_locals(
79+
tcx: TyCtxt<'_, '_, 'tcx>,
80+
free_regions: &FxHashSet<RegionVid>,
81+
mir: &Mir<'tcx>,
82+
) -> Vec<Local> {
83+
let live_locals: Vec<Local> = mir
84+
.local_decls
85+
.iter_enumerated()
86+
.filter_map(|(local, local_decl)| {
87+
if tcx.all_free_regions_meet(&local_decl.ty, |r| {
88+
free_regions.contains(&r.to_region_vid())
89+
}) {
90+
None
91+
} else {
92+
Some(local)
93+
}
94+
})
95+
.collect();
96+
97+
debug!("{} total variables", mir.local_decls.len());
98+
debug!("{} variables need liveness", live_locals.len());
99+
debug!("{} regions outlive free regions", free_regions.len());
100+
101+
live_locals
47102
}
48103

49104
/// Computes all regions that are (currently) known to outlive free

0 commit comments

Comments
 (0)