Skip to content

Commit 61d9491

Browse files
committed
document Iris migration architecture
1 parent e10bcdd commit 61d9491

2 files changed

Lines changed: 966 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,156 @@ The interpreter is deliberately optimized for **simplicity of reasoning, not exe
1010

1111
Lean toolchain is pinned in `lean-toolchain`.
1212

13+
## Iris migration (`iris` branch only)
14+
15+
> **Temporary branch-scoped notice.** This section must exist only on the
16+
> `iris` integration branch and branches created from `iris` during the
17+
> migration. It overrides conflicting guidance elsewhere in this file,
18+
> especially guidance to preserve the current big-step interpreter's shape.
19+
> Before merging the completed migration into `main`, remove this notice or
20+
> update it to describe the new permanent architecture.
21+
22+
The purpose of the `iris` branch is to integrate
23+
[iris-lean](https://github.com/leanprover-community/iris-lean) and migrate the
24+
reference Wasm interpreter from its current fuel-bounded big-step semantics to
25+
small-step semantics suitable for Iris.
26+
27+
The migration is complete when:
28+
29+
- The interpreter is fully expressed using small-step semantics in the form
30+
expected by iris-lean.
31+
- Every currently supported Wasm feature and test remains covered, with no
32+
semantic regressions.
33+
- Differential testing remains possible. In particular, there must still be an
34+
executable, fuel-bounded way to run Wasm code, built by iterating the
35+
executable small-step function rather than by maintaining a second semantics.
36+
- All required iris-lean language and proof-mode instances are implemented, so
37+
downstream proofs can use iris-lean directly.
38+
- Existing proofs and theorem statements need not retain their exact shape.
39+
Nevertheless, preserve the intent and coverage of existing examples whenever
40+
possible, rewriting them against the new semantics rather than silently
41+
deleting them.
42+
43+
The intended split between an Iris expression and state is approximately:
44+
45+
```lean
46+
inductive Expr
47+
| running : ThreadState → Expr
48+
| done : List WasmVal → Expr
49+
| trapped : TrapReason → Expr
50+
deriving BEq, Repr
51+
52+
structure Store where
53+
functions : Array Function
54+
memories : Array Memory
55+
globals : Array Global
56+
tables : Array Table
57+
deriving BEq, Repr
58+
```
59+
60+
Treat this as an architectural guide, not a requirement to preserve these exact
61+
names. `Expr` contains the whole per-execution `ThreadState`, not merely the
62+
remaining instruction list. `Store` contains the shared runtime resources.
63+
Keep that ownership boundary explicit when adding fields: thread-local control
64+
and operand state belongs in `ThreadState`; resources observed or mutated
65+
through the runtime store belong in `Store`.
66+
67+
For convenient executable interpretation, package the Iris expression and
68+
state together, while keeping the relational semantics authoritative:
69+
70+
```lean
71+
/- Convenient packaging for the executable interpreter. The Iris adapter will
72+
split this back into its `Expr` and `State` arguments. -/
73+
structure Config where
74+
expr : Expr
75+
store : Store
76+
deriving BEq, Repr
77+
78+
inductive Step : Config → Config → Prop where
79+
-- Full definition of all valid transitions.
80+
81+
def step? : Config → Option Config
82+
| ⟨.running thread, store⟩ => stepRunning? thread store
83+
| ⟨.done _, _⟩ => none
84+
| ⟨.trapped _, _⟩ => none
85+
86+
theorem step?_sound {config config' : Config} :
87+
step? config = some config' → Step config config' := by
88+
sorry
89+
90+
theorem step?_complete {config config' : Config} :
91+
Step config config' → step? config = some config' := by
92+
sorry
93+
94+
theorem step_sound {config config' : Config} :
95+
step? config = some config' → Step config config' :=
96+
step?_sound
97+
98+
theorem step_complete {config config' : Config} :
99+
Step config config' → step? config = some config' :=
100+
step?_complete
101+
102+
theorem step_iff {config config' : Config} :
103+
step? config = some config' ↔ Step config config' :=
104+
⟨step_sound, step_complete⟩
105+
```
106+
107+
The exact iris-lean adapter must follow the API of the pinned iris-lean
108+
dependency. Implement every required instance from this `Expr`/`Store` split
109+
and prove it against `Step`; do not introduce a parallel Iris-only transition
110+
relation.
111+
112+
Use instruction-level granularity as the default: one `Step` should normally
113+
execute one Wasm instruction. Administrative transitions may execute no Wasm
114+
instruction when they expose or remove control frames, prepare or return from a
115+
function call, propagate a trap, or otherwise reorganize the machine so the
116+
next instruction can run. Prefer small, explicit administrative transitions
117+
over hiding multi-stage control behavior inside a single large step. A
118+
transition may perform the atomic state effects intrinsic to its instruction;
119+
do not split an instruction solely to mirror implementation helper functions.
120+
Document intentional exceptions and keep the granularity consistent across
121+
related instructions.
122+
123+
For now, assume the Wasm semantics implemented here is deterministic, as
124+
required by `step? : Config → Option Config` and `step?_complete`. Whenever a
125+
feature appears nondeterministic—or depends on unspecified host behavior,
126+
scheduling, external input, or an arbitrary choice—flag it before implementing
127+
the transition. Record whether the behavior can be made deterministic by an
128+
explicit input or policy in `Store`. If genuine nondeterminism is required,
129+
stop and decide how to represent executable successor choices and adapt the
130+
Iris semantics and correspondence theorem; never choose an outcome silently.
131+
132+
During the migration:
133+
134+
- Pin iris-lean to a known revision. Upgrade it intentionally, recording any
135+
adapter or instance changes required by the new API.
136+
- Treat the instruction and administrative-step policy above as the default
137+
Iris atomicity boundary. Review deviations explicitly; do not change
138+
granularity merely to make the executable runner more convenient.
139+
- Make the relational `Step` and executable `step?` correspond in every PR.
140+
A new transition is incomplete until both sides and their soundness and
141+
completeness proofs agree.
142+
- State and preserve invariants at transition boundaries, including stack
143+
typing, index validity, store-extension/ownership properties, and the
144+
distinction between normal completion and traps.
145+
- Keep `.done` and `.trapped` terminal. Out-of-fuel belongs to the executable
146+
runner's result, not to `Expr` or the semantic relation.
147+
- Add regression or differential tests while porting each instruction family.
148+
Where feasible, compare the new runner with the old interpreter until the old
149+
implementation is removed.
150+
- Keep migration PRs reviewable and layered: introduce representation and
151+
compatibility lemmas first, then port coherent instruction families and
152+
their examples. Do not remove the old path before equivalent coverage exists.
153+
- Build every affected package in dependency order. A successful build remains
154+
the test criterion throughout the transition.
155+
156+
iris-lean does not currently provide total-execution reasoning for this
157+
integration. Iris proofs therefore establish behavior conditional on reaching
158+
completion; they do not establish termination. This loss of total-correctness
159+
claims is accepted during the migration. Keep termination-sensitive theorem
160+
intent documented so it can be restored if total reasoning becomes available,
161+
and do not describe a partial-correctness result as a termination proof.
162+
13163
## Repository layout
14164

15165
Three Lake packages in a monorepo, forming a strict dependency chain:

0 commit comments

Comments
 (0)