1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
3 use rustc_data_structures::fx::FxIndexSet;
4 use rustc_index::bit_set::SparseBitMatrix;
5 use rustc_index::interval::IntervalSet;
6 use rustc_index::interval::SparseIntervalMatrix;
7 use rustc_index::Idx;
8 use rustc_index::IndexVec;
9 use rustc_middle::mir::{BasicBlock, Body, Location};
10 use rustc_middle::ty::{self, RegionVid};
11 use std::fmt::Debug;
12 use std::rc::Rc;
13
14 /// Maps between a `Location` and a `PointIndex` (and vice versa).
15 pub(crate) struct RegionValueElements {
16 /// For each basic block, how many points are contained within?
17 statements_before_block: IndexVec<BasicBlock, usize>,
18
19 /// Map backward from each point to the basic block that it
20 /// belongs to.
21 basic_blocks: IndexVec<PointIndex, BasicBlock>,
22
23 num_points: usize,
24 }
25
26 impl RegionValueElements {
new(body: &Body<'_>) -> Self27 pub(crate) fn new(body: &Body<'_>) -> Self {
28 let mut num_points = 0;
29 let statements_before_block: IndexVec<BasicBlock, usize> = body
30 .basic_blocks
31 .iter()
32 .map(|block_data| {
33 let v = num_points;
34 num_points += block_data.statements.len() + 1;
35 v
36 })
37 .collect();
38 debug!("RegionValueElements: statements_before_block={:#?}", statements_before_block);
39 debug!("RegionValueElements: num_points={:#?}", num_points);
40
41 let mut basic_blocks = IndexVec::with_capacity(num_points);
42 for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
43 basic_blocks.extend((0..=bb_data.statements.len()).map(|_| bb));
44 }
45
46 Self { statements_before_block, basic_blocks, num_points }
47 }
48
49 /// Total number of point indices
num_points(&self) -> usize50 pub(crate) fn num_points(&self) -> usize {
51 self.num_points
52 }
53
54 /// Converts a `Location` into a `PointIndex`. O(1).
point_from_location(&self, location: Location) -> PointIndex55 pub(crate) fn point_from_location(&self, location: Location) -> PointIndex {
56 let Location { block, statement_index } = location;
57 let start_index = self.statements_before_block[block];
58 PointIndex::new(start_index + statement_index)
59 }
60
61 /// Converts a `Location` into a `PointIndex`. O(1).
entry_point(&self, block: BasicBlock) -> PointIndex62 pub(crate) fn entry_point(&self, block: BasicBlock) -> PointIndex {
63 let start_index = self.statements_before_block[block];
64 PointIndex::new(start_index)
65 }
66
67 /// Return the PointIndex for the block start of this index.
to_block_start(&self, index: PointIndex) -> PointIndex68 pub(crate) fn to_block_start(&self, index: PointIndex) -> PointIndex {
69 PointIndex::new(self.statements_before_block[self.basic_blocks[index]])
70 }
71
72 /// Converts a `PointIndex` back to a location. O(1).
to_location(&self, index: PointIndex) -> Location73 pub(crate) fn to_location(&self, index: PointIndex) -> Location {
74 assert!(index.index() < self.num_points);
75 let block = self.basic_blocks[index];
76 let start_index = self.statements_before_block[block];
77 let statement_index = index.index() - start_index;
78 Location { block, statement_index }
79 }
80
81 /// Sometimes we get point-indices back from bitsets that may be
82 /// out of range (because they round up to the nearest 2^N number
83 /// of bits). Use this function to filter such points out if you
84 /// like.
point_in_range(&self, index: PointIndex) -> bool85 pub(crate) fn point_in_range(&self, index: PointIndex) -> bool {
86 index.index() < self.num_points
87 }
88 }
89
90 rustc_index::newtype_index! {
91 /// A single integer representing a `Location` in the MIR control-flow
92 /// graph. Constructed efficiently from `RegionValueElements`.
93 #[debug_format = "PointIndex({})"]
94 pub struct PointIndex {}
95 }
96
97 rustc_index::newtype_index! {
98 /// A single integer representing a `ty::Placeholder`.
99 #[debug_format = "PlaceholderIndex({})"]
100 pub struct PlaceholderIndex {}
101 }
102
103 /// An individual element in a region value -- the value of a
104 /// particular region variable consists of a set of these elements.
105 #[derive(Debug, Clone)]
106 pub(crate) enum RegionElement {
107 /// A point in the control-flow graph.
108 Location(Location),
109
110 /// A universally quantified region from the root universe (e.g.,
111 /// a lifetime parameter).
112 RootUniversalRegion(RegionVid),
113
114 /// A placeholder (e.g., instantiated from a `for<'a> fn(&'a u32)`
115 /// type).
116 PlaceholderRegion(ty::PlaceholderRegion),
117 }
118
119 /// When we initially compute liveness, we use an interval matrix storing
120 /// liveness ranges for each region-vid.
121 pub(crate) struct LivenessValues<N: Idx> {
122 elements: Rc<RegionValueElements>,
123 points: SparseIntervalMatrix<N, PointIndex>,
124 }
125
126 impl<N: Idx> LivenessValues<N> {
127 /// Creates a new set of "region values" that tracks causal information.
128 /// Each of the regions in num_region_variables will be initialized with an
129 /// empty set of points and no causal information.
new(elements: Rc<RegionValueElements>) -> Self130 pub(crate) fn new(elements: Rc<RegionValueElements>) -> Self {
131 Self { points: SparseIntervalMatrix::new(elements.num_points), elements }
132 }
133
134 /// Iterate through each region that has a value in this set.
rows(&self) -> impl Iterator<Item = N>135 pub(crate) fn rows(&self) -> impl Iterator<Item = N> {
136 self.points.rows()
137 }
138
139 /// Adds the given element to the value for the given region. Returns whether
140 /// the element is newly added (i.e., was not already present).
add_element(&mut self, row: N, location: Location) -> bool141 pub(crate) fn add_element(&mut self, row: N, location: Location) -> bool {
142 debug!("LivenessValues::add(r={:?}, location={:?})", row, location);
143 let index = self.elements.point_from_location(location);
144 self.points.insert(row, index)
145 }
146
147 /// Adds all the elements in the given bit array into the given
148 /// region. Returns whether any of them are newly added.
add_elements(&mut self, row: N, locations: &IntervalSet<PointIndex>) -> bool149 pub(crate) fn add_elements(&mut self, row: N, locations: &IntervalSet<PointIndex>) -> bool {
150 debug!("LivenessValues::add_elements(row={:?}, locations={:?})", row, locations);
151 self.points.union_row(row, locations)
152 }
153
154 /// Adds all the control-flow points to the values for `r`.
add_all_points(&mut self, row: N)155 pub(crate) fn add_all_points(&mut self, row: N) {
156 self.points.insert_all_into_row(row);
157 }
158
159 /// Returns `true` if the region `r` contains the given element.
contains(&self, row: N, location: Location) -> bool160 pub(crate) fn contains(&self, row: N, location: Location) -> bool {
161 let index = self.elements.point_from_location(location);
162 self.points.row(row).is_some_and(|r| r.contains(index))
163 }
164
165 /// Returns an iterator of all the elements contained by the region `r`
get_elements(&self, row: N) -> impl Iterator<Item = Location> + '_166 pub(crate) fn get_elements(&self, row: N) -> impl Iterator<Item = Location> + '_ {
167 self.points
168 .row(row)
169 .into_iter()
170 .flat_map(|set| set.iter())
171 .take_while(move |&p| self.elements.point_in_range(p))
172 .map(move |p| self.elements.to_location(p))
173 }
174
175 /// Returns a "pretty" string value of the region. Meant for debugging.
region_value_str(&self, r: N) -> String176 pub(crate) fn region_value_str(&self, r: N) -> String {
177 region_value_str(self.get_elements(r).map(RegionElement::Location))
178 }
179 }
180
181 /// Maps from `ty::PlaceholderRegion` values that are used in the rest of
182 /// rustc to the internal `PlaceholderIndex` values that are used in
183 /// NLL.
184 #[derive(Debug, Default)]
185 pub(crate) struct PlaceholderIndices {
186 indices: FxIndexSet<ty::PlaceholderRegion>,
187 }
188
189 impl PlaceholderIndices {
190 /// Returns the `PlaceholderIndex` for the inserted `PlaceholderRegion`
insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex191 pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
192 let (index, _) = self.indices.insert_full(placeholder);
193 index.into()
194 }
195
lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex196 pub(crate) fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
197 self.indices.get_index_of(&placeholder).unwrap().into()
198 }
199
lookup_placeholder( &self, placeholder: PlaceholderIndex, ) -> ty::PlaceholderRegion200 pub(crate) fn lookup_placeholder(
201 &self,
202 placeholder: PlaceholderIndex,
203 ) -> ty::PlaceholderRegion {
204 self.indices[placeholder.index()]
205 }
206
len(&self) -> usize207 pub(crate) fn len(&self) -> usize {
208 self.indices.len()
209 }
210 }
211
212 /// Stores the full values for a set of regions (in contrast to
213 /// `LivenessValues`, which only stores those points in the where a
214 /// region is live). The full value for a region may contain points in
215 /// the CFG, but also free regions as well as bound universe
216 /// placeholders.
217 ///
218 /// Example:
219 ///
220 /// ```text
221 /// fn foo(x: &'a u32) -> &'a u32 {
222 /// let y: &'0 u32 = x; // let's call this `'0`
223 /// y
224 /// }
225 /// ```
226 ///
227 /// Here, the variable `'0` would contain the free region `'a`,
228 /// because (since it is returned) it must live for at least `'a`. But
229 /// it would also contain various points from within the function.
230 #[derive(Clone)]
231 pub(crate) struct RegionValues<N: Idx> {
232 elements: Rc<RegionValueElements>,
233 placeholder_indices: Rc<PlaceholderIndices>,
234 points: SparseIntervalMatrix<N, PointIndex>,
235 free_regions: SparseBitMatrix<N, RegionVid>,
236
237 /// Placeholders represent bound regions -- so something like `'a`
238 /// in `for<'a> fn(&'a u32)`.
239 placeholders: SparseBitMatrix<N, PlaceholderIndex>,
240 }
241
242 impl<N: Idx> RegionValues<N> {
243 /// Creates a new set of "region values" that tracks causal information.
244 /// Each of the regions in num_region_variables will be initialized with an
245 /// empty set of points and no causal information.
new( elements: &Rc<RegionValueElements>, num_universal_regions: usize, placeholder_indices: &Rc<PlaceholderIndices>, ) -> Self246 pub(crate) fn new(
247 elements: &Rc<RegionValueElements>,
248 num_universal_regions: usize,
249 placeholder_indices: &Rc<PlaceholderIndices>,
250 ) -> Self {
251 let num_placeholders = placeholder_indices.len();
252 Self {
253 elements: elements.clone(),
254 points: SparseIntervalMatrix::new(elements.num_points),
255 placeholder_indices: placeholder_indices.clone(),
256 free_regions: SparseBitMatrix::new(num_universal_regions),
257 placeholders: SparseBitMatrix::new(num_placeholders),
258 }
259 }
260
261 /// Adds the given element to the value for the given region. Returns whether
262 /// the element is newly added (i.e., was not already present).
add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool263 pub(crate) fn add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool {
264 debug!("add(r={:?}, elem={:?})", r, elem);
265 elem.add_to_row(self, r)
266 }
267
268 /// Adds all the control-flow points to the values for `r`.
add_all_points(&mut self, r: N)269 pub(crate) fn add_all_points(&mut self, r: N) {
270 self.points.insert_all_into_row(r);
271 }
272
273 /// Adds all elements in `r_from` to `r_to` (because e.g., `r_to:
274 /// r_from`).
add_region(&mut self, r_to: N, r_from: N) -> bool275 pub(crate) fn add_region(&mut self, r_to: N, r_from: N) -> bool {
276 self.points.union_rows(r_from, r_to)
277 | self.free_regions.union_rows(r_from, r_to)
278 | self.placeholders.union_rows(r_from, r_to)
279 }
280
281 /// Returns `true` if the region `r` contains the given element.
contains(&self, r: N, elem: impl ToElementIndex) -> bool282 pub(crate) fn contains(&self, r: N, elem: impl ToElementIndex) -> bool {
283 elem.contained_in_row(self, r)
284 }
285
286 /// Returns the lowest statement index in `start..=end` which is not contained by `r`.
first_non_contained_inclusive( &self, r: N, block: BasicBlock, start: usize, end: usize, ) -> Option<usize>287 pub(crate) fn first_non_contained_inclusive(
288 &self,
289 r: N,
290 block: BasicBlock,
291 start: usize,
292 end: usize,
293 ) -> Option<usize> {
294 let row = self.points.row(r)?;
295 let block = self.elements.entry_point(block);
296 let start = block.plus(start);
297 let end = block.plus(end);
298 let first_unset = row.first_unset_in(start..=end)?;
299 Some(first_unset.index() - block.index())
300 }
301
302 /// `self[to] |= values[from]`, essentially: that is, take all the
303 /// elements for the region `from` from `values` and add them to
304 /// the region `to` in `self`.
merge_liveness<M: Idx>(&mut self, to: N, from: M, values: &LivenessValues<M>)305 pub(crate) fn merge_liveness<M: Idx>(&mut self, to: N, from: M, values: &LivenessValues<M>) {
306 if let Some(set) = values.points.row(from) {
307 self.points.union_row(to, set);
308 }
309 }
310
311 /// Returns `true` if `sup_region` contains all the CFG points that
312 /// `sub_region` contains. Ignores universal regions.
contains_points(&self, sup_region: N, sub_region: N) -> bool313 pub(crate) fn contains_points(&self, sup_region: N, sub_region: N) -> bool {
314 if let Some(sub_row) = self.points.row(sub_region) {
315 if let Some(sup_row) = self.points.row(sup_region) {
316 sup_row.superset(sub_row)
317 } else {
318 // sup row is empty, so sub row must be empty
319 sub_row.is_empty()
320 }
321 } else {
322 // sub row is empty, always true
323 true
324 }
325 }
326
327 /// Returns the locations contained within a given region `r`.
locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a328 pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a {
329 self.points.row(r).into_iter().flat_map(move |set| {
330 set.iter()
331 .take_while(move |&p| self.elements.point_in_range(p))
332 .map(move |p| self.elements.to_location(p))
333 })
334 }
335
336 /// Returns just the universal regions that are contained in a given region's value.
universal_regions_outlived_by<'a>( &'a self, r: N, ) -> impl Iterator<Item = RegionVid> + 'a337 pub(crate) fn universal_regions_outlived_by<'a>(
338 &'a self,
339 r: N,
340 ) -> impl Iterator<Item = RegionVid> + 'a {
341 self.free_regions.row(r).into_iter().flat_map(|set| set.iter())
342 }
343
344 /// Returns all the elements contained in a given region's value.
placeholders_contained_in<'a>( &'a self, r: N, ) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a345 pub(crate) fn placeholders_contained_in<'a>(
346 &'a self,
347 r: N,
348 ) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
349 self.placeholders
350 .row(r)
351 .into_iter()
352 .flat_map(|set| set.iter())
353 .map(move |p| self.placeholder_indices.lookup_placeholder(p))
354 }
355
356 /// Returns all the elements contained in a given region's value.
elements_contained_in<'a>( &'a self, r: N, ) -> impl Iterator<Item = RegionElement> + 'a357 pub(crate) fn elements_contained_in<'a>(
358 &'a self,
359 r: N,
360 ) -> impl Iterator<Item = RegionElement> + 'a {
361 let points_iter = self.locations_outlived_by(r).map(RegionElement::Location);
362
363 let free_regions_iter =
364 self.universal_regions_outlived_by(r).map(RegionElement::RootUniversalRegion);
365
366 let placeholder_universes_iter =
367 self.placeholders_contained_in(r).map(RegionElement::PlaceholderRegion);
368
369 points_iter.chain(free_regions_iter).chain(placeholder_universes_iter)
370 }
371
372 /// Returns a "pretty" string value of the region. Meant for debugging.
region_value_str(&self, r: N) -> String373 pub(crate) fn region_value_str(&self, r: N) -> String {
374 region_value_str(self.elements_contained_in(r))
375 }
376 }
377
378 pub(crate) trait ToElementIndex: Debug + Copy {
add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool379 fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool;
380
contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool381 fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool;
382 }
383
384 impl ToElementIndex for Location {
add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool385 fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
386 let index = values.elements.point_from_location(self);
387 values.points.insert(row, index)
388 }
389
contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool390 fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
391 let index = values.elements.point_from_location(self);
392 values.points.contains(row, index)
393 }
394 }
395
396 impl ToElementIndex for RegionVid {
add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool397 fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
398 values.free_regions.insert(row, self)
399 }
400
contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool401 fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
402 values.free_regions.contains(row, self)
403 }
404 }
405
406 impl ToElementIndex for ty::PlaceholderRegion {
add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool407 fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
408 let index = values.placeholder_indices.lookup_index(self);
409 values.placeholders.insert(row, index)
410 }
411
contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool412 fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
413 let index = values.placeholder_indices.lookup_index(self);
414 values.placeholders.contains(row, index)
415 }
416 }
417
location_set_str( elements: &RegionValueElements, points: impl IntoIterator<Item = PointIndex>, ) -> String418 pub(crate) fn location_set_str(
419 elements: &RegionValueElements,
420 points: impl IntoIterator<Item = PointIndex>,
421 ) -> String {
422 region_value_str(
423 points
424 .into_iter()
425 .take_while(|&p| elements.point_in_range(p))
426 .map(|p| elements.to_location(p))
427 .map(RegionElement::Location),
428 )
429 }
430
region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String431 fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String {
432 let mut result = String::new();
433 result.push('{');
434
435 // Set to Some(l1, l2) when we have observed all the locations
436 // from l1..=l2 (inclusive) but not yet printed them. This
437 // gets extended if we then see l3 where l3 is the successor
438 // to l2.
439 let mut open_location: Option<(Location, Location)> = None;
440
441 let mut sep = "";
442 let mut push_sep = |s: &mut String| {
443 s.push_str(sep);
444 sep = ", ";
445 };
446
447 for element in elements {
448 match element {
449 RegionElement::Location(l) => {
450 if let Some((location1, location2)) = open_location {
451 if location2.block == l.block
452 && location2.statement_index == l.statement_index - 1
453 {
454 open_location = Some((location1, l));
455 continue;
456 }
457
458 push_sep(&mut result);
459 push_location_range(&mut result, location1, location2);
460 }
461
462 open_location = Some((l, l));
463 }
464
465 RegionElement::RootUniversalRegion(fr) => {
466 if let Some((location1, location2)) = open_location {
467 push_sep(&mut result);
468 push_location_range(&mut result, location1, location2);
469 open_location = None;
470 }
471
472 push_sep(&mut result);
473 result.push_str(&format!("{:?}", fr));
474 }
475
476 RegionElement::PlaceholderRegion(placeholder) => {
477 if let Some((location1, location2)) = open_location {
478 push_sep(&mut result);
479 push_location_range(&mut result, location1, location2);
480 open_location = None;
481 }
482
483 push_sep(&mut result);
484 result.push_str(&format!("{:?}", placeholder));
485 }
486 }
487 }
488
489 if let Some((location1, location2)) = open_location {
490 push_sep(&mut result);
491 push_location_range(&mut result, location1, location2);
492 }
493
494 result.push('}');
495
496 return result;
497
498 fn push_location_range(str: &mut String, location1: Location, location2: Location) {
499 if location1 == location2 {
500 str.push_str(&format!("{:?}", location1));
501 } else {
502 assert_eq!(location1.block, location2.block);
503 str.push_str(&format!(
504 "{:?}[{}..={}]",
505 location1.block, location1.statement_index, location2.statement_index
506 ));
507 }
508 }
509 }
510