Skip to content

Commit 1fedadd

Browse files
committed
add doc-comment for module
1 parent fc87640 commit 1fedadd

File tree

1 file changed

+127
-12
lines changed

1 file changed

+127
-12
lines changed

compiler/rustc_mir_dataflow/src/impls/live_borrows.rs

Lines changed: 127 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,122 @@
1+
//! This module defines a more fine-grained analysis for `Local`s that are live due
2+
//! to outstanding references or raw pointers than `MaybeBorrowedLocals`.
3+
//!
4+
//! The analysis consists of three steps:
5+
//! 1. build a dependency graph that relates `Local`s based on their borrowing relationship.
6+
//! As an example if we have something like this (in a simplified MIR representation):
7+
//!
8+
//! ```ignore(rust)
9+
//! _4 = Bar {}
10+
//! _5 = Ref(_4)
11+
//! ```
12+
//!
13+
//! Then we add an edge from `_5` to `_4`.
14+
//! 2. perform a liveness analysis for borrowed `Local`s.
15+
//! Continuing our example from step 1, if we later have a use of `_5`, `_5` is
16+
//! live at least from its definition to that use of it.
17+
//! 3. Combine the two analyses from step 1 and 2. For any `Local` that corresponds
18+
//! to a borrow (`_5` in our example), we want to keep the `Local` (`_4`), which is actually
19+
//! borrowed through it, live over the range at which the borrow is live. Hence for any point
20+
//! in that range we traverse our dependency graph and look for leaf nodes. In our example
21+
//! we would find an edge from `_5` to `_4`, which is a leaf node and hence we keep `_4` live
22+
//! over that range.
23+
//!
24+
//! There are some corner cases we need to look out for to make this analysis sound. Let's look
25+
//! at each of the three steps in more detail and elaborate how these steps deal with these corner
26+
//! cases.
27+
//!
28+
//! 1. Dependency Graph
29+
//!
30+
//! The `Node`s in the dependency graph include data values of type `NodeKind`. `NodeKind` has
31+
//! three variants: `Local`, `Borrow` and `LocalWithRefs`.
32+
//! * `NodeKind::Local` is used for `Local`s that are borrowed somewhere (`_4` in our example)
33+
//! * `NodeKind::Borrow` is used for `Local`s that correspond to borrows (`_5` in our example) and
34+
//! also `Local`s that result from re-borrows.
35+
//! * `NodeKind::LocalWithRefs` is used for `Local`s that aren't themselves borrows, but contain
36+
//! borrowed `Local`s. We want to keep these `Local`s live and also any of the references/pointers
37+
//! they might contain. Let's look at an example:
38+
//!
39+
//! ```ignore(rust)
40+
//! _4 = Bar {}
41+
//! _5 = Ref(_4)
42+
//! _6 = Aggregate(..)(move _5)
43+
//! ...
44+
//! _7 = (_6.0)
45+
//! ```
46+
//!
47+
//! In this example `_6` would be given `NodeKind::LocalWithRefs` and our graph would look
48+
//! as follows:
49+
//!
50+
//! `_7 (NodeKind::Borrow) -> `_6` (NodeKind::LocalWithRefs) -> `_5` (NodeKind::Borrow) -> `_4` (NodeKind::Local)
51+
//!
52+
//! In addition to keeping `_6` alive over the range of `_7` we also keep `_4` alive (leaf node).
53+
//!
54+
//! Additionally `NodeKind::LocalWithRefs` is also used for raw pointers that are cast to
55+
//! `usize`:
56+
//!
57+
//! ```ignore(rust)
58+
//! _4 = Bar {}
59+
//! _5 = AddressOf(_4)
60+
//! _6 = _5 as usize
61+
//! _7 = Aggregate(..) (move _6)
62+
//! _8 = (_7.0)
63+
//! ```
64+
//!
65+
//! In this example our graph would have the following edges:
66+
//! * `_5` (Borrow) -> `_4` (Local)
67+
//! * `_6` (LocalWithRefs) -> `_5` (Borrow)
68+
//! * `_7` (LocalWithRefs) -> `_6` (LocalWithRefs)
69+
//! * `_8` (LocalWithRefs) -> `_7` (LocalWithRefs) (FIXME this one is currently not being done)
70+
//!
71+
//! We also have to be careful when dealing with `Terminator`s. Whenever we pass references,
72+
//! pointers or `Local`s with `NodeKind::LocalWithRefs` (FIXME currently not done) to
73+
//! a `TerminatorKind::Call` or `TerminatorKind::Yield` and the destination `Place` or resume place, resp.,
74+
//! contains references/pointers or generic parameters we have to be careful and treat the
75+
//! `Local`s corresponding to the `Place`s as `NodeKind::LocalWithRef`s.
76+
//!
77+
//! 2. Liveness analysis for borrows
78+
//!
79+
//! We perform a standard liveness analysis on any outstanding references, pointers or `Local`s
80+
//! with `NodeKind::LocalWithRefs`. So we `gen` at any use site, which are either direct uses
81+
//! of these `Local`s or projections that contain these `Local`s. So e.g.:
82+
//!
83+
//! ```ignore(rust)
84+
//! 1. _3 = Foo {}
85+
//! 2. _4 = Bar {}
86+
//! 3. _5 = Ref(_3)
87+
//! 4. _6 = Ref(_4)
88+
//! 5. _7 = Aggregate(..)(move _5)
89+
//! 6. _8 = Call(..)(move _6) (assume _8 contains no refs/ptrs or generic params)
90+
//! 7. _9 = (_8.0)
91+
//! 8. (_7.0) = _9
92+
//! ```
93+
//!
94+
//! * `_5` is live from stmt 3 to stmt 5
95+
//! * `_6` is live from stmt 4 to stmt 6
96+
//! * `_7` is a `Local` of kind `LocalWithRef` so needs to be taken into account in the
97+
//! analyis. It's live from stmt 5 to stmt 8
98+
//!
99+
//! 3. Determining which `Local`s are borrowed
100+
//!
101+
//! Let's use our last example again. The dependency graph for that example looks as follows:
102+
//!
103+
//! `_5` (Borrow) -> `_3` (Local)
104+
//! `_6` (Borrow) -> `_4` (Local)
105+
//! `_7` (LocalWithRef) -> `_5` (Borrow)
106+
//! `_7` (LocalWithRef) -> `_9` (Local)
107+
//!
108+
//! So at each of those statements we have the following `Local`s that are live due to borrows:
109+
//!
110+
//! 1. {}
111+
//! 2. {}
112+
//! 3. {_3}
113+
//! 4. {_3, _4}
114+
//! 5. {_3, _4, _7}
115+
//! 6. {_3, _4, _7}
116+
//! 7. {_3, _7}
117+
//! 8. {_3, _7}
118+
//!
119+
1120
use super::*;
2121

3122
use crate::framework::{Analysis, Results, ResultsCursor};
@@ -14,16 +133,6 @@ use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitable};
14133
use core::ops::ControlFlow;
15134
use either::Either;
16135

17-
/// This module defines a more fine-grained analysis for `Local`s that are live due
18-
/// to outstanding references or raw pointers.
19-
/// The idea behind the analysis is that we first build a dependency graph between
20-
/// `Local`s corresponding to references or pointers and the `Local`s that are borrowed.
21-
/// This is done by the `BorrowDependencies` struct.
22-
/// As a second step we perform a liveness analysis for references and pointers, which is
23-
/// done by `LiveBorrows`.
24-
/// Finally we combine the results of the liveness analysis and the dependency graph to
25-
/// infer which borrowed locals need to be live at a given `Location`.
26-
27136
#[derive(Copy, Clone, Debug)]
28137
enum NodeKind {
29138
// An node corresponding to the place of the borrowed place (`_4` in this case) in
@@ -237,7 +346,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
237346

238347
self.dep_graph.add_edge(src_node_idx, node_idx, ());
239348
}
240-
_ => self.super_rvalue(rvalue, location),
349+
_ => {
350+
// FIXME Need to also create edges for `Local`s that correspond to `NodeKind::LocalWithRefs` here
351+
self.super_rvalue(rvalue, location)
352+
}
241353
}
242354
}
243355

@@ -272,7 +384,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BorrowDependencies<'a, 'tcx> {
272384
}
273385
None => {}
274386
},
275-
_ => {}
387+
_ => {
388+
// FIXME Need to also create edges for `Local`s that correspond to `NodeKind::LocalWithRefs` here
389+
}
276390
}
277391

278392
self.super_place(place, context, location)
@@ -382,6 +496,7 @@ where
382496
borrows_to_locals
383497
}
384498

499+
// FIXME Account for cycles in the graph!
385500
fn dfs_for_node(
386501
node_idx: NodeIndex,
387502
borrows_to_locals: &mut FxHashMap<Local, Vec<Local>>,

0 commit comments

Comments
 (0)