Skip to content

Commit e67cfe8

Browse files
committed
Auto merge of #161938 - jackh726:polonius-lazy-liveness-final, r=lqd
Defer extra liveness calculation for Polonius Alpha Best reviewed by commit. This moves liveness calculation of NLL-boring/Polonius-relevant locals to be lazy. This allows us to skip unnecessary work. r? lqd
2 parents c64de18 + 86b1e3d commit e67cfe8

10 files changed

Lines changed: 427 additions & 117 deletions

File tree

‎compiler/rustc_borrowck/src/lib.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ struct CollectRegionConstraintsResult<'tcx> {
310310
deferred_closure_requirements: DeferredClosureRequirements<'tcx>,
311311
deferred_opaque_type_errors: Vec<DeferredOpaqueTypeError<'tcx>>,
312312
polonius_facts: Option<AllFacts<RustcFacts>>,
313-
polonius_context: Option<PoloniusContext>,
313+
polonius_context: Option<PoloniusContext<'tcx>>,
314314
}
315315

316316
/// Start borrow checking by collecting the region constraints for
@@ -797,7 +797,7 @@ pub(crate) struct MirBorrowckCtxt<'a, 'diag, 'tcx> {
797797
/// Results of Polonius analysis.
798798
polonius_output: Option<&'a PoloniusOutput>,
799799
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
800-
polonius_context: Option<&'a PoloniusContext>,
800+
polonius_context: Option<&'a PoloniusContext<'tcx>>,
801801
}
802802

803803
// Check that:

‎compiler/rustc_borrowck/src/nll.rs‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub(crate) struct NllOutput<'tcx> {
4646

4747
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g.
4848
/// localized typeck and liveness constraints.
49-
pub polonius_context: Option<PoloniusContext>,
49+
pub polonius_context: Option<PoloniusContext<'tcx>>,
5050
}
5151

5252
/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
@@ -121,7 +121,7 @@ pub(crate) fn compute_regions<'tcx>(
121121
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
122122
constraints: MirTypeckRegionConstraints<'tcx>,
123123
mut polonius_facts: Option<AllFacts<RustcFacts>>,
124-
mut polonius_context: Option<PoloniusContext>,
124+
mut polonius_context: Option<PoloniusContext<'tcx>>,
125125
) -> NllOutput<'tcx> {
126126
let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
127127
|| infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
@@ -144,20 +144,20 @@ pub(crate) fn compute_regions<'tcx>(
144144
&lowered_constraints,
145145
);
146146

147-
let num_points = location_map.num_points();
148-
149147
// If requested for `-Zpolonius=next`, compute loan liveness information.
150148
// This is done prior to `RegionInferenceContext::new`, because we may add
151149
// additional liveness constraints.
152150
if let Some(polonius_context) = polonius_context.as_mut() {
153151
let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness");
154152
polonius_context.compute_loan_liveness(
153+
infcx,
155154
&mut lowered_constraints.liveness_constraints,
156155
lowered_constraints.outlives_constraints.outlives().iter().copied(),
157156
&universal_region_relations.universal_regions,
158157
body,
158+
move_data,
159+
&location_map,
159160
borrow_set,
160-
num_points,
161161
);
162162
}
163163

‎compiler/rustc_borrowck/src/polonius/constraints.rs‎

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
22
use rustc_index::IndexVec;
33
use rustc_middle::mir::{Body, Location};
44
use rustc_middle::ty::RegionVid;
5-
use rustc_mir_dataflow::points::PointIndex;
5+
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
66

77
use crate::BorrowSet;
88
use crate::constraints::OutlivesConstraint;
99
use crate::dataflow::BorrowIndex;
10-
use crate::polonius::{ConstraintDirection, LiveRegionVariances};
11-
use crate::region_infer::values::LivenessValues;
10+
use crate::polonius::ConstraintDirection;
11+
use crate::polonius::liveness::RegionLiveness;
1212
use crate::type_check::Locations;
13-
use crate::universal_regions::UniversalRegions;
1413

1514
/// A localized outlives constraint reifies the CFG location where the outlives constraint holds,
1615
/// within the origins themselves as if they were different from point to point: from `a: b`
@@ -49,11 +48,17 @@ pub(super) struct LocalizedConstraintGraph {
4948
logical_edges: IndexVec<RegionVid, FxIndexSet<RegionVid>>,
5049
}
5150

51+
/// The source of liveness information for a given region.
52+
pub(super) trait LivenessSource<'loc> {
53+
fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_>;
54+
fn location_map(&self) -> &'loc DenseLocationMap;
55+
}
56+
5257
/// The visitor interface when traversing a `LocalizedConstraintGraph`.
5358
pub(super) trait LocalizedConstraintGraphVisitor {
5459
/// Callback called when traversing a given `loan` encounters a localized `node` it hasn't
5560
/// visited before.
56-
fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode) {}
61+
fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode, _is_live: bool) {}
5762

5863
/// Callback called when discovering a new `successor` node for the `current_node`.
5964
fn on_successor_discovered(&mut self, _current_node: LocalizedNode, _successor: LocalizedNode) {
@@ -63,7 +68,7 @@ pub(super) trait LocalizedConstraintGraphVisitor {
6368
impl LocalizedConstraintGraph {
6469
/// Traverses the constraints and returns the indexed graph of edges per node.
6570
pub(super) fn new<'tcx>(
66-
liveness: &LivenessValues,
71+
location_map: &DenseLocationMap,
6772
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
6873
) -> Self {
6974
let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
@@ -80,7 +85,7 @@ impl LocalizedConstraintGraph {
8085
Locations::Single(location) => {
8186
let node = LocalizedNode {
8287
region: outlives_constraint.sup,
83-
point: liveness.point_from_location(location),
88+
point: location_map.point_from_location(location),
8489
};
8590
edges.entry(node).or_default().insert(outlives_constraint.sub);
8691
}
@@ -92,20 +97,18 @@ impl LocalizedConstraintGraph {
9297

9398
/// Traverses the localized constraint graph per-loan, and notifies the `visitor` of discovered
9499
/// nodes and successors.
95-
pub(super) fn traverse<'tcx>(
100+
pub(super) fn traverse<'tcx, 'loc>(
96101
&self,
97102
body: &Body<'tcx>,
98-
liveness: &LivenessValues,
99-
live_region_variances: &LiveRegionVariances,
100-
universal_regions: &UniversalRegions<'tcx>,
101103
borrow_set: &BorrowSet<'tcx>,
104+
liveness_source: &mut impl LivenessSource<'loc>,
102105
visitor: &mut impl LocalizedConstraintGraphVisitor,
103106
) {
104-
let live_regions = liveness.points();
105-
106107
let mut visited = FxHashSet::default();
107108
let mut stack = Vec::new();
108109

110+
let location_map = liveness_source.location_map();
111+
109112
// Compute reachability per loan by traversing each loan's subgraph starting from where it
110113
// is introduced.
111114
for (loan_idx, loan) in borrow_set.iter_enumerated() {
@@ -114,15 +117,16 @@ impl LocalizedConstraintGraph {
114117

115118
let start_node = LocalizedNode {
116119
region: loan.region,
117-
point: liveness.point_from_location(loan.reserve_location),
120+
point: location_map.point_from_location(loan.reserve_location),
118121
};
119122
visited.insert(start_node);
120123
stack.push(start_node);
121124

122125
while let Some(node) = stack.pop() {
126+
let liveness = liveness_source.liveness_for_region(node.region);
123127
// We've reached a node we haven't visited before.
124-
let location = liveness.location_from_point(node.point);
125-
visitor.on_node_traversed(loan_idx, node);
128+
let location = location_map.to_location(node.point);
129+
visitor.on_node_traversed(loan_idx, node, liveness.is_live_at(node.point));
126130

127131
// When we find a _new_ successor, we'd like to
128132
// - visit it eventually,
@@ -140,26 +144,6 @@ impl LocalizedConstraintGraph {
140144
// - the materialized logical edges that exist virtually at all points for this
141145
// node's region, localized at this point.
142146

143-
// Universal regions propagate loans along the CFG, i.e. forwards only.
144-
let is_universal_region = universal_regions.is_universal_region(node.region);
145-
146-
// Note: there currently are cases related to promoted and const generics, where we don't yet
147-
// have variance information (possibly about temporary regions created when typeck sanitizes the
148-
// promoteds). Until that is done, we conservatively fallback to maximizing reachability by
149-
// adding a bidirectional edge here. This will not limit traversal whatsoever, and thus
150-
// propagate liveness when needed.
151-
//
152-
// FIXME: add the missing variance information and remove this fallback bidirectional edge.
153-
let liveness_direction = if is_universal_region {
154-
ConstraintDirection::Forward
155-
} else {
156-
live_region_variances
157-
.get(node.region)
158-
.copied()
159-
.flatten()
160-
.unwrap_or(ConstraintDirection::Bidirectional)
161-
};
162-
163147
// The physical edges present at this node are:
164148
//
165149
// 1. the typeck edges that flow from region to region *at this point*.
@@ -178,14 +162,14 @@ impl LocalizedConstraintGraph {
178162
//
179163
// We still need to check liveness for each next point though.
180164
if matches!(
181-
liveness_direction,
165+
liveness.direction,
182166
ConstraintDirection::Forward | ConstraintDirection::Bidirectional
183167
) {
184168
if body[location.block].statements.get(location.statement_index).is_some() {
185169
// Intra-block edges, straight line constraints from each point to its successor
186170
// within the same block.
187171
let next_point = node.point + 1;
188-
if live_regions.contains(node.region, next_point) {
172+
if liveness.is_live_at(next_point) {
189173
successor_found(LocalizedNode {
190174
region: node.region,
191175
point: next_point,
@@ -197,8 +181,8 @@ impl LocalizedConstraintGraph {
197181
for successor_block in body[location.block].terminator().successors() {
198182
let next_location =
199183
Location { block: successor_block, statement_index: 0 };
200-
let next_point = liveness.point_from_location(next_location);
201-
if live_regions.contains(node.region, next_point) {
184+
let next_point = location_map.point_from_location(next_location);
185+
if liveness.is_live_at(next_point) {
202186
successor_found(LocalizedNode {
203187
region: node.region,
204188
point: next_point,
@@ -219,9 +203,9 @@ impl LocalizedConstraintGraph {
219203
// Liveness flows into the regions live at the next point. So, in a backwards view, we'll link
220204
// the region from the current point, if it's live there, to the previous point.
221205
if matches!(
222-
liveness_direction,
206+
liveness.direction,
223207
ConstraintDirection::Backward | ConstraintDirection::Bidirectional
224-
) && live_regions.contains(node.region, node.point)
208+
) && liveness.is_live_at(node.point)
225209
{
226210
if location.statement_index > 0 {
227211
// Backward edges to the predecessor point in the same block.
@@ -239,7 +223,8 @@ impl LocalizedConstraintGraph {
239223
block: pred_block,
240224
statement_index: body[pred_block].statements.len(),
241225
};
242-
let previous_point = liveness.point_from_location(previous_location);
226+
let previous_point =
227+
location_map.point_from_location(previous_location);
243228
successor_found(LocalizedNode {
244229
region: node.region,
245230
point: previous_point,

‎compiler/rustc_borrowck/src/polonius/dump.rs‎

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,55 @@ use rustc_index::IndexVec;
55
use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions};
66
use rustc_middle::mir::{Body, Location};
77
use rustc_middle::ty::{RegionVid, TyCtxt};
8-
use rustc_mir_dataflow::points::PointIndex;
8+
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
99
use rustc_session::config::MirIncludeSpans;
1010

1111
use crate::borrow_set::BorrowSet;
1212
use crate::constraints::OutlivesConstraint;
1313
use crate::dataflow::BorrowIndex;
14-
use crate::polonius::{LocalizedConstraintGraphVisitor, LocalizedNode, PoloniusContext};
14+
use crate::polonius::liveness::RegionLiveness;
15+
use crate::polonius::{
16+
LiveRegionVariances, LivenessSource, LocalizedConstraintGraphVisitor, LocalizedNode,
17+
PoloniusContext,
18+
};
1519
use crate::region_infer::values::LivenessValues;
1620
use crate::type_check::Locations;
21+
use crate::universal_regions::UniversalRegions;
1722
use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext};
1823

1924
/// The polonius MIR dump template: a regular HTML file for easy editing, with special dummy
2025
/// sections to be replaced by real contents.
2126
const TEMPLATE: &str = include_str!("./dump/polonius-mir-dump.template.html");
2227

28+
/// A `LivenessSource` for already-existing liveness and variance data.
29+
struct CachedLivenessSource<'a, 'tcx> {
30+
live_region_variances: &'a LiveRegionVariances,
31+
universal_regions: &'a UniversalRegions<'tcx>,
32+
liveness: &'a LivenessValues,
33+
}
34+
35+
impl<'a, 'tcx> LivenessSource<'a> for CachedLivenessSource<'a, 'tcx> {
36+
fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_> {
37+
RegionLiveness::new(
38+
region,
39+
self.live_region_variances,
40+
self.universal_regions,
41+
self.liveness.points(),
42+
)
43+
}
44+
fn location_map(&self) -> &'a DenseLocationMap {
45+
self.liveness.location_map()
46+
}
47+
}
48+
2349
/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
2450
pub(crate) fn dump_polonius_mir<'tcx>(
2551
infcx: &BorrowckInferCtxt<'tcx>,
2652
body: &Body<'tcx>,
2753
regioncx: &RegionInferenceContext<'tcx>,
2854
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
2955
borrow_set: &BorrowSet<'tcx>,
30-
polonius_context: Option<&PoloniusContext>,
56+
polonius_context: Option<&PoloniusContext<'tcx>>,
3157
) {
3258
let tcx = infcx.tcx;
3359
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
@@ -41,16 +67,14 @@ pub(crate) fn dump_polonius_mir<'tcx>(
4167

4268
// If we have a polonius graph to dump along the rest of the MIR and NLL info, we extract its
4369
// constraints here.
70+
let mut liveness_source = CachedLivenessSource {
71+
live_region_variances: &polonius_context.live_region_variances,
72+
universal_regions: regioncx.universal_regions(),
73+
liveness: regioncx.liveness_constraints(),
74+
};
4475
let mut collector = MirDumpCollector::default();
4576
if let Some(graph) = &polonius_context.graph {
46-
graph.traverse(
47-
body,
48-
regioncx.liveness_constraints(),
49-
&polonius_context.live_region_variances,
50-
regioncx.universal_regions(),
51-
borrow_set,
52-
&mut collector,
53-
);
77+
graph.traverse(body, borrow_set, &mut liveness_source, &mut collector);
5478
}
5579

5680
let extra_data = &|pass_where, out: &mut dyn io::Write| {
@@ -98,7 +122,7 @@ struct MirDumpCollector {
98122
}
99123

100124
impl LocalizedConstraintGraphVisitor for MirDumpCollector {
101-
fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode) {
125+
fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode, _is_live: bool) {
102126
self.reachability.entry(loan).or_default().push(node);
103127
}
104128

0 commit comments

Comments
 (0)