Loops
The loop head is the basic block with an incoming back edge. We define as the location of the first statement of the loop head.
The pre-loop block is the block prior to the loop head. We assume that there is always a unique pre-loop block. The pre-loop state is the state after evaluating the terminator of the pre-loop block.
The following operations are performed when we join the pre-loop block with the loop head. Note that at this point we've already computed .
We construct the state for the loop head as follows:
Step 1 - Identify Relevant Loop Nodes
We identify the following sets of places:
-
: the places used inside the loop that are live and initialized at .
-
: the subset of that are directly borrowed by a borrow live at
-
: the subset of that contain lifetime projections
-
: Places used in the loop that may be relevant for the invariant:
is the union of and the associated lifetime projections of .
are the associated lifetime projections of .
Step 2 - Expand Pre-State Graph to Include Relevant Loop Nodes
The nodes in will need to appear in but may not be present in (for example, it's possible that the loop could borrow from a subplace that requires unpacking). We construct a graph by unpacking such that contains all places in .
Step 3 - Identify Borrow Roots
The borrow roots of a place are the most immediate places that could be borrowing from and later become accessible, and excluding places already in
We defined the borrow roots using the function :
- Initialize a list to contain all lifetime projections in
- while is not empty:
- Pop from
- For each edge blocked by in :
- If the edge is an unpack edge, add all of its blocked nodes to
- Otherwise, for each blocked node in :
- If or , do nothing
- If is live at , add to
- If is a root in , add to
- Otherwise, add to
- The resulting roots are the associated places of
We then identify , the most immediate nodes that could be borrowing from and later become accessible (excluding nodes already in ). is the union of the roots for each place in .
Step 4 - Construct Abstraction Graph And Compute Blocked Lifetime Projections
We construct an abstraction graph that describes the blocking relations potentially introduced in the loop from places in to nodes in and from nodes in to nodes in .
connect() function:
We begin by define a helper function which adds edges to based on being blocked by in the loop:
- Identify : the top-level lifetime projection in
- Insert a loop abstraction edge into
- For each :
- Identify the lifetime projections in that may mutate borrows in
- If is nonempty:
- Introduce a placeholder node
- Add a borrowflow hyperedge
- Add a future hyperedges:
- Identify the lifetime projections in that borrows in may flow into
- If is nonempty:
- Add a borrowflow hyperedge
- Identify the lifetime projections in that may mutate borrows in
Algorithm:
-
For each blocker place :
- For each , perform
- For each
- If blocks at , then perform
-
Subsequently, ensure that is well-formed by adding unpack edges where appropriate. For example, if
(*x).f
is in the graph, there should also be an expansion edge from*x
to(*x).f
. -
We identify the set of lifetime projections that will need to be renamed (indicating they will be expanded in the loop and remain non-accessible at the loop head). is the set of non-leaf lifetime projection nodes in (leaf nodes are accessible at the head).
-
Label all lifetime projections in with location , add connections to their
Future
nodes as necessary.
Step 5 - Label Blocked Lifetime Projections in Pre-State
The resulting graph for the loop head will require new labels on lifetime projections modified in the loop. We begin by constructing an intermediate graph by labelling each lifetime projection in with and remove capabilities to all places in in .
Step 6 - Identify Pre-State Subgraph to Replace With Abstraction Graph
We then identify the subgraph that will be removed from and replaced with the loop abstraction .
- Let = .
- We construct by including all nodes in and all edges where there exists where is on a path between and in .
Step 7 - Replace Pre-State Subgraph with Abstraction Graph
The graph for the loop head is defined as
Step 8 (Optional) - Confirm Invariant is Correct
To confirm that the resulting graph is correct, for any back edge into the state at with state , performing the loop join operation on and should yield .