diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index dec10416bcb..bc7b8f8df46 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -43,12 +43,27 @@ impl BitVector { self.data.iter().map(|e| e.count_ones() as usize).sum() } + /// True if `self` contains the bit `bit`. #[inline] pub fn contains(&self, bit: C) -> bool { let (word, mask) = word_mask(bit); (self.data[word] & mask) != 0 } + /// True if `self` contains all the bits in `other`. + /// + /// The two vectors must have the same length. + #[inline] + pub fn contains_all(&self, other: &BitVector) -> bool { + assert_eq!(self.data.len(), other.data.len()); + self.data.iter().zip(&other.data).all(|(a, b)| (a & b) == *b) + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.data.iter().all(|a| *a == 0) + } + /// Returns true if the bit has changed. #[inline] pub fn insert(&mut self, bit: C) -> bool { @@ -349,6 +364,10 @@ impl SparseBitMatrix { self.vector.len() } + pub fn rows(&self) -> impl Iterator { + self.vector.indices() + } + /// Iterates through all the columns set to true in a given row of /// the matrix. pub fn iter<'a>(&'a self, row: R) -> impl Iterator + 'a { @@ -522,3 +541,45 @@ fn matrix_iter() { } assert!(iter.next().is_none()); } + +#[test] +fn sparse_matrix_iter() { + let mut matrix = SparseBitMatrix::new(64, 100); + matrix.add(3, 22); + matrix.add(3, 75); + matrix.add(2, 99); + matrix.add(4, 0); + matrix.merge(3, 5); + + let expected = [99]; + let mut iter = expected.iter(); + for i in matrix.iter(2) { + let j = *iter.next().unwrap(); + assert_eq!(i, j); + } + assert!(iter.next().is_none()); + + let expected = [22, 75]; + let mut iter = expected.iter(); + for i in matrix.iter(3) { + let j = *iter.next().unwrap(); + assert_eq!(i, j); + } + assert!(iter.next().is_none()); + + let expected = [0]; + let mut iter = expected.iter(); + for i in matrix.iter(4) { + let j = *iter.next().unwrap(); + assert_eq!(i, j); + } + assert!(iter.next().is_none()); + + let expected = [22, 75]; + let mut iter = expected.iter(); + for i in matrix.iter(5) { + let j = *iter.next().unwrap(); + assert_eq!(i, j); + } + assert!(iter.next().is_none()); +} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index a74f4f5539f..3ba95895bfd 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -48,9 +48,6 @@ pub struct RegionInferenceContext<'tcx> { /// from as well as its final inferred value. definitions: IndexVec>, - /// Maps from points/universal-regions to a `RegionElementIndex`. - elements: Rc, - /// The liveness constraints added to each region. For most /// regions, these start out empty and steadily grow, though for /// each universally quantified region R they start out containing @@ -219,14 +216,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut scc_values = RegionValues::new(elements); - for (region, location_set) in liveness_constraints.iter_enumerated() { + for region in liveness_constraints.regions_with_points() { let scc = constraint_sccs.scc(region); - scc_values.merge_into(scc, location_set); + scc_values.merge_row(scc, region, &liveness_constraints); } let mut result = Self { definitions, - elements: elements.clone(), liveness_constraints, constraints, constraint_graph, @@ -273,7 +269,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // For each universally quantified region X: - let elements = self.elements.clone(); let universal_regions = self.universal_regions.clone(); for variable in universal_regions.universal_regions() { // These should be free-region variables. @@ -283,9 +278,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); // Add all nodes in the CFG to liveness constraints - for point_index in elements.all_point_indices() { - self.add_live_element(variable, point_index); - } + let variable_scc = self.constraint_sccs.scc(variable); + self.liveness_constraints.add_all_points(variable); + self.scc_values.add_all_points(variable_scc); // Add `end(X)` into the set for X. self.add_live_element(variable, variable); @@ -782,7 +777,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // now). Therefore, the sup-region outlives the sub-region if, // for each universal region R1 in the sub-region, there // exists some region R2 in the sup-region that outlives R1. - let universal_outlives = self.scc_values + let universal_outlives = self + .scc_values .universal_regions_outlived_by(sub_region_scc) .all(|r1| { self.scc_values diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index 4db5267cbd4..8092b773eae 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -10,11 +10,12 @@ use rustc::mir::{BasicBlock, Location, Mir}; use rustc::ty::RegionVid; -use rustc_data_structures::bitvec::{BitVector, SparseBitMatrix}; +use rustc_data_structures::bitvec::SparseBitMatrix; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt::Debug; use std::rc::Rc; +use std::ops::Range; /// Maps between the various kinds of elements of a region value to /// the internal indices that w use. @@ -50,90 +51,65 @@ impl RegionValueElements { Self { statements_before_block, - num_universal_regions, num_points, + num_universal_regions, } } - /// Total number of element indices that exist. - crate fn num_elements(&self) -> usize { - self.num_points + self.num_universal_regions + fn point_from_location(&self, location: Location) -> PointIndex { + let Location { + block, + statement_index, + } = location; + let start_index = self.statements_before_block[block]; + PointIndex::new(start_index + statement_index) } - /// Converts an element of a region value into a `RegionElementIndex`. - crate fn index(&self, elem: T) -> RegionElementIndex { - elem.to_element_index(self) + /// Range coverting all point indices. + fn all_points(&self) -> Range { + PointIndex::new(0)..PointIndex::new(self.num_points) } - /// Iterates over the `RegionElementIndex` for all points in the CFG. - crate fn all_point_indices<'a>(&'a self) -> impl Iterator + 'a { - (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions)) - } + /// Converts a particular `RegionElementIndex` to a location, if + /// that is what it represents. Returns `None` otherwise. + crate fn to_location(&self, i: PointIndex) -> Location { + let point_index = i.index(); - /// Converts a particular `RegionElementIndex` to the `RegionElement` it represents. - crate fn to_element(&self, i: RegionElementIndex) -> RegionElement { - debug!("to_element(i={:?})", i); + // Find the basic block. We have a vector with the + // starting index of the statement in each block. Imagine + // we have statement #22, and we have a vector like: + // + // [0, 10, 20] + // + // In that case, this represents point_index 2 of + // basic block BB2. We know this because BB0 accounts for + // 0..10, BB1 accounts for 11..20, and BB2 accounts for + // 20... + // + // To compute this, we could do a binary search, but + // because I am lazy we instead iterate through to find + // the last point where the "first index" (0, 10, or 20) + // was less than the statement index (22). In our case, this will + // be (BB2, 20). + // + // Nit: we could do a binary search here but I'm too lazy. + let (block, &first_index) = self + .statements_before_block + .iter_enumerated() + .filter(|(_, first_index)| **first_index <= point_index) + .last() + .unwrap(); - if let Some(r) = self.to_universal_region(i) { - RegionElement::UniversalRegion(r) - } else { - let point_index = i.index() - self.num_universal_regions; - - // Find the basic block. We have a vector with the - // starting index of the statement in each block. Imagine - // we have statement #22, and we have a vector like: - // - // [0, 10, 20] - // - // In that case, this represents point_index 2 of - // basic block BB2. We know this because BB0 accounts for - // 0..10, BB1 accounts for 11..20, and BB2 accounts for - // 20... - // - // To compute this, we could do a binary search, but - // because I am lazy we instead iterate through to find - // the last point where the "first index" (0, 10, or 20) - // was less than the statement index (22). In our case, this will - // be (BB2, 20). - // - // Nit: we could do a binary search here but I'm too lazy. - let (block, &first_index) = self - .statements_before_block - .iter_enumerated() - .filter(|(_, first_index)| **first_index <= point_index) - .last() - .unwrap(); - - RegionElement::Location(Location { - block, - statement_index: point_index - first_index, - }) - } - } - - /// Converts a particular `RegionElementIndex` to a universal - /// region, if that is what it represents. Returns `None` - /// otherwise. - crate fn to_universal_region(&self, i: RegionElementIndex) -> Option { - if i.index() < self.num_universal_regions { - Some(RegionVid::new(i.index())) - } else { - None + Location { + block, + statement_index: point_index - first_index, } } } -/// A newtype for the integers that represent one of the possible -/// elements in a region. These are the rows in the `SparseBitMatrix` that -/// is used to store the values of all regions. They have the following -/// convention: -/// -/// - The first N indices represent free regions (where N = universal_regions.len()). -/// - The remainder represent the points in the CFG (see `point_indices` map). -/// -/// You can convert a `RegionElementIndex` into a `RegionElement` -/// using the `to_region_elem` method. -newtype_index!(RegionElementIndex { DEBUG_FORMAT = "RegionElementIndex({})" }); +/// A single integer representing a `Location` in the MIR control-flow +/// graph. Constructed efficiently from `RegionValueElements`. +newtype_index!(PointIndex { DEBUG_FORMAT = "PointIndex({})" }); /// An individual element in a region value -- the value of a /// particular region variable consists of a set of these elements. @@ -142,36 +118,9 @@ crate enum RegionElement { /// A point in the control-flow graph. Location(Location), - /// An in-scope, universally quantified region (e.g., a lifetime parameter). - UniversalRegion(RegionVid), -} - -crate trait ToElementIndex: Debug + Copy { - fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex; -} - -impl ToElementIndex for Location { - fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex { - let Location { - block, - statement_index, - } = self; - let start_index = elements.statements_before_block[block]; - RegionElementIndex::new(elements.num_universal_regions + start_index + statement_index) - } -} - -impl ToElementIndex for RegionVid { - fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex { - assert!(self.index() < elements.num_universal_regions); - RegionElementIndex::new(self.index()) - } -} - -impl ToElementIndex for RegionElementIndex { - fn to_element_index(self, _elements: &RegionValueElements) -> RegionElementIndex { - self - } + /// A universally quantified region from the root universe (e.g., + /// a lifetime parameter). + RootUniversalRegion(RegionVid), } /// Stores the values for a set of regions. These are stored in a @@ -181,7 +130,8 @@ impl ToElementIndex for RegionElementIndex { #[derive(Clone)] crate struct RegionValues { elements: Rc, - matrix: SparseBitMatrix, + points: SparseBitMatrix, + free_regions: SparseBitMatrix, } impl RegionValues { @@ -191,7 +141,8 @@ impl RegionValues { crate fn new(elements: &Rc) -> Self { Self { elements: elements.clone(), - matrix: SparseBitMatrix::new(elements.num_elements()), + points: SparseBitMatrix::new(elements.num_points), + free_regions: SparseBitMatrix::new(elements.num_universal_regions), } } @@ -202,53 +153,83 @@ impl RegionValues { r: N, elem: impl ToElementIndex, ) -> bool { - let i = self.elements.index(elem); debug!("add(r={:?}, elem={:?})", r, elem); - self.matrix.add(r, i) + elem.add_to_row(self, r) + } + + /// Adds all the control-flow points to the values for `r`. + crate fn add_all_points(&mut self, r: N) { + // FIXME OMG so inefficient. We'll fix later. + for p in self.elements.all_points() { + self.points.add(r, p); + } } /// Add all elements in `r_from` to `r_to` (because e.g. `r_to: /// r_from`). crate fn add_region(&mut self, r_to: N, r_from: N) -> bool { - self.matrix.merge(r_from, r_to) + self.points.merge(r_from, r_to) | self.free_regions.merge(r_from, r_to) + // FIXME universes? } /// True if the region `r` contains the given element. - crate fn contains(&self, r: N, elem: impl ToElementIndex) -> bool { - let i = self.elements.index(elem); - self.matrix.contains(r, i) + crate fn contains( + &self, + r: N, + elem: impl ToElementIndex, + ) -> bool { + elem.contained_in_row(self, r) } - /// Iterates through each row and the accompanying bit set. - pub fn iter_enumerated<'a>( - &'a self - ) -> impl Iterator)> + 'a { - self.matrix.iter_enumerated() + /// Iterate through each region that has a value in this set. + crate fn regions_with_points<'a>(&'a self) -> impl Iterator { + self.points.rows() } - /// Merge a row, `from`, originating in another `RegionValues` into the `into` row. - pub fn merge_into(&mut self, into: N, from: &BitVector) -> bool { - self.matrix.merge_into(into, from) + /// `self[to] |= values[from]`, essentially: that is, take all the + /// elements for the region `from` from `values` and add them to + /// the region `to` in `self`. + crate fn merge_row(&mut self, to: N, from: M, values: &RegionValues) { + if let Some(set) = values.points.row(from) { + self.points.merge_into(to, set); + } + + if let Some(set) = values.free_regions.row(from) { + self.free_regions.merge_into(to, set); + } } /// True if `sup_region` contains all the CFG points that /// `sub_region` contains. Ignores universal regions. - crate fn contains_points(&self, sup_region: N, sub_region: N) -> bool { + crate fn contains_points( + &self, + sup_region: N, + sub_region: N, + ) -> bool { // This could be done faster by comparing the bitsets. But I // am lazy. - self.element_indices_contained_in(sub_region) - .skip_while(|&i| self.elements.to_universal_region(i).is_some()) - .all(|e| self.contains(sup_region, e)) + if let Some(sub_row) = self.points.row(sub_region) { + if let Some(sup_row) = self.points.row(sup_region) { + sup_row.contains_all(sub_row) + } else { + // sup row is empty, so sub row must be empty + sub_row.is_empty() + } + } else { + // sub row is empty, always true + true + } } - /// Iterate over the value of the region `r`, yielding up element - /// indices. You may prefer `universal_regions_outlived_by` or - /// `elements_contained_in`. - crate fn element_indices_contained_in<'a>( + /// Returns the locations contained within a given region `r`. + crate fn locations_outlived_by<'a>( &'a self, r: N, - ) -> impl Iterator + 'a { - self.matrix.iter(r).map(move |i| i) + ) -> impl Iterator + 'a { + self.points + .row(r) + .into_iter() + .flat_map(move |set| set.iter().map(move |p| self.elements.to_location(p))) } /// Returns just the universal regions that are contained in a given region's value. @@ -256,10 +237,10 @@ impl RegionValues { &'a self, r: N, ) -> impl Iterator + 'a { - self.element_indices_contained_in(r) - .map(move |i| self.elements.to_universal_region(i)) - .take_while(move |v| v.is_some()) // universal regions are a prefix - .map(move |v| v.unwrap()) + self.free_regions + .row(r) + .into_iter() + .flat_map(|set| set.iter()) } /// Returns all the elements contained in a given region's value. @@ -267,8 +248,15 @@ impl RegionValues { &'a self, r: N, ) -> impl Iterator + 'a { - self.element_indices_contained_in(r) - .map(move |r| self.elements.to_element(r)) + let points_iter = self + .locations_outlived_by(r) + .map(RegionElement::Location); + + let free_regions_iter = self + .universal_regions_outlived_by(r) + .map(RegionElement::RootUniversalRegion); + + points_iter.chain(free_regions_iter) } /// Returns a "pretty" string value of the region. Meant for debugging. @@ -306,7 +294,7 @@ impl RegionValues { open_location = Some((l, l)); } - RegionElement::UniversalRegion(fr) => { + RegionElement::RootUniversalRegion(fr) => { if let Some((location1, location2)) = open_location { push_sep(&mut result); Self::push_location_range(&mut result, location1, location2); @@ -341,3 +329,55 @@ impl RegionValues { } } } + +crate trait ToElementIndex: Debug + Copy { + fn add_to_row( + self, + values: &mut RegionValues, + row: N, + ) -> bool; + + fn contained_in_row( + self, + values: &RegionValues, + row: N, + ) -> bool; +} + +impl ToElementIndex for Location { + fn add_to_row( + self, + values: &mut RegionValues, + row: N, + ) -> bool { + let index = values.elements.point_from_location(self); + values.points.add(row, index) + } + + fn contained_in_row( + self, + values: &RegionValues, + row: N, + ) -> bool { + let index = values.elements.point_from_location(self); + values.points.contains(row, index) + } +} + +impl ToElementIndex for RegionVid { + fn add_to_row( + self, + values: &mut RegionValues, + row: N, + ) -> bool { + values.free_regions.add(row, self) + } + + fn contained_in_row( + self, + values: &RegionValues, + row: N, + ) -> bool { + values.free_regions.contains(row, self) + } +} diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 6483b9b3c68..382248c2d15 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -27,6 +27,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(core_intrinsics)] #![feature(decl_macro)] #![feature(fs_read_write)] +#![feature(in_band_lifetimes)] #![feature(macro_vis_matcher)] #![feature(exhaustive_patterns)] #![feature(range_contains)]