refactor region value bitmatrix

This commit is contained in:
Niko Matsakis 2017-12-01 11:07:53 -05:00
parent a30e2259da
commit 77663a677d
4 changed files with 270 additions and 110 deletions

View File

@ -70,7 +70,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
with_msg: &mut FnMut(&str) -> io::Result<()>,
) -> io::Result<()> {
for region in self.definitions.indices() {
let value = self.region_value_str_from_matrix(&self.liveness_constraints, region);
let value = self.liveness_constraints.region_value_str(region);
if value != "{}" {
with_msg(&format!("{:?} live at {}", region, value))?;
}

View File

@ -19,15 +19,15 @@ use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location
use rustc::ty::{self, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::bitvec::BitMatrix;
use rustc_data_structures::indexed_vec::Idx;
use std::collections::BTreeMap;
use std::fmt;
use std::rc::Rc;
use syntax_pos::Span;
mod annotation;
mod dump_mir;
mod graphviz;
mod values;
use self::values::{RegionValueElements, RegionValues};
pub struct RegionInferenceContext<'tcx> {
/// Contains the definition for every region variable. Region
@ -36,27 +36,22 @@ pub struct RegionInferenceContext<'tcx> {
/// from as well as its final inferred value.
definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
/// Maps from points/universal-regions to a `RegionElementIndex`.
elements: Rc<RegionValueElements>,
/// 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
/// the entire CFG and `end(R)`.
///
/// In this `BitMatrix` representation, the rows are the region
/// variables and the columns are the free regions and MIR locations.
liveness_constraints: BitMatrix,
liveness_constraints: RegionValues,
/// The final inferred values of the inference variables; `None`
/// until `solve` is invoked.
inferred_values: Option<BitMatrix>,
inferred_values: Option<RegionValues>,
/// The constraints we have accumulated and used during solving.
constraints: Vec<Constraint>,
/// A map from each MIR Location to its column index in
/// `liveness_constraints`/`inferred_values`. (The first N columns are
/// the free regions.)
point_indices: BTreeMap<Location, usize>,
/// Information about the universally quantified regions in scope
/// on this function and their (known) relations to one another.
universal_regions: UniversalRegions<'tcx>,
@ -112,19 +107,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let num_region_variables = var_origins.len();
let num_universal_regions = universal_regions.len();
let mut num_points = 0;
let mut point_indices = BTreeMap::new();
let mut points = Vec::new();
for (block, block_data) in mir.basic_blocks().iter_enumerated() {
for statement_index in 0..block_data.statements.len() + 1 {
let location = Location {
points.push(Location {
block,
statement_index,
};
point_indices.insert(location, num_universal_regions + num_points);
num_points += 1;
});
}
}
let elements = &Rc::new(RegionValueElements::new(points, num_universal_regions));
// Create a RegionDefinition for each inference variable.
let definitions = var_origins
@ -134,13 +126,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let mut result = Self {
definitions,
liveness_constraints: BitMatrix::new(
num_region_variables,
num_universal_regions + num_points,
),
elements: elements.clone(),
liveness_constraints: RegionValues::new(elements, num_region_variables),
inferred_values: None,
constraints: Vec::new(),
point_indices,
universal_regions,
};
@ -186,14 +175,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.definitions[variable].is_universal = true;
// Add all nodes in the CFG to liveness constraints
for (_location, point_index) in &self.point_indices {
self.liveness_constraints
.add(variable.index(), *point_index);
for point_index in self.elements.all_point_indices() {
self.liveness_constraints.add(variable, point_index);
}
// Add `end(X)` into the set for X.
self.liveness_constraints
.add(variable.index(), variable.index());
self.liveness_constraints.add(variable, variable);
}
}
@ -217,32 +204,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let inferred_values = self.inferred_values
.as_ref()
.expect("region values not yet inferred");
self.region_contains_point_in_matrix(inferred_values, r, p)
}
/// True if given region `r` contains the point `p`, when
/// evaluated in the set of region values `matrix`.
fn region_contains_point_in_matrix(
&self,
matrix: &BitMatrix,
r: RegionVid,
p: Location,
) -> bool {
let point_index = self.point_indices
.get(&p)
.expect("point index should be known");
matrix.contains(r.index(), *point_index)
}
/// True if given region `r` contains the `end(s)`, when
/// evaluated in the set of region values `matrix`.
fn region_contains_region_in_matrix(
&self,
matrix: &BitMatrix,
r: RegionVid,
s: RegionVid,
) -> bool {
matrix.contains(r.index(), s.index())
inferred_values.contains(r, p)
}
/// Returns access to the value of `r` for debugging purposes.
@ -251,43 +213,21 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.as_ref()
.expect("region values not yet inferred");
self.region_value_str_from_matrix(inferred_values, r)
}
fn region_value_str_from_matrix(&self,
matrix: &BitMatrix,
r: RegionVid) -> String {
let mut result = String::new();
result.push_str("{");
let mut sep = "";
for &point in self.point_indices.keys() {
if self.region_contains_point_in_matrix(matrix, r, point) {
result.push_str(&format!("{}{:?}", sep, point));
sep = ", ";
}
}
for fr in (0..self.universal_regions.len()).map(RegionVid::new) {
if self.region_contains_region_in_matrix(matrix, r, fr) {
result.push_str(&format!("{}{:?}", sep, fr));
sep = ", ";
}
}
result.push_str("}");
result
inferred_values.region_value_str(r)
}
/// Indicates that the region variable `v` is live at the point `point`.
pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) -> bool {
debug!("add_live_point({:?}, {:?})", v, point);
assert!(self.inferred_values.is_none(), "values already inferred");
let point_index = self.point_indices
.get(&point)
.expect("point index should be known");
self.liveness_constraints.add(v.index(), *point_index)
debug!("add_live_point: @{:?}", point);
let element = self.elements.index(point);
if self.liveness_constraints.add(v, element) {
true
} else {
false
}
}
/// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
@ -386,16 +326,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
outlives_requirements: &mut Vec<ClosureOutlivesRequirement>,
) {
let inferred_values = self.inferred_values.as_ref().unwrap();
let longer_value = inferred_values.iter(longer_fr.index());
debug!("check_universal_region(fr={:?})", longer_fr);
// Find every region `o` such that `fr: o`
// (because `fr` includes `end(o)`).
let shorter_frs = longer_value
.take_while(|&i| i < self.universal_regions.len())
.map(RegionVid::new);
for shorter_fr in shorter_frs {
for shorter_fr in inferred_values.universal_regions_outlived_by(longer_fr) {
// If it is known that `fr: o`, carry on.
if self.universal_regions.outlives(longer_fr, shorter_fr) {
continue;
@ -512,20 +448,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
fn copy(
&self,
inferred_values: &mut BitMatrix,
inferred_values: &mut RegionValues,
mir: &Mir<'tcx>,
from_region: RegionVid,
to_region: RegionVid,
start_point: Location,
constraint_point: Location,
) -> bool {
let mut changed = false;
let mut stack = vec![];
let mut visited = FxHashSet();
stack.push(start_point);
stack.push(constraint_point);
while let Some(p) = stack.pop() {
if !self.region_contains_point_in_matrix(inferred_values, from_region, p) {
let point_index = self.elements.index(p);
if !inferred_values.contains(from_region, point_index) {
debug!(" not in from-region");
continue;
}
@ -535,8 +473,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
continue;
}
let point_index = self.point_indices.get(&p).unwrap();
changed |= inferred_values.add(to_region.index(), *point_index);
let new = inferred_values.add(to_region, point_index);
changed |= new;
let block_data = &mir[p.block];
let successor_points = if p.statement_index < block_data.statements.len() {
@ -564,13 +502,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// If we reach the END point in the graph, then copy
// over any skolemized end points in the `from_region`
// and make sure they are included in the `to_region`.
let universal_region_indices = inferred_values
.iter(from_region.index())
.take_while(|&i| i < self.universal_regions.len())
.collect::<Vec<_>>();
for fr in &universal_region_indices {
changed |= inferred_values.add(to_region.index(), *fr);
}
changed |=
inferred_values.add_universal_regions_outlived_by(from_region, to_region);
} else {
stack.extend(successor_points);
}

View File

@ -0,0 +1,227 @@
use std::rc::Rc;
use rustc_data_structures::bitvec::BitMatrix;
use rustc_data_structures::indexed_vec::Idx;
use rustc::mir::Location;
use rustc::ty::RegionVid;
/// Maps between the various kinds of elements of a region value to
/// the internal indices that w use.
pub(super) struct RegionValueElements {
points: Vec<Location>,
num_universal_regions: usize,
}
impl RegionValueElements {
pub(super) fn new(points: Vec<Location>, num_universal_regions: usize) -> Self {
Self {
points,
num_universal_regions,
}
}
/// Converts an element of a region value into a `RegionElementIndex`.
pub(super) fn index<T: ToElementIndex>(&self, elem: T) -> RegionElementIndex {
elem.to_element_index(self)
}
/// Iterates over the `RegionElementIndex` for all points in the CFG.
pub(super) fn all_point_indices<'a>(&'a self) -> impl Iterator<Item = RegionElementIndex> + 'a {
(0..self.points.len()).map(move |i| RegionElementIndex::new(i + self.num_universal_regions))
}
/// Iterates over the `RegionElementIndex` for all points in the CFG.
pub(super) fn all_universal_region_indices(&self) -> impl Iterator<Item = RegionElementIndex> {
(0..self.num_universal_regions).map(move |i| RegionElementIndex::new(i))
}
/// Converts a particular `RegionElementIndex` to the `RegionElement` it represents.
pub(super) fn to_element(&self, i: RegionElementIndex) -> RegionElement {
if let Some(r) = self.to_universal_region(i) {
RegionElement::UniversalRegion(r)
} else {
RegionElement::Location(self.points[i.index() - self.num_universal_regions])
}
}
/// Converts a particular `RegionElementIndex` to a universal
/// region, if that is what it represents. Returns `None`
/// otherwise.
pub(super) fn to_universal_region(&self, i: RegionElementIndex) -> Option<RegionVid> {
if i.index() < self.num_universal_regions {
Some(RegionVid::new(i.index()))
} else {
None
}
}
}
/// A newtype for the integers that represent one of the possible
/// elements in a region. These are the rows in the `BitMatrix` 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({})" });
/// An individual element in a region value -- the value of a
/// particular region variable consists of a set of these elements.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(super) enum RegionElement {
/// A point in the control-flow graph.
Location(Location),
/// An in-scope, universally quantified region (e.g., a liftime parameter).
UniversalRegion(RegionVid),
}
pub(super) trait ToElementIndex {
fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
}
impl ToElementIndex for Location {
fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
let index = elements.points.binary_search(&self).unwrap();
RegionElementIndex::new(index + elements.num_universal_regions)
}
}
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
}
}
/// Stores the values for a set of regions. These are stored in a
/// compact `BitMatrix` representation, with one row per region
/// variable. The columns consist of either universal regions or
/// points in the CFG.
#[derive(Clone)]
pub(super) struct RegionValues {
elements: Rc<RegionValueElements>,
matrix: BitMatrix,
}
impl RegionValues {
pub(super) fn new(elements: &Rc<RegionValueElements>, num_region_variables: usize) -> Self {
assert!(
elements.num_universal_regions <= num_region_variables,
"universal regions are a subset of the region variables"
);
let num_columns = elements.points.len() + elements.num_universal_regions;
Self {
elements: elements.clone(),
matrix: BitMatrix::new(num_region_variables, num_columns),
}
}
/// Adds the given element to the value for the given region. Returns true if
/// the element is newly added (i.e., was not already present).
pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E) -> bool {
let i = self.elements.index(elem);
if self.matrix.add(r.index(), i.index()) {
debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
true
} else {
false
}
}
/// Adds all the universal regions outlived by `from_region` to
/// `to_region`.
pub(super) fn add_universal_regions_outlived_by(
&mut self,
from_region: RegionVid,
to_region: RegionVid,
) -> bool {
// FIXME. We could optimize this by improving
// `BitMatrix::merge` so it does not always merge an entire
// row.
debug!("add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
from_region, to_region);
let mut changed = false;
for elem in self.elements.all_universal_region_indices() {
if self.contains(from_region, elem) {
changed |= self.add(to_region, elem);
}
}
changed
}
/// True if the region `r` contains the given element.
pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
let i = self.elements.index(elem);
self.matrix.contains(r.index(), i.index())
}
/// Iterate over the value of the region `r`, yielding up element
/// indices. You may prefer `universal_regions_outlived_by` or
/// `elements_contained_in`.
pub(super) fn element_indices_contained_in<'a>(
&'a self,
r: RegionVid,
) -> impl Iterator<Item = RegionElementIndex> + 'a {
self.matrix
.iter(r.index())
.map(move |i| RegionElementIndex::new(i))
}
/// Returns just the universal regions that are contained in a given region's value.
pub(super) fn universal_regions_outlived_by<'a>(
&'a self,
r: RegionVid,
) -> impl Iterator<Item = RegionVid> + '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())
}
/// Returns all the elements contained in a given region's value.
pub(super) fn elements_contained_in<'a>(
&'a self,
r: RegionVid,
) -> impl Iterator<Item = RegionElement> + 'a {
self.element_indices_contained_in(r)
.map(move |r| self.elements.to_element(r))
}
/// Returns a "pretty" string value of the region. Meant for debugging.
pub(super) fn region_value_str(&self, r: RegionVid) -> String {
let mut result = String::new();
result.push_str("{");
for (index, element) in self.elements_contained_in(r).enumerate() {
if index > 0 {
result.push_str(", ");
}
match element {
RegionElement::Location(l) => {
result.push_str(&format!("{:?}", l));
}
RegionElement::UniversalRegion(fr) => {
result.push_str(&format!("{:?}", fr));
}
}
}
result.push_str("}");
result
}
}

View File

@ -33,10 +33,10 @@ fn main() {
// | '_#3r | Local | ['_#3r]
// |
// | Inferred Region Values
// | '_#0r | {bb0[0], bb0[1], '_#0r}
// | '_#1r | {bb0[0], bb0[1], '_#1r}
// | '_#2r | {bb0[0], bb0[1], '_#2r}
// | '_#3r | {bb0[0], bb0[1], '_#3r}
// | '_#0r | {'_#0r, bb0[0], bb0[1]}
// | '_#1r | {'_#1r, bb0[0], bb0[1]}
// | '_#2r | {'_#2r, bb0[0], bb0[1]}
// | '_#3r | {'_#3r, bb0[0], bb0[1]}
// |
// ...
// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool {