Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ env:
# This should be limited to packages that are intended for publishing.
RUST_MIN_VER_PKGS: >-
-p execution_tape
-p execution_graph
# List of packages that don't support `no_std`.
RUST_STD_ONLY_PKGS: >-
--exclude execution_graph_examples
Expand Down
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
# `forest-rs/execution`

Workspace repository for the `execution_tape` bytecode container + VM and related crates.
Workspace repository for `execution_tape`, a small verifiable bytecode container and VM, and
`execution_graph`, an incremental execution graph built on top of it.

Crates:
- `execution_tape/`: core `no_std + alloc` crate (format, verifier, VM, tracing hooks)
- `execution_tape_conformance/`: conformance/regression tests
- `execution_tape_wind_tunnel/`: benchmarks (Criterion)
- `execution_tape/`: publishable `no_std + alloc` crate for the bytecode format, verifier, VM,
tracing hooks, host ABI, and disassembler.
- `execution_graph/`: publishable `no_std + alloc` crate for dirty-tracked incremental execution
of verified tape programs.
- `execution_graph_examples/`: runnable graph examples, including the `tax` demo.
- `execution_tape_conformance/`: conformance/regression tests for the tape format, verifier, and VM.
- `execution_tape_profiling/`: optional profiling adapters; kept separate from the core crate.
- `execution_tape_wind_tunnel/` and `execution_graph_wind_tunnel/`: Criterion benchmarks.

Docs:
- `docs/overview.md`
- `docs/v1_spec.md`
- `docs/overview.md`: design notes for the tape VM and host boundary.
- `docs/v1_spec.md`: current v1 bytecode/container draft.
- `execution_tape/README.md`: crate overview and quick start.
- `execution_graph/README.md`: graph model, dependency keys, and demo.

The workspace MSRV is Rust 1.88.

Tickets:
- Tickets live in `.tickets/` at the repo root.
Expand All @@ -20,5 +30,9 @@ Suggested checks:
cargo fmt --all
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo test --doc --workspace --all-features
cargo package -p execution_tape
cargo package -p execution_graph
cargo bench -p execution_tape_wind_tunnel
cargo bench -p execution_graph_wind_tunnel
```
26 changes: 26 additions & 0 deletions execution_graph/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!-- Instructions

This changelog follows the patterns described here: <https://keepachangelog.com/en/>.

Subheadings to categorize changes are `added, changed, deprecated, removed, fixed, security`.

-->

# Changelog

The latest published Execution Graph release is [0.0.1](#001-2026-05-31) which was released on 2026-05-31.
You can find its changes [documented below](#001-2026-05-31).

## [Unreleased]

## [0.0.1][] (2026-05-31)

This release has an [MSRV][] of 1.88.

This is the initial release of Execution Graph, a `no_std` incremental execution graph for
dirty-tracked re-execution of verified `execution_tape` programs.

[Unreleased]: https://github.com/forest-rs/execution/compare/execution_graph-v0.0.1...HEAD
[0.0.1]: https://github.com/forest-rs/execution/releases/tag/execution_graph-v0.0.1

[MSRV]: README.md#minimum-supported-rust-version-msrv
71 changes: 71 additions & 0 deletions execution_graph/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,63 @@ Incremental execution graph built on `execution_tape`.
This crate provides a small `no_std` graph that executes verified `execution_tape` programs as
nodes and re-executes only the nodes that are affected by changes.

## Quick Start

Use `execution_tape` to build verified programs, then wire them as graph nodes:

```rust
use std::sync::Arc;

use execution_graph::ExecutionGraph;
use execution_tape::asm::{Asm, FunctionSig, ProgramBuilder};
use execution_tape::host::{Host, HostContext, HostError, SigHash, ValueRef};
use execution_tape::program::ValueType;
use execution_tape::value::Value;
use execution_tape::vm::Limits;

struct NoHost;

impl Host for NoHost {
fn call(
&mut self,
_symbol: &str,
_sig_hash: SigHash,
_args: &[ValueRef<'_>],
_rets: &mut [Value],
_ctx: HostContext<'_, '_>,
) -> Result<u64, HostError> {
Err(HostError::UnknownSymbol)
}
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut asm = Asm::new();
asm.const_i64(2, 1);
asm.i64_add(3, 1, 2);
asm.ret(0, &[3]);

let mut builder = ProgramBuilder::new();
let entry = builder.push_function_checked(
asm,
FunctionSig {
arg_types: vec![ValueType::I64],
ret_types: vec![ValueType::I64],
},
)?;
builder.set_function_output_name(entry, 0, "y")?;
let program = Arc::new(builder.build_verified()?);

let mut graph = ExecutionGraph::new(NoHost, Limits::default());
let node = graph.add_node(program, entry, vec!["x".into()]);
graph.set_input_value(node, "x", Value::I64(41));

let summary = graph.run_all()?;
assert_eq!(summary.executed_nodes, 1);
assert_eq!(graph.node_outputs(node).unwrap().get("y"), Some(&Value::I64(42)));
Ok(())
}
```

## Model

- **Nodes** are `(VerifiedProgram, entry FuncId)` pairs.
Expand Down Expand Up @@ -48,7 +105,21 @@ Run the demo with:
cargo run -p execution_graph_examples --bin tax
```

Emit Graphviz DOT for the same graph:

```sh
cargo run -p execution_graph_examples --bin tax -- --dot
```

## Current limitations

- `execution_graph` intentionally stays close to the VM: traps expose `execution_tape::vm::TrapInfo`
rather than source-language diagnostics.
- VM traps are still collapsed to `GraphError::Trap` at the graph boundary. Missing inputs,
missing upstream outputs, bad output arity, and strict-deps failures are reported with context.
- Graph nodes are currently `execution_tape` entrypoints only; custom dispatch can be layered later
without changing the resource-key model.

## Minimum supported Rust Version (MSRV)

This crate has been verified to compile with **Rust 1.88** and later.
56 changes: 56 additions & 0 deletions execution_graph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,62 @@
//! used consistently: the name passed to [`ExecutionGraph::set_input_value`] must match the name
//! passed to [`ExecutionGraph::invalidate_input`] (otherwise the invalidation will not affect the
//! reads recorded by runs).
//!
//! ## Example
//!
//! ```
//! use std::sync::Arc;
//!
//! use execution_graph::ExecutionGraph;
//! use execution_tape::asm::{Asm, FunctionSig, ProgramBuilder};
//! use execution_tape::host::{Host, HostContext, HostError, SigHash, ValueRef};
//! use execution_tape::program::ValueType;
//! use execution_tape::value::Value;
//! use execution_tape::vm::Limits;
//!
//! struct NoHost;
//!
//! impl Host for NoHost {
//! fn call(
//! &mut self,
//! _symbol: &str,
//! _sig_hash: SigHash,
//! _args: &[ValueRef<'_>],
//! _rets: &mut [Value],
//! _ctx: HostContext<'_, '_>,
//! ) -> Result<u64, HostError> {
//! Err(HostError::UnknownSymbol)
//! }
//! }
//!
//! let mut asm = Asm::new();
//! asm.const_i64(2, 1);
//! asm.i64_add(3, 1, 2);
//! asm.ret(0, &[3]);
//!
//! let mut builder = ProgramBuilder::new();
//! let entry = builder.push_function_checked(
//! asm,
//! FunctionSig {
//! arg_types: vec![ValueType::I64],
//! ret_types: vec![ValueType::I64],
//! },
//! )?;
//! builder.set_function_output_name(entry, 0, "y")?;
//! let program = Arc::new(builder.build_verified()?);
//!
//! let mut graph = ExecutionGraph::new(NoHost, Limits::default());
//! let node = graph.add_node(program, entry, vec!["x".into()])?;
//! graph.set_input_value(node, "x", Value::I64(41))?;
//!
//! let summary = graph.run_all()?;
//! assert_eq!(summary.executed_nodes, 1);
//! assert_eq!(
//! graph.node_outputs(node).unwrap().get("y"),
//! Some(&Value::I64(42))
//! );
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```

#![no_std]

Expand Down
26 changes: 26 additions & 0 deletions execution_tape/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!-- Instructions

This changelog follows the patterns described here: <https://keepachangelog.com/en/>.

Subheadings to categorize changes are `added, changed, deprecated, removed, fixed, security`.

-->

# Changelog

The latest published Execution Tape release is [0.0.1](#001-2026-05-31) which was released on 2026-05-31.
You can find its changes [documented below](#001-2026-05-31).

## [Unreleased]

## [0.0.1][] (2026-05-31)

This release has an [MSRV][] of 1.88.

This is the initial release of Execution Tape, a `no_std` bytecode container, verifier, register VM
runtime, host-call ABI, tracing API, and disassembler.

[Unreleased]: https://github.com/forest-rs/execution/compare/execution_tape-v0.0.1...HEAD
[0.0.1]: https://github.com/forest-rs/execution/releases/tag/execution_tape-v0.0.1

[MSRV]: README.md#minimum-supported-rust-version-msrv
96 changes: 93 additions & 3 deletions execution_tape/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,96 @@

Portable, verifiable bytecode container format and register VM runtime (draft).

Design docs:
- `docs/overview.md`
- `docs/v1_spec.md`
`execution_tape` is the low-level execution layer for already-lowered programs. It owns the
portable program format, verifier, register VM, host-call ABI, aggregate values, tracing hooks, and
disassembly tools. It does not own language semantics, graph authoring, or host object lifetimes.

The crate is `no_std + alloc` by default. The `std` feature is currently reserved for integrations
that need standard-library support.

## Quick Start

Build, verify, and run a one-function program:

```rust
extern crate alloc;

use alloc::vec;

use execution_tape::asm::{Asm, FunctionSig, ProgramBuilder};
use execution_tape::host::{Host, HostContext, HostError, SigHash, ValueRef};
use execution_tape::program::ValueType;
use execution_tape::trace::TraceMask;
use execution_tape::value::Value;
use execution_tape::vm::{Limits, Vm};

struct NoHost;

impl Host for NoHost {
fn call(
&mut self,
_symbol: &str,
_sig_hash: SigHash,
_args: &[ValueRef<'_>],
_rets: &mut [Value],
_ctx: HostContext<'_, '_>,
) -> Result<u64, HostError> {
Err(HostError::UnknownSymbol)
}
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut asm = Asm::new();
asm.const_i64(2, 1);
asm.i64_add(3, 1, 2);
asm.ret(0, &[3]);

let mut builder = ProgramBuilder::new();
builder.set_program_name("add_one");
let entry = builder.push_function_checked(
asm,
FunctionSig {
arg_types: vec![ValueType::I64],
ret_types: vec![ValueType::I64],
},
)?;
builder.set_function_input_name(entry, 0, "x")?;
builder.set_function_output_name(entry, 0, "y")?;

let program = builder.build_verified()?;
let mut vm = Vm::new(NoHost, Limits::default());
let out = vm.run(&program, entry, &[Value::I64(41)], TraceMask::NONE, None)?;
assert_eq!(out, vec![Value::I64(42)]);
Ok(())
}
```

## Core Pieces

- `asm`: ergonomic builders for functions, call signatures, constants, host signatures, and
bytecode emission.
- `program`: serialized program model, type tables, constants, host signatures, and names.
- `verifier`: validation and lowering into an execution-ready `VerifiedProgram`.
- `vm`: bounded interpreter for verified programs.
- `host`: host-call trait, borrowed argument views, aggregate readers, and access recording hooks.
- `trace`: low-overhead tracing events for profiling and diagnostics.
- `disasm`: human-readable disassembly for verified programs.

## Design Docs

The repository-level design notes live outside the packaged crate:

- <https://github.com/forest-rs/execution/blob/main/docs/overview.md>
- <https://github.com/forest-rs/execution/blob/main/docs/v1_spec.md>

## Examples

Print disassembly for a small branching program:

```sh
cargo run -p execution_tape --example disasm
```

## Minimum supported Rust Version (MSRV)

This crate has been verified to compile with **Rust 1.88** and later.
Loading