Skip to content

Commit 499d784

Browse files
committed
amortize dfs storage creation
1 parent 96f3e56 commit 499d784

File tree

2 files changed

+65
-31
lines changed

2 files changed

+65
-31
lines changed

src/librustc_mir/borrow_check/nll/region_infer/dfs.rs

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,26 @@ use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueEle
1818
use syntax::codemap::Span;
1919
use rustc::mir::{Location, Mir};
2020
use rustc::ty::RegionVid;
21-
use rustc_data_structures::fx::FxHashSet;
21+
use rustc_data_structures::bitvec::BitVector;
22+
use rustc_data_structures::indexed_vec::Idx;
23+
24+
pub(super) struct DfsStorage {
25+
stack: Vec<Location>,
26+
visited: BitVector,
27+
}
2228

2329
impl<'tcx> RegionInferenceContext<'tcx> {
30+
/// Creates dfs storage for use by dfs; this should be shared
31+
/// across as many calls to dfs as possible to amortize allocation
32+
/// costs.
33+
pub(super) fn new_dfs_storage(&self) -> DfsStorage {
34+
let num_elements = self.elements.num_elements();
35+
DfsStorage {
36+
stack: vec![],
37+
visited: BitVector::new(num_elements),
38+
}
39+
}
40+
2441
/// Function used to satisfy or test a `R1: R2 @ P`
2542
/// constraint. The core idea is that it performs a DFS starting
2643
/// from `P`. The precise actions *during* that DFS depend on the
@@ -35,25 +52,28 @@ impl<'tcx> RegionInferenceContext<'tcx> {
3552
/// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the
3653
/// value that `op` returned.
3754
#[inline(never)] // ensure dfs is identifiable in profiles
38-
pub(super) fn dfs<C>(&self, mir: &Mir<'tcx>, mut op: C) -> Result<bool, C::Early>
55+
pub(super) fn dfs<C>(
56+
&self,
57+
mir: &Mir<'tcx>,
58+
dfs: &mut DfsStorage,
59+
mut op: C,
60+
) -> Result<bool, C::Early>
3961
where
4062
C: DfsOp,
4163
{
4264
let mut changed = false;
4365

44-
let mut stack = vec![];
45-
let mut visited = FxHashSet();
46-
47-
stack.push(op.start_point());
48-
while let Some(p) = stack.pop() {
66+
dfs.visited.clear();
67+
dfs.stack.push(op.start_point());
68+
while let Some(p) = dfs.stack.pop() {
4969
let point_index = self.elements.index(p);
5070

5171
if !op.source_region_contains(point_index) {
5272
debug!(" not in from-region");
5373
continue;
5474
}
5575

56-
if !visited.insert(p) {
76+
if !dfs.visited.insert(point_index.index()) {
5777
debug!(" already visited");
5878
continue;
5979
}
@@ -63,25 +83,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
6383

6484
let block_data = &mir[p.block];
6585

66-
let start_stack_len = stack.len();
86+
let start_stack_len = dfs.stack.len();
6787

6888
if p.statement_index < block_data.statements.len() {
69-
stack.push(Location {
89+
dfs.stack.push(Location {
7090
statement_index: p.statement_index + 1,
7191
..p
7292
});
7393
} else {
74-
stack.extend(block_data.terminator().successors().iter().map(
75-
|&basic_block| {
76-
Location {
94+
dfs.stack.extend(
95+
block_data
96+
.terminator()
97+
.successors()
98+
.iter()
99+
.map(|&basic_block| Location {
77100
statement_index: 0,
78101
block: basic_block,
79-
}
80-
},
81-
));
102+
}),
103+
);
82104
}
83105

84-
if stack.len() == start_stack_len {
106+
if dfs.stack.len() == start_stack_len {
85107
// If we reach the END point in the graph, then copy
86108
// over any skolemized end points in the `from_region`
87109
// and make sure they are included in the `to_region`.
@@ -230,9 +252,8 @@ impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> {
230252
// `X: ur_in_source`, OK.
231253
if self.inferred_values
232254
.universal_regions_outlived_by(self.target_region)
233-
.any(|ur_in_target| {
234-
self.universal_regions.outlives(ur_in_target, ur_in_source)
235-
}) {
255+
.any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source))
256+
{
236257
continue;
237258
}
238259

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

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
436436
) -> Option<ClosureRegionRequirements<'gcx>> {
437437
assert!(self.inferred_values.is_none(), "values already inferred");
438438

439-
self.propagate_constraints(mir);
439+
let dfs_storage = &mut self.new_dfs_storage();
440+
441+
self.propagate_constraints(mir, dfs_storage);
440442

441443
// If this is a closure, we can propagate unsatisfied
442444
// `outlives_requirements` to our creator, so create a vector
@@ -449,7 +451,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
449451
None
450452
};
451453

452-
self.check_type_tests(infcx, mir, mir_def_id, outlives_requirements.as_mut());
454+
self.check_type_tests(infcx, mir, dfs_storage, mir_def_id, outlives_requirements.as_mut());
453455

454456
self.check_universal_regions(infcx, mir_def_id, outlives_requirements.as_mut());
455457

@@ -469,22 +471,28 @@ impl<'tcx> RegionInferenceContext<'tcx> {
469471
/// Re-execute the region inference, this time tracking causal information.
470472
/// This is significantly slower, so it is done only when an error is being reported.
471473
pub(super) fn compute_causal_info(&self, mir: &Mir<'tcx>) -> RegionCausalInfo {
472-
let inferred_values = self.compute_region_values(mir, TrackCauses(true));
474+
let dfs_storage = &mut self.new_dfs_storage();
475+
let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(true));
473476
RegionCausalInfo { inferred_values }
474477
}
475478

476479
/// Propagate the region constraints: this will grow the values
477480
/// for each region variable until all the constraints are
478481
/// satisfied. Note that some values may grow **too** large to be
479482
/// feasible, but we check this later.
480-
fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
483+
fn propagate_constraints(&mut self, mir: &Mir<'tcx>, dfs_storage: &mut dfs::DfsStorage) {
481484
self.dependency_map = Some(self.build_dependency_map());
482-
let inferred_values = self.compute_region_values(mir, TrackCauses(false));
485+
let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(false));
483486
self.inferred_values = Some(inferred_values);
484487
}
485488

486489
#[inline(never)] // ensure dfs is identifiable in profiles
487-
fn compute_region_values(&self, mir: &Mir<'tcx>, track_causes: TrackCauses) -> RegionValues {
490+
fn compute_region_values(
491+
&self,
492+
mir: &Mir<'tcx>,
493+
dfs_storage: &mut dfs::DfsStorage,
494+
track_causes: TrackCauses,
495+
) -> RegionValues {
488496
debug!("compute_region_values()");
489497
debug!("compute_region_values: constraints={:#?}", {
490498
let mut constraints: Vec<_> = self.constraints.iter().collect();
@@ -515,6 +523,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
515523
// outlives constraint.
516524
let Ok(made_changes) = self.dfs(
517525
mir,
526+
dfs_storage,
518527
CopyFromSourceToTarget {
519528
source_region: constraint.sub,
520529
target_region: constraint.sup,
@@ -569,6 +578,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
569578
&self,
570579
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
571580
mir: &Mir<'tcx>,
581+
dfs_storage: &mut dfs::DfsStorage,
572582
mir_def_id: DefId,
573583
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
574584
) {
@@ -577,7 +587,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
577587
for type_test in &self.type_tests {
578588
debug!("check_type_test: {:?}", type_test);
579589

580-
if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) {
590+
if self.eval_region_test(mir, dfs_storage, type_test.point, type_test.lower_bound, &type_test.test) {
581591
continue;
582592
}
583593

@@ -834,6 +844,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
834844
fn eval_region_test(
835845
&self,
836846
mir: &Mir<'tcx>,
847+
dfs_storage: &mut dfs::DfsStorage,
837848
point: Location,
838849
lower_bound: RegionVid,
839850
test: &RegionTest,
@@ -846,26 +857,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
846857
match test {
847858
RegionTest::IsOutlivedByAllRegionsIn(regions) => regions
848859
.iter()
849-
.all(|&r| self.eval_outlives(mir, r, lower_bound, point)),
860+
.all(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)),
850861

851862
RegionTest::IsOutlivedByAnyRegionIn(regions) => regions
852863
.iter()
853-
.any(|&r| self.eval_outlives(mir, r, lower_bound, point)),
864+
.any(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)),
854865

855866
RegionTest::Any(tests) => tests
856867
.iter()
857-
.any(|test| self.eval_region_test(mir, point, lower_bound, test)),
868+
.any(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)),
858869

859870
RegionTest::All(tests) => tests
860871
.iter()
861-
.all(|test| self.eval_region_test(mir, point, lower_bound, test)),
872+
.all(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)),
862873
}
863874
}
864875

865876
// Evaluate whether `sup_region: sub_region @ point`.
866877
fn eval_outlives(
867878
&self,
868879
mir: &Mir<'tcx>,
880+
dfs_storage: &mut dfs::DfsStorage,
869881
sup_region: RegionVid,
870882
sub_region: RegionVid,
871883
point: Location,
@@ -881,6 +893,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
881893
// yield an `Err` result.
882894
match self.dfs(
883895
mir,
896+
dfs_storage,
884897
TestTargetOutlivesSource {
885898
source_region: sub_region,
886899
target_region: sup_region,

0 commit comments

Comments
 (0)