Struct midenc_hir_transform::Treeify
source · pub struct Treeify;Expand description
This pass rewrites the CFG of a function so that it forms a tree.
While we technically call this treeification, the CFG cannot be fully converted into a tree in general, as loops must be preserved (they can be copied along multiple control flow paths, but we want to preserve the loop structure in the CFG).
The treeify transformation concerns itself with any block B which has multiple predecessors
in the control flow graph, where for at least two of those predecessors, the predecessor is
always visited before B, if control flows through both. This is a slightly less restrictive
conditon than the dominance property, but is very much related - the primary difference being
that unlike dominance, what we are capturing is that the predecessor block is not along a
loopback edge. It is quite common for a predecessor block to always be visited first in the
CFG, while not dominating its successor: consider an if/else expression, where control splits
at the if/else, and rejoins afterwards, the code in the final block where control is joined
can only be reached after either the if or else block has executed, but neither the if
nor the else blocks can be considered to “dominate” the final block in the graph theoretical
sense.
The actual treeification process works like so:
- For each block B, in the postorder sort of the CFG, determine if B has more than one predecessor P, where P appears before B in the reverse postorder sort of the CFG. a. If found, treeify the block as described in subsequent steps b. Otherwise, ignore this block and proceed
- For each P, clone B to a new block B’, and rewrite P such that it branches to B’ rather than B.
- For each successor S of B: a. If S is a loop header, and S appears before B in the reverse postorder sort of the CFG, then it is a loopback edge, so the corresponding edge from B’ to S is left intact. b. If S is a loop header, but S appears after B in the reverse postorder sort of the CFG, then it is treated like other blocks (see c.) c. Otherwise, clone S to S’, and rewrite B’ to branch to S’ instead of S.
- Repeat step 2 for the successors of S, recursively, until the subgraph reachable from B
Since we are treeifying blocks from the leaves of the CFG to the root, and because we do not follow loopback edges which escape/continue an outer loop - whenever we clone a subgraph of the CFG, we know that it has already been treeified, as we only start to treeify a block once all of the blocks reachable via that block have been treeified.
In short, we’re trying to split blocks with multiple predecessors such that all blocks have either zero or one predecessors, i.e. the CFG forms a tree. As mentioned previously, we must make an exception for loop headers, which by definition must have at least one predecessor which is a loopback edge, but this suits us just fine, as Miden Assembly provides control flow instructions compatible with lowering from such a CFG.
§Examples
§Basic DAG
This example demonstrates how the DAG of a function with multiple returns gets transformed:
blk0
|
v
blk1 -> blk3 -> ret
| /
| /
| /
v v
blk2
|
v
ret
Becomes:
blk0
|
v
blk1 -> blk3 -> ret
| |
| |
| |
v v
blk2 blk2
| |
v v
ret ret
§Basic Loop
This is an example of a function with multiple returns and a simple loop:
blk0
| -------
v v |
blk1 -> blk3 -> blk4 -> blk5 -> ret
| /
| /
| /
v v
blk2
|
v
ret
Becomes:
blk0
| -------
v v |
blk1 -> blk3 -> blk4 -> blk5 -> ret
| |
| |
| |
v v
blk2 blk2
| |
v v
ret ret
§Complex Loop
This is an example of a function with a complex loop (i.e. multiple exit points):
blk0
|
v
blk1
| \
| blk2 <-----
| | |
| blk3 |
| / \ |
| / blk4--
| / |
vv |
blk5 blk6
Becomes:
blk0
|
v
blk1
| \
| \
| blk2 <---
| | |
| v |
| blk3 |
| | \ |
| | blk4--
| | |
v v v
blk5 blk5 blk6
NOTE: Here, when generating code for blk5 and blk6, the loop depth is 0, so
we will emit a single push.0 at the end of both blocks which will terminate the
containing loop, and then return from the function as we’ve reached the bottom
of the tree.
§Nested Loops
This is an extension of the example above, but with nested loops:
blk0
|
v
blk1
| \
| blk2 <-------
| | | |
| blk3 | |
| / \ | |
| / blk4-- |
| / | |
vv v |
blk5<- blk6-->blk7-->blk8
| ^ |
| |_____________|
| |
|__________________|
We have two loops, the outer one starting at blk2:
blk2->blk3->blk4->blk2blk2->blk3->blk4->blk6->blk7->blk2
And the inner one starting at blk6:
blk6->blk7->blk8->blk6
Additionally, there are multiple exits through the loops, depending on the path taken:
blk2->blk3->blk5blk2->blk3->blk4->blk6->blk7->blk8->blk5blk6->blk7->blk8->blk5
After transformation, this becomes:
blk0
|
v
blk1
| \
| blk2 <-------
| | | |
| blk3 | |
| | \ | |
| | blk4-- |
| | | |
v v v |
blk5 blk5 blk6-->blk7-->blk8
^ | |
|_____________|_|
|
v
blk5
During codegen though, we end up with the following tree of stack machine code.
At each point where control flow either continues a loop or leaves it, we must
- Duplicate loop headers on control flow edges leading to those headers
- Emit N
push.0instructions on control flow edges exiting the function from a loop depth of N - Emit a combination of the above on control flow edges exiting an inner loop for an outer loop, depending on what depths the predecessor and successor blocks are at
blk0
blk1
if.true
blk2
while.true
blk3
if.true
blk4
if.true
blk2 # duplicated outer loop header
else
blk6
while.true
blk7
if.true
blk2 # duplicated outer loop header
push.0 # break out of inner loop
else
blk8
if.true
blk6 # duplicated inner loop header
else
blk5
push.0 # break out of outer loop
push.0 # break out of inner loop
end
end
end
end
else
blk5
push.0 # break out of outer loop
end
end
else
blk5
end
Trait Implementations§
source§impl PassInfo for Treeify
impl PassInfo for Treeify
source§const FLAG: &'static str = Treeify
const FLAG: &'static str = Treeify
source§const SUMMARY: &'static str = /// This pass rewrites the CFG of a function so that it forms a tree.
///
/// While we technically call this treeification, the CFG cannot be fully converted into a
/// tree in general, as loops must be preserved (they can be copied along multiple control
/// flow paths, but we want to preserve the loop structure in the CFG).
///
/// The treeify transformation concerns itself with any block B which has multiple predecessors
/// in the control flow graph, where for at least two of those predecessors, the predecessor is
/// always visited before B, if control flows through both. This is a slightly less restrictive
/// conditon than the dominance property, but is very much related - the primary difference being
/// that unlike dominance, what we are capturing is that the predecessor block is not along a
/// loopback edge. It is quite common for a predecessor block to always be visited first in the
/// CFG, while not dominating its successor: consider an if/else expression, where control splits
/// at the `if/else`, and rejoins afterwards, the code in the final block where control is joined
/// can only be reached after either the `if` or `else` block has executed, but neither the `if`
/// nor the `else` blocks can be considered to "dominate" the final block in the graph theoretical
/// sense.
///
/// The actual treeification process works like so:
///
/// 1. For each block B, in the postorder sort of the CFG, determine if B has more than one
/// predecessor P, where P appears before B in the reverse postorder sort of the CFG. a. If
/// found, treeify the block as described in subsequent steps b. Otherwise, ignore this block and
/// proceed
/// 2. For each P, clone B to a new block B', and rewrite P such that it branches to B' rather than
/// B.
/// 3. For each successor S of B:
/// a. If S is a loop header, and S appears before B in the reverse postorder sort of the CFG,
/// then it is a loopback edge, so the corresponding edge from B' to S is left intact.
/// b. If S is a loop header, but S appears after B in the reverse postorder sort of the CFG,
/// then it is treated like other blocks (see c.)
/// c. Otherwise, clone S to S', and rewrite B' to branch to S' instead of S.
/// 4. Repeat step 2 for the successors of S, recursively, until the subgraph reachable from B
///
/// Since we are treeifying blocks from the leaves of the CFG to the root, and because we do not
/// follow loopback edges which escape/continue an outer loop - whenever we clone a subgraph of
/// the CFG, we know that it has already been treeified, as we only start to treeify a block once
/// all of the blocks reachable via that block have been treeified.
///
/// In short, we're trying to split blocks with multiple predecessors such that all blocks have
/// either zero or one predecessors, i.e. the CFG forms a tree. As mentioned previously, we must
/// make an exception for loop headers, which by definition must have at least one predecessor
/// which is a loopback edge, but this suits us just fine, as Miden Assembly provides control flow
/// instructions compatible with lowering from such a CFG.
///
/// # Examples
///
/// ## Basic DAG
///
/// This example demonstrates how the DAG of a function with multiple returns gets transformed:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1 -> blk3 -> ret
/// | /
/// | /
/// | /
/// v v
/// blk2
/// |
/// v
/// ret
/// ```
///
/// Becomes:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1 -> blk3 -> ret
/// | |
/// | |
/// | |
/// v v
/// blk2 blk2
/// | |
/// v v
/// ret ret
/// ```
///
/// ## Basic Loop
///
/// This is an example of a function with multiple returns and a simple loop:
///
/// ```text,ignore
/// blk0
/// | -------
/// v v |
/// blk1 -> blk3 -> blk4 -> blk5 -> ret
/// | /
/// | /
/// | /
/// v v
/// blk2
/// |
/// v
/// ret
/// ```
///
/// Becomes:
///
/// ```text,ignore
/// blk0
/// | -------
/// v v |
/// blk1 -> blk3 -> blk4 -> blk5 -> ret
/// | |
/// | |
/// | |
/// v v
/// blk2 blk2
/// | |
/// v v
/// ret ret
/// ```
///
/// ## Complex Loop
///
/// This is an example of a function with a complex loop (i.e. multiple exit points):
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1
/// | \
/// | blk2 <-----
/// | | |
/// | blk3 |
/// | / \ |
/// | / blk4--
/// | / |
/// vv |
/// blk5 blk6
/// ```
///
/// Becomes:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1
/// | \
/// | \
/// | blk2 <---
/// | | |
/// | v |
/// | blk3 |
/// | | \ |
/// | | blk4--
/// | | |
/// v v v
/// blk5 blk5 blk6
/// ```
///
/// NOTE: Here, when generating code for `blk5` and `blk6`, the loop depth is 0, so
/// we will emit a single `push.0` at the end of both blocks which will terminate the
/// containing loop, and then return from the function as we've reached the bottom
/// of the tree.
///
/// ## Nested Loops
///
/// This is an extension of the example above, but with nested loops:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1
/// | \
/// | blk2 <-------
/// | | | |
/// | blk3 | |
/// | / \ | |
/// | / blk4-- |
/// | / | |
/// vv v |
/// blk5<- blk6-->blk7-->blk8
/// | ^ |
/// | |_____________|
/// | |
/// |__________________|
/// ```
///
/// We have two loops, the outer one starting at `blk2`:
///
/// * `blk2->blk3->blk4->blk2`
/// * `blk2->blk3->blk4->blk6->blk7->blk2`
///
/// And the inner one starting at `blk6`:
///
/// * `blk6->blk7->blk8->blk6`
///
/// Additionally, there are multiple exits through the loops, depending on the path taken:
///
/// * `blk2->blk3->blk5`
/// * `blk2->blk3->blk4->blk6->blk7->blk8->blk5`
/// * `blk6->blk7->blk8->blk5`
///
/// After transformation, this becomes:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1
/// | \
/// | blk2 <-------
/// | | | |
/// | blk3 | |
/// | | \ | |
/// | | blk4-- |
/// | | | |
/// v v v |
/// blk5 blk5 blk6-->blk7-->blk8
/// ^ | |
/// |_____________|_|
/// |
/// v
/// blk5
/// ```
///
/// During codegen though, we end up with the following tree of stack machine code.
///
/// At each point where control flow either continues a loop or leaves it, we must
///
/// * Duplicate loop headers on control flow edges leading to those headers
/// * Emit N `push.0` instructions on control flow edges exiting the function from a loop depth of N
/// * Emit a combination of the above on control flow edges exiting an inner loop for an outer loop,
/// depending on what depths the predecessor and successor blocks are at
///
/// ```text,ignore
/// blk0
/// blk1
/// if.true
/// blk2
/// while.true
/// blk3
/// if.true
/// blk4
/// if.true
/// blk2 # duplicated outer loop header
/// else
/// blk6
/// while.true
/// blk7
/// if.true
/// blk2 # duplicated outer loop header
/// push.0 # break out of inner loop
/// else
/// blk8
/// if.true
/// blk6 # duplicated inner loop header
/// else
/// blk5
/// push.0 # break out of outer loop
/// push.0 # break out of inner loop
/// end
/// end
/// end
/// end
/// else
/// blk5
/// push.0 # break out of outer loop
/// end
/// end
/// else
/// blk5
/// end
/// ```
#[derive(Default, PassInfo, ModuleRewritePassAdapter)]
pub struct Treeify;
const SUMMARY: &'static str = /// This pass rewrites the CFG of a function so that it forms a tree. /// /// While we technically call this treeification, the CFG cannot be fully converted into a /// tree in general, as loops must be preserved (they can be copied along multiple control /// flow paths, but we want to preserve the loop structure in the CFG). /// /// The treeify transformation concerns itself with any block B which has multiple predecessors /// in the control flow graph, where for at least two of those predecessors, the predecessor is /// always visited before B, if control flows through both. This is a slightly less restrictive /// conditon than the dominance property, but is very much related - the primary difference being /// that unlike dominance, what we are capturing is that the predecessor block is not along a /// loopback edge. It is quite common for a predecessor block to always be visited first in the /// CFG, while not dominating its successor: consider an if/else expression, where control splits /// at the `if/else`, and rejoins afterwards, the code in the final block where control is joined /// can only be reached after either the `if` or `else` block has executed, but neither the `if` /// nor the `else` blocks can be considered to "dominate" the final block in the graph theoretical /// sense. /// /// The actual treeification process works like so: /// /// 1. For each block B, in the postorder sort of the CFG, determine if B has more than one /// predecessor P, where P appears before B in the reverse postorder sort of the CFG. a. If /// found, treeify the block as described in subsequent steps b. Otherwise, ignore this block and /// proceed /// 2. For each P, clone B to a new block B', and rewrite P such that it branches to B' rather than /// B. /// 3. For each successor S of B: /// a. If S is a loop header, and S appears before B in the reverse postorder sort of the CFG, /// then it is a loopback edge, so the corresponding edge from B' to S is left intact. /// b. If S is a loop header, but S appears after B in the reverse postorder sort of the CFG, /// then it is treated like other blocks (see c.) /// c. Otherwise, clone S to S', and rewrite B' to branch to S' instead of S. /// 4. Repeat step 2 for the successors of S, recursively, until the subgraph reachable from B /// /// Since we are treeifying blocks from the leaves of the CFG to the root, and because we do not /// follow loopback edges which escape/continue an outer loop - whenever we clone a subgraph of /// the CFG, we know that it has already been treeified, as we only start to treeify a block once /// all of the blocks reachable via that block have been treeified. /// /// In short, we're trying to split blocks with multiple predecessors such that all blocks have /// either zero or one predecessors, i.e. the CFG forms a tree. As mentioned previously, we must /// make an exception for loop headers, which by definition must have at least one predecessor /// which is a loopback edge, but this suits us just fine, as Miden Assembly provides control flow /// instructions compatible with lowering from such a CFG. /// /// # Examples /// /// ## Basic DAG /// /// This example demonstrates how the DAG of a function with multiple returns gets transformed: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 -> blk3 -> ret /// | / /// | / /// | / /// v v /// blk2 /// | /// v /// ret /// ``` /// /// Becomes: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 -> blk3 -> ret /// | | /// | | /// | | /// v v /// blk2 blk2 /// | | /// v v /// ret ret /// ``` /// /// ## Basic Loop /// /// This is an example of a function with multiple returns and a simple loop: /// /// ```text,ignore /// blk0 /// | ------- /// v v | /// blk1 -> blk3 -> blk4 -> blk5 -> ret /// | / /// | / /// | / /// v v /// blk2 /// | /// v /// ret /// ``` /// /// Becomes: /// /// ```text,ignore /// blk0 /// | ------- /// v v | /// blk1 -> blk3 -> blk4 -> blk5 -> ret /// | | /// | | /// | | /// v v /// blk2 blk2 /// | | /// v v /// ret ret /// ``` /// /// ## Complex Loop /// /// This is an example of a function with a complex loop (i.e. multiple exit points): /// /// ```text,ignore /// blk0 /// | /// v /// blk1 /// | \ /// | blk2 <----- /// | | | /// | blk3 | /// | / \ | /// | / blk4-- /// | / | /// vv | /// blk5 blk6 /// ``` /// /// Becomes: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 /// | \ /// | \ /// | blk2 <--- /// | | | /// | v | /// | blk3 | /// | | \ | /// | | blk4-- /// | | | /// v v v /// blk5 blk5 blk6 /// ``` /// /// NOTE: Here, when generating code for `blk5` and `blk6`, the loop depth is 0, so /// we will emit a single `push.0` at the end of both blocks which will terminate the /// containing loop, and then return from the function as we've reached the bottom /// of the tree. /// /// ## Nested Loops /// /// This is an extension of the example above, but with nested loops: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 /// | \ /// | blk2 <------- /// | | | | /// | blk3 | | /// | / \ | | /// | / blk4-- | /// | / | | /// vv v | /// blk5<- blk6-->blk7-->blk8 /// | ^ | /// | |_____________| /// | | /// |__________________| /// ``` /// /// We have two loops, the outer one starting at `blk2`: /// /// * `blk2->blk3->blk4->blk2` /// * `blk2->blk3->blk4->blk6->blk7->blk2` /// /// And the inner one starting at `blk6`: /// /// * `blk6->blk7->blk8->blk6` /// /// Additionally, there are multiple exits through the loops, depending on the path taken: /// /// * `blk2->blk3->blk5` /// * `blk2->blk3->blk4->blk6->blk7->blk8->blk5` /// * `blk6->blk7->blk8->blk5` /// /// After transformation, this becomes: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 /// | \ /// | blk2 <------- /// | | | | /// | blk3 | | /// | | \ | | /// | | blk4-- | /// | | | | /// v v v | /// blk5 blk5 blk6-->blk7-->blk8 /// ^ | | /// |_____________|_| /// | /// v /// blk5 /// ``` /// /// During codegen though, we end up with the following tree of stack machine code. /// /// At each point where control flow either continues a loop or leaves it, we must /// /// * Duplicate loop headers on control flow edges leading to those headers /// * Emit N `push.0` instructions on control flow edges exiting the function from a loop depth of N /// * Emit a combination of the above on control flow edges exiting an inner loop for an outer loop, /// depending on what depths the predecessor and successor blocks are at /// /// ```text,ignore /// blk0 /// blk1 /// if.true /// blk2 /// while.true /// blk3 /// if.true /// blk4 /// if.true /// blk2 # duplicated outer loop header /// else /// blk6 /// while.true /// blk7 /// if.true /// blk2 # duplicated outer loop header /// push.0 # break out of inner loop /// else /// blk8 /// if.true /// blk6 # duplicated inner loop header /// else /// blk5 /// push.0 # break out of outer loop /// push.0 # break out of inner loop /// end /// end /// end /// end /// else /// blk5 /// push.0 # break out of outer loop /// end /// end /// else /// blk5 /// end /// ``` #[derive(Default, PassInfo, ModuleRewritePassAdapter)] pub struct Treeify;
source§const DESCRIPTION: &'static str = /// This pass rewrites the CFG of a function so that it forms a tree.
///
/// While we technically call this treeification, the CFG cannot be fully converted into a
/// tree in general, as loops must be preserved (they can be copied along multiple control
/// flow paths, but we want to preserve the loop structure in the CFG).
///
/// The treeify transformation concerns itself with any block B which has multiple predecessors
/// in the control flow graph, where for at least two of those predecessors, the predecessor is
/// always visited before B, if control flows through both. This is a slightly less restrictive
/// conditon than the dominance property, but is very much related - the primary difference being
/// that unlike dominance, what we are capturing is that the predecessor block is not along a
/// loopback edge. It is quite common for a predecessor block to always be visited first in the
/// CFG, while not dominating its successor: consider an if/else expression, where control splits
/// at the `if/else`, and rejoins afterwards, the code in the final block where control is joined
/// can only be reached after either the `if` or `else` block has executed, but neither the `if`
/// nor the `else` blocks can be considered to "dominate" the final block in the graph theoretical
/// sense.
///
/// The actual treeification process works like so:
///
/// 1. For each block B, in the postorder sort of the CFG, determine if B has more than one
/// predecessor P, where P appears before B in the reverse postorder sort of the CFG. a. If
/// found, treeify the block as described in subsequent steps b. Otherwise, ignore this block and
/// proceed
/// 2. For each P, clone B to a new block B', and rewrite P such that it branches to B' rather than
/// B.
/// 3. For each successor S of B:
/// a. If S is a loop header, and S appears before B in the reverse postorder sort of the CFG,
/// then it is a loopback edge, so the corresponding edge from B' to S is left intact.
/// b. If S is a loop header, but S appears after B in the reverse postorder sort of the CFG,
/// then it is treated like other blocks (see c.)
/// c. Otherwise, clone S to S', and rewrite B' to branch to S' instead of S.
/// 4. Repeat step 2 for the successors of S, recursively, until the subgraph reachable from B
///
/// Since we are treeifying blocks from the leaves of the CFG to the root, and because we do not
/// follow loopback edges which escape/continue an outer loop - whenever we clone a subgraph of
/// the CFG, we know that it has already been treeified, as we only start to treeify a block once
/// all of the blocks reachable via that block have been treeified.
///
/// In short, we're trying to split blocks with multiple predecessors such that all blocks have
/// either zero or one predecessors, i.e. the CFG forms a tree. As mentioned previously, we must
/// make an exception for loop headers, which by definition must have at least one predecessor
/// which is a loopback edge, but this suits us just fine, as Miden Assembly provides control flow
/// instructions compatible with lowering from such a CFG.
///
/// # Examples
///
/// ## Basic DAG
///
/// This example demonstrates how the DAG of a function with multiple returns gets transformed:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1 -> blk3 -> ret
/// | /
/// | /
/// | /
/// v v
/// blk2
/// |
/// v
/// ret
/// ```
///
/// Becomes:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1 -> blk3 -> ret
/// | |
/// | |
/// | |
/// v v
/// blk2 blk2
/// | |
/// v v
/// ret ret
/// ```
///
/// ## Basic Loop
///
/// This is an example of a function with multiple returns and a simple loop:
///
/// ```text,ignore
/// blk0
/// | -------
/// v v |
/// blk1 -> blk3 -> blk4 -> blk5 -> ret
/// | /
/// | /
/// | /
/// v v
/// blk2
/// |
/// v
/// ret
/// ```
///
/// Becomes:
///
/// ```text,ignore
/// blk0
/// | -------
/// v v |
/// blk1 -> blk3 -> blk4 -> blk5 -> ret
/// | |
/// | |
/// | |
/// v v
/// blk2 blk2
/// | |
/// v v
/// ret ret
/// ```
///
/// ## Complex Loop
///
/// This is an example of a function with a complex loop (i.e. multiple exit points):
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1
/// | \
/// | blk2 <-----
/// | | |
/// | blk3 |
/// | / \ |
/// | / blk4--
/// | / |
/// vv |
/// blk5 blk6
/// ```
///
/// Becomes:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1
/// | \
/// | \
/// | blk2 <---
/// | | |
/// | v |
/// | blk3 |
/// | | \ |
/// | | blk4--
/// | | |
/// v v v
/// blk5 blk5 blk6
/// ```
///
/// NOTE: Here, when generating code for `blk5` and `blk6`, the loop depth is 0, so
/// we will emit a single `push.0` at the end of both blocks which will terminate the
/// containing loop, and then return from the function as we've reached the bottom
/// of the tree.
///
/// ## Nested Loops
///
/// This is an extension of the example above, but with nested loops:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1
/// | \
/// | blk2 <-------
/// | | | |
/// | blk3 | |
/// | / \ | |
/// | / blk4-- |
/// | / | |
/// vv v |
/// blk5<- blk6-->blk7-->blk8
/// | ^ |
/// | |_____________|
/// | |
/// |__________________|
/// ```
///
/// We have two loops, the outer one starting at `blk2`:
///
/// * `blk2->blk3->blk4->blk2`
/// * `blk2->blk3->blk4->blk6->blk7->blk2`
///
/// And the inner one starting at `blk6`:
///
/// * `blk6->blk7->blk8->blk6`
///
/// Additionally, there are multiple exits through the loops, depending on the path taken:
///
/// * `blk2->blk3->blk5`
/// * `blk2->blk3->blk4->blk6->blk7->blk8->blk5`
/// * `blk6->blk7->blk8->blk5`
///
/// After transformation, this becomes:
///
/// ```text,ignore
/// blk0
/// |
/// v
/// blk1
/// | \
/// | blk2 <-------
/// | | | |
/// | blk3 | |
/// | | \ | |
/// | | blk4-- |
/// | | | |
/// v v v |
/// blk5 blk5 blk6-->blk7-->blk8
/// ^ | |
/// |_____________|_|
/// |
/// v
/// blk5
/// ```
///
/// During codegen though, we end up with the following tree of stack machine code.
///
/// At each point where control flow either continues a loop or leaves it, we must
///
/// * Duplicate loop headers on control flow edges leading to those headers
/// * Emit N `push.0` instructions on control flow edges exiting the function from a loop depth of N
/// * Emit a combination of the above on control flow edges exiting an inner loop for an outer loop,
/// depending on what depths the predecessor and successor blocks are at
///
/// ```text,ignore
/// blk0
/// blk1
/// if.true
/// blk2
/// while.true
/// blk3
/// if.true
/// blk4
/// if.true
/// blk2 # duplicated outer loop header
/// else
/// blk6
/// while.true
/// blk7
/// if.true
/// blk2 # duplicated outer loop header
/// push.0 # break out of inner loop
/// else
/// blk8
/// if.true
/// blk6 # duplicated inner loop header
/// else
/// blk5
/// push.0 # break out of outer loop
/// push.0 # break out of inner loop
/// end
/// end
/// end
/// end
/// else
/// blk5
/// push.0 # break out of outer loop
/// end
/// end
/// else
/// blk5
/// end
/// ```
#[derive(Default, PassInfo, ModuleRewritePassAdapter)]
pub struct Treeify;
const DESCRIPTION: &'static str = /// This pass rewrites the CFG of a function so that it forms a tree. /// /// While we technically call this treeification, the CFG cannot be fully converted into a /// tree in general, as loops must be preserved (they can be copied along multiple control /// flow paths, but we want to preserve the loop structure in the CFG). /// /// The treeify transformation concerns itself with any block B which has multiple predecessors /// in the control flow graph, where for at least two of those predecessors, the predecessor is /// always visited before B, if control flows through both. This is a slightly less restrictive /// conditon than the dominance property, but is very much related - the primary difference being /// that unlike dominance, what we are capturing is that the predecessor block is not along a /// loopback edge. It is quite common for a predecessor block to always be visited first in the /// CFG, while not dominating its successor: consider an if/else expression, where control splits /// at the `if/else`, and rejoins afterwards, the code in the final block where control is joined /// can only be reached after either the `if` or `else` block has executed, but neither the `if` /// nor the `else` blocks can be considered to "dominate" the final block in the graph theoretical /// sense. /// /// The actual treeification process works like so: /// /// 1. For each block B, in the postorder sort of the CFG, determine if B has more than one /// predecessor P, where P appears before B in the reverse postorder sort of the CFG. a. If /// found, treeify the block as described in subsequent steps b. Otherwise, ignore this block and /// proceed /// 2. For each P, clone B to a new block B', and rewrite P such that it branches to B' rather than /// B. /// 3. For each successor S of B: /// a. If S is a loop header, and S appears before B in the reverse postorder sort of the CFG, /// then it is a loopback edge, so the corresponding edge from B' to S is left intact. /// b. If S is a loop header, but S appears after B in the reverse postorder sort of the CFG, /// then it is treated like other blocks (see c.) /// c. Otherwise, clone S to S', and rewrite B' to branch to S' instead of S. /// 4. Repeat step 2 for the successors of S, recursively, until the subgraph reachable from B /// /// Since we are treeifying blocks from the leaves of the CFG to the root, and because we do not /// follow loopback edges which escape/continue an outer loop - whenever we clone a subgraph of /// the CFG, we know that it has already been treeified, as we only start to treeify a block once /// all of the blocks reachable via that block have been treeified. /// /// In short, we're trying to split blocks with multiple predecessors such that all blocks have /// either zero or one predecessors, i.e. the CFG forms a tree. As mentioned previously, we must /// make an exception for loop headers, which by definition must have at least one predecessor /// which is a loopback edge, but this suits us just fine, as Miden Assembly provides control flow /// instructions compatible with lowering from such a CFG. /// /// # Examples /// /// ## Basic DAG /// /// This example demonstrates how the DAG of a function with multiple returns gets transformed: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 -> blk3 -> ret /// | / /// | / /// | / /// v v /// blk2 /// | /// v /// ret /// ``` /// /// Becomes: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 -> blk3 -> ret /// | | /// | | /// | | /// v v /// blk2 blk2 /// | | /// v v /// ret ret /// ``` /// /// ## Basic Loop /// /// This is an example of a function with multiple returns and a simple loop: /// /// ```text,ignore /// blk0 /// | ------- /// v v | /// blk1 -> blk3 -> blk4 -> blk5 -> ret /// | / /// | / /// | / /// v v /// blk2 /// | /// v /// ret /// ``` /// /// Becomes: /// /// ```text,ignore /// blk0 /// | ------- /// v v | /// blk1 -> blk3 -> blk4 -> blk5 -> ret /// | | /// | | /// | | /// v v /// blk2 blk2 /// | | /// v v /// ret ret /// ``` /// /// ## Complex Loop /// /// This is an example of a function with a complex loop (i.e. multiple exit points): /// /// ```text,ignore /// blk0 /// | /// v /// blk1 /// | \ /// | blk2 <----- /// | | | /// | blk3 | /// | / \ | /// | / blk4-- /// | / | /// vv | /// blk5 blk6 /// ``` /// /// Becomes: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 /// | \ /// | \ /// | blk2 <--- /// | | | /// | v | /// | blk3 | /// | | \ | /// | | blk4-- /// | | | /// v v v /// blk5 blk5 blk6 /// ``` /// /// NOTE: Here, when generating code for `blk5` and `blk6`, the loop depth is 0, so /// we will emit a single `push.0` at the end of both blocks which will terminate the /// containing loop, and then return from the function as we've reached the bottom /// of the tree. /// /// ## Nested Loops /// /// This is an extension of the example above, but with nested loops: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 /// | \ /// | blk2 <------- /// | | | | /// | blk3 | | /// | / \ | | /// | / blk4-- | /// | / | | /// vv v | /// blk5<- blk6-->blk7-->blk8 /// | ^ | /// | |_____________| /// | | /// |__________________| /// ``` /// /// We have two loops, the outer one starting at `blk2`: /// /// * `blk2->blk3->blk4->blk2` /// * `blk2->blk3->blk4->blk6->blk7->blk2` /// /// And the inner one starting at `blk6`: /// /// * `blk6->blk7->blk8->blk6` /// /// Additionally, there are multiple exits through the loops, depending on the path taken: /// /// * `blk2->blk3->blk5` /// * `blk2->blk3->blk4->blk6->blk7->blk8->blk5` /// * `blk6->blk7->blk8->blk5` /// /// After transformation, this becomes: /// /// ```text,ignore /// blk0 /// | /// v /// blk1 /// | \ /// | blk2 <------- /// | | | | /// | blk3 | | /// | | \ | | /// | | blk4-- | /// | | | | /// v v v | /// blk5 blk5 blk6-->blk7-->blk8 /// ^ | | /// |_____________|_| /// | /// v /// blk5 /// ``` /// /// During codegen though, we end up with the following tree of stack machine code. /// /// At each point where control flow either continues a loop or leaves it, we must /// /// * Duplicate loop headers on control flow edges leading to those headers /// * Emit N `push.0` instructions on control flow edges exiting the function from a loop depth of N /// * Emit a combination of the above on control flow edges exiting an inner loop for an outer loop, /// depending on what depths the predecessor and successor blocks are at /// /// ```text,ignore /// blk0 /// blk1 /// if.true /// blk2 /// while.true /// blk3 /// if.true /// blk4 /// if.true /// blk2 # duplicated outer loop header /// else /// blk6 /// while.true /// blk7 /// if.true /// blk2 # duplicated outer loop header /// push.0 # break out of inner loop /// else /// blk8 /// if.true /// blk6 # duplicated inner loop header /// else /// blk5 /// push.0 # break out of outer loop /// push.0 # break out of inner loop /// end /// end /// end /// end /// else /// blk5 /// push.0 # break out of outer loop /// end /// end /// else /// blk5 /// end /// ``` #[derive(Default, PassInfo, ModuleRewritePassAdapter)] pub struct Treeify;
source§impl RewritePass for Treeify
impl RewritePass for Treeify
source§fn apply(
&mut self,
function: &mut Self::Entity,
analyses: &mut AnalysisManager,
session: &Session,
) -> RewriteResult
fn apply( &mut self, function: &mut Self::Entity, analyses: &mut AnalysisManager, session: &Session, ) -> RewriteResult
entitysource§fn should_apply(&self, _entity: &Self::Entity, _session: &Session) -> bool
fn should_apply(&self, _entity: &Self::Entity, _session: &Session) -> bool
entitysource§fn chain<R>(self, next: R) -> RewriteSet<Self::Entity>
fn chain<R>(self, next: R) -> RewriteSet<Self::Entity>
next as a pipeline of rewritesAuto Trait Implementations§
impl Freeze for Treeify
impl RefUnwindSafe for Treeify
impl Send for Treeify
impl Sync for Treeify
impl Unpin for Treeify
impl UnwindSafe for Treeify
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moresource§impl<D> OwoColorize for D
impl<D> OwoColorize for D
source§fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
source§fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
source§fn black(&self) -> FgColorDisplay<'_, Black, Self>
fn black(&self) -> FgColorDisplay<'_, Black, Self>
source§fn on_black(&self) -> BgColorDisplay<'_, Black, Self>
fn on_black(&self) -> BgColorDisplay<'_, Black, Self>
source§fn red(&self) -> FgColorDisplay<'_, Red, Self>
fn red(&self) -> FgColorDisplay<'_, Red, Self>
source§fn on_red(&self) -> BgColorDisplay<'_, Red, Self>
fn on_red(&self) -> BgColorDisplay<'_, Red, Self>
source§fn green(&self) -> FgColorDisplay<'_, Green, Self>
fn green(&self) -> FgColorDisplay<'_, Green, Self>
source§fn on_green(&self) -> BgColorDisplay<'_, Green, Self>
fn on_green(&self) -> BgColorDisplay<'_, Green, Self>
source§fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>
fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>
source§fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>
fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>
source§fn blue(&self) -> FgColorDisplay<'_, Blue, Self>
fn blue(&self) -> FgColorDisplay<'_, Blue, Self>
source§fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>
fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>
source§fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>
fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>
source§fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
source§fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>
fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>
source§fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>
source§fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>
fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>
source§fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>
fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>
source§fn white(&self) -> FgColorDisplay<'_, White, Self>
fn white(&self) -> FgColorDisplay<'_, White, Self>
source§fn on_white(&self) -> BgColorDisplay<'_, White, Self>
fn on_white(&self) -> BgColorDisplay<'_, White, Self>
source§fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
source§fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
source§fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
source§fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
source§fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
source§fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
source§fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
source§fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
source§fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
source§fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
source§fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
source§fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
source§fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
source§fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
source§fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
source§fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
source§fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
source§fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
source§fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
source§fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
source§fn bold(&self) -> BoldDisplay<'_, Self>
fn bold(&self) -> BoldDisplay<'_, Self>
source§fn dimmed(&self) -> DimDisplay<'_, Self>
fn dimmed(&self) -> DimDisplay<'_, Self>
source§fn italic(&self) -> ItalicDisplay<'_, Self>
fn italic(&self) -> ItalicDisplay<'_, Self>
source§fn underline(&self) -> UnderlineDisplay<'_, Self>
fn underline(&self) -> UnderlineDisplay<'_, Self>
source§fn blink(&self) -> BlinkDisplay<'_, Self>
fn blink(&self) -> BlinkDisplay<'_, Self>
source§fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
source§fn reversed(&self) -> ReversedDisplay<'_, Self>
fn reversed(&self) -> ReversedDisplay<'_, Self>
source§fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
source§fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::fg or
a color-specific method, such as OwoColorize::green, Read moresource§fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::bg or
a color-specific method, such as OwoColorize::on_yellow, Read more