Generalize interpreter traversal over CFG, Block, DiGraph, and UnGraph bodies#684
Generalize interpreter traversal over CFG, Block, DiGraph, and UnGraph bodies#684zhenrongliew wants to merge 7 commits into
Conversation
This commit introduces a new test file `body_kinds.rs` that contains acceptance tests for the generic interpreter bodies. The tests cover various scenarios including: - A Cfg-bodied `main` calling a DiGraph-bodied function. - A statement inside a Cfg block that owns a DiGraph body. - A linear callable with a flat instruction list. - Ensuring that graph nodes run in topological order. These tests aim to validate the interaction between Cfg-SSA code and DiGraph computational graphs, ensuring correct execution and error handling.
- Renamed `BodyFrame` to `BlockFrame` and adjusted related documentation to reflect the new terminology. - Enhanced the `ToyFrame` enum to include `BlockFrame` and `CfgFrame`, replacing `BodyFrame`. - Updated `FrameBuild` implementations in `ToyFrame` and `TracingFrame` to accommodate the new frame types. - Revised tests in `tests/body_kinds.rs` to ensure compatibility with the updated frame structure. - Added comprehensive tests for structured control flow (SCF) operations, including `scf.if` and `scf.for`, demonstrating their integration with the new frame types. - Implemented a custom callable-UnGraph policy to handle ungraph traversal, ensuring proper execution order and output handling.
Resolve conflicts from the Region->CFG rename (#683, now in rust): - Apply the same Cfg->CFG / CFGs->CFG naming to this branch's changes. - Keep this branch's restructure of concrete/frames.rs into concrete/frames/ (drop the stale single-file frames.rs that rust only renamed). - Drop now-unused CFG imports surfaced by the merge. All tests (1234) and doctests pass; fmt and clippy clean.
Builders set `SSAInfo.kind` (use→def) but nobody ever populated
`SSAInfo.uses` (def→use). Both finalize paths just copied an
always-empty vec through. So finalized IR shipped an empty def-use.
- `StageInfo::rebuild_use_index()`: clears every live value's `uses,`
then scans each live statement's operands (`HasArguments` order) and
records one `Use { stmt, operand_index } `on the value each slot reads.
Idempotent, so the future rewriter can re-run it. Skips deleted
statements/tombstoned SSAs.
- both `finalize()` and `finalize_unchecked()` call it before returning.
- `Use` gains `new()` + `stmt()/operand_index()` accessors, `Copy`, and
serde parity with its container `SSAInfo`.
- `rebuild_use_index` now scan over nodes.digraphs, pushing a
`DiGraphYield { graph, index }` into `SSAInfo.uses`.
```rust
pub enum Use {
StatementOperand { stmt: Statement, index: usize },
DiGraphYield { graph: DiGraph, index: usize },
}
```
|
|
||
| /// The default walk plan of a digraph body (ports, toposorted nodes, | ||
| /// yields). Errors on cyclic digraphs. | ||
| fn digraph_walk_plan( |
There was a problem hiding this comment.
a default impl should be an unreachable error
| /// Capabilities required by forward frames. | ||
| /// | ||
| /// Re-exported as [`FrameDriver`](crate::FrameDriver). | ||
| pub trait ForwardFrameDriver: Env { |
There was a problem hiding this comment.
I think this trait can be decoupiled more we should revisit it later.
| Body::Block(block) => { | ||
| F::from_block(BlockFrame::new(target.stage, index, block, entry.args)) | ||
| } | ||
| Body::DiGraph(graph) => { |
There was a problem hiding this comment.
maybe allow CallFrame to be generic on body frames, so user can specify different body traversal rules with the same call convention.
pub trait HasBodyFrame {
type CFG;
type Block;
...
}
struct DefaultBodyFrame;
impl HasBodyFrame for DefaultBodyFrame {}
CallFrame<V, DefaultBodyFrame> // the current| } | ||
| } | ||
|
|
||
| pub fn step_into<I, F>(self, interp: &mut I) -> Result<FrameEffect<F, Completion<V>>, I::Error> |
There was a problem hiding this comment.
extract a common interface for frames, e.g step_into
| /// Where a graph port sits on its owning statement's boundary. | ||
| /// | ||
| /// This is a **location**, not a value mapping: the owning statement's | ||
| /// dialect rule translates port/index into its operands, captures, or | ||
| /// results — for values (forward) and demand (backward) alike. | ||
| #[derive(Clone, Copy, Debug)] | ||
| pub struct PortBoundary { | ||
| /// The statement that owns the graph. | ||
| pub owner: Statement, | ||
| /// Which boundary slot this port occupies. | ||
| pub index: usize, | ||
| } |
There was a problem hiding this comment.
Maybe consider moving this to anchor, or somewhere where location belongs.
|
|
||
| impl CFGTopology { | ||
| /// Deprecated name for [`BodyTopology`]; kept for one release. | ||
| pub type CFGTopology = BodyTopology; |
There was a problem hiding this comment.
we don't need compatibility
| @@ -0,0 +1,170 @@ | |||
| //! Mixed graph/SSA test language: regular SSA IR (arith + cf + function | |||
There was a problem hiding this comment.
need a more e2e test, like from a text file to interpreter/abstract interpreter
| ScfFor(ScfForFrame<V, E>), | ||
| } | ||
|
|
||
| impl<V, E> FrameBuild<V, E> for ToyFrame<V, E> { |
There was a problem hiding this comment.
ultimately this can be derive-ed in most cases, we can revisit this for UX later
Generalize interpreter body traversal beyond CFGs
Part of #667. This addresses the second follow-up identified in the issue:
Depends on the Region → CFG rename PR.
Summary
Bodyvocabulary covering:CfgBlockDiGraphUnGraphCfgFrametraverses CFGs.BlockFrametraverses one Block.DiGraphFrametraverses a directed computational graph.CallFramemanages callee resolution, activation lifetime, returns, and caller result binding.FrameBuild::from_ungraph_entry; otherwise the interpreter reportsNoDefaultWalker.Scope
Bodyremains an intentionally closed enum of Kirin’s built-in body representations. The extensibility point for UnGraph is its traversal policy, since an undirected graph has no inherent execution order.The remaining toy-lang sparse/dense liveness simplification from #667 is left for a separate PR.