Skip to content

Commit

Permalink
Auto merge of #90637 - Mark-Simulacrum:liveness-btree, r=lqd
Browse files Browse the repository at this point in the history
Store liveness in interval sets for region inference

On the 100,000 line test case from #90445, this reduces memory usage from 35 GB to 444 MB at peak (based on DHAT results, though with regular malloc), and yields a 9.4x speedup, with wall time going from 14.5 seconds to 1.5s. Performance results show that for the majority of real-world code this has little to no impact, but it's expected to generally scale better for auto-generated functions and other cases which stress this area of the compiler, as results on #90445 illustrate.

There may also be further room for improvement in future PRs making use of this data structures benefits over raw bitsets (which, at some level, are a less perfect fit for representing liveness, which is almost always composed of contiguous ranges, not point locations).

Fixes #90445.
  • Loading branch information
bors committed Dec 31, 2021
2 parents 984a6bf + 4abb328 commit cfa3fe5
Show file tree
Hide file tree
Showing 7 changed files with 491 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3976,6 +3976,7 @@ dependencies = [
"arrayvec",
"rustc_macros",
"rustc_serialize",
"smallvec",
]

[[package]]
Expand Down
20 changes: 11 additions & 9 deletions compiler/rustc_borrowck/src/region_infer/values.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
use rustc_index::bit_set::SparseBitMatrix;
use rustc_index::interval::IntervalSet;
use rustc_index::interval::SparseIntervalMatrix;
use rustc_index::vec::Idx;
use rustc_index::vec::IndexVec;
use rustc_middle::mir::{BasicBlock, Body, Location};
Expand Down Expand Up @@ -110,19 +112,19 @@ crate enum RegionElement {
PlaceholderRegion(ty::PlaceholderRegion),
}

/// When we initially compute liveness, we use a bit matrix storing
/// points for each region-vid.
/// When we initially compute liveness, we use an interval matrix storing
/// liveness ranges for each region-vid.
crate struct LivenessValues<N: Idx> {
elements: Rc<RegionValueElements>,
points: SparseBitMatrix<N, PointIndex>,
points: SparseIntervalMatrix<N, PointIndex>,
}

impl<N: Idx> LivenessValues<N> {
/// Creates a new set of "region values" that tracks causal information.
/// Each of the regions in num_region_variables will be initialized with an
/// empty set of points and no causal information.
crate fn new(elements: Rc<RegionValueElements>) -> Self {
Self { points: SparseBitMatrix::new(elements.num_points), elements }
Self { points: SparseIntervalMatrix::new(elements.num_points), elements }
}

/// Iterate through each region that has a value in this set.
Expand All @@ -140,7 +142,7 @@ impl<N: Idx> LivenessValues<N> {

/// Adds all the elements in the given bit array into the given
/// region. Returns whether any of them are newly added.
crate fn add_elements(&mut self, row: N, locations: &HybridBitSet<PointIndex>) -> bool {
crate fn add_elements(&mut self, row: N, locations: &IntervalSet<PointIndex>) -> bool {
debug!("LivenessValues::add_elements(row={:?}, locations={:?})", row, locations);
self.points.union_row(row, locations)
}
Expand All @@ -153,7 +155,7 @@ impl<N: Idx> LivenessValues<N> {
/// Returns `true` if the region `r` contains the given element.
crate fn contains(&self, row: N, location: Location) -> bool {
let index = self.elements.point_from_location(location);
self.points.contains(row, index)
self.points.row(row).map_or(false, |r| r.contains(index))
}

/// Returns an iterator of all the elements contained by the region `r`
Expand Down Expand Up @@ -221,7 +223,7 @@ impl PlaceholderIndices {
crate struct RegionValues<N: Idx> {
elements: Rc<RegionValueElements>,
placeholder_indices: Rc<PlaceholderIndices>,
points: SparseBitMatrix<N, PointIndex>,
points: SparseIntervalMatrix<N, PointIndex>,
free_regions: SparseBitMatrix<N, RegionVid>,

/// Placeholders represent bound regions -- so something like `'a`
Expand All @@ -241,7 +243,7 @@ impl<N: Idx> RegionValues<N> {
let num_placeholders = placeholder_indices.len();
Self {
elements: elements.clone(),
points: SparseBitMatrix::new(elements.num_points),
points: SparseIntervalMatrix::new(elements.num_points),
placeholder_indices: placeholder_indices.clone(),
free_regions: SparseBitMatrix::new(num_universal_regions),
placeholders: SparseBitMatrix::new(num_placeholders),
Expand Down
17 changes: 9 additions & 8 deletions compiler/rustc_borrowck/src/type_check/liveness/trace.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_index::bit_set::HybridBitSet;
use rustc_index::interval::IntervalSet;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
use rustc_middle::ty::{Ty, TypeFoldable};
Expand Down Expand Up @@ -105,12 +106,12 @@ struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {

/// Points where the current variable is "use live" -- meaning
/// that there is a future "full use" that may use its value.
use_live_at: HybridBitSet<PointIndex>,
use_live_at: IntervalSet<PointIndex>,

/// Points where the current variable is "drop live" -- meaning
/// that there is no future "full use" that may use its value, but
/// there is a future drop.
drop_live_at: HybridBitSet<PointIndex>,
drop_live_at: IntervalSet<PointIndex>,

/// Locations where drops may occur.
drop_locations: Vec<Location>,
Expand All @@ -125,8 +126,8 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
LivenessResults {
cx,
defs: HybridBitSet::new_empty(num_points),
use_live_at: HybridBitSet::new_empty(num_points),
drop_live_at: HybridBitSet::new_empty(num_points),
use_live_at: IntervalSet::new(num_points),
drop_live_at: IntervalSet::new(num_points),
drop_locations: vec![],
stack: vec![],
}
Expand Down Expand Up @@ -165,7 +166,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
drop_used: Vec<(Local, Location)>,
live_locals: FxHashSet<Local>,
) {
let locations = HybridBitSet::new_empty(self.cx.elements.num_points());
let locations = IntervalSet::new(self.cx.elements.num_points());

for (local, location) in drop_used {
if !live_locals.contains(&local) {
Expand Down Expand Up @@ -456,7 +457,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
fn add_use_live_facts_for(
&mut self,
value: impl TypeFoldable<'tcx>,
live_at: &HybridBitSet<PointIndex>,
live_at: &IntervalSet<PointIndex>,
) {
debug!("add_use_live_facts_for(value={:?})", value);

Expand All @@ -473,7 +474,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
dropped_local: Local,
dropped_ty: Ty<'tcx>,
drop_locations: &[Location],
live_at: &HybridBitSet<PointIndex>,
live_at: &IntervalSet<PointIndex>,
) {
debug!(
"add_drop_live_constraint(\
Expand Down Expand Up @@ -521,7 +522,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
elements: &RegionValueElements,
typeck: &mut TypeChecker<'_, 'tcx>,
value: impl TypeFoldable<'tcx>,
live_at: &HybridBitSet<PointIndex>,
live_at: &IntervalSet<PointIndex>,
) {
debug!("make_all_regions_live(value={:?})", value);
debug!(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_index/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ doctest = false
arrayvec = { version = "0.7", default-features = false }
rustc_serialize = { path = "../rustc_serialize" }
rustc_macros = { path = "../rustc_macros" }
smallvec = "1"
Loading

0 comments on commit cfa3fe5

Please sign in to comment.