## PR-03 — Frame model v0: locals, operand stack, and function metadata **Why:** `let x: int = 1` failing usually means locals/frames are not modeled correctly. ### Scope * Define `FunctionMeta`: * `code_offset`, `code_len` * `param_slots`, `local_slots`, `return_slots` * `max_stack_slots` (computed by verifier or compiler) * Define `Frame`: * `base` (stack base index) * `locals_base` (or equivalent) * `return_slots` * `pc_return` * Decide representation: * Option A (recommended v0): **single VM stack** with fixed layout per frame: * `[args][locals][operand_stack...]` * Use `base + local_index` addressing. ### Deliverables * `CallStack` with `Vec` * `enter_frame(meta)` allocates locals area (zero-init) * `leave_frame()` reclaims to previous base ### Tests * locals are isolated per call * locals are zero-initialized * stack is restored exactly after return ### Acceptance * Locals are deterministic and independent from operand stack usage. --- ## PR-04 — Locals opcodes: GET_LOCAL / SET_LOCAL / INIT_LOCAL **Why:** PBS `let` and parameters need first-class support. ### Scope * Implement opcodes: * `GET_LOCAL ` pushes value slots * `SET_LOCAL ` pops value slots and writes * `INIT_LOCAL ` (optional) for explicit initialization semantics * Enforce bounds: local slot index must be within `[0..param+local_slots)` * Enforce slot width: if types are multi-slot, compiler emits multiple GET/SET or uses `*_N` variants. ### Deliverables * `LocalAddressing` utilities * Deterministic trap codes: * `TRAP_INVALID_LOCAL` * `TRAP_LOCAL_WIDTH_MISMATCH` (if enforced) ### Tests * `let x: int = 1; return x;` works * invalid local index traps ### Acceptance * `let` works reliably; no stack side effects beyond specified pops/pushes. --- ## PR-05 — Core arithmetic + comparisons in VM (int/bounded/bool) **Why:** The minimal executable PBS needs arithmetic that doesn’t corrupt stack. ### Scope * Implement v0 numeric opcodes (slot-safe): * `IADD, ISUB, IMUL, IDIV, IMOD` * `ICMP_EQ, ICMP_NE, ICMP_LT, ICMP_LE, ICMP_GT, ICMP_GE` * `BADD, BSUB, ...` (or unify with tagged values) * Define conversion opcodes if lowering expects them: * `BOUND_TO_INT`, `INT_TO_BOUND_CHECKED` (trap OOB) ### Deliverables * Deterministic traps: * `TRAP_DIV_ZERO` * `TRAP_OOB` (bounded checks) ### Tests * simple arithmetic chain * div by zero traps * bounded conversions trap on overflow ### Acceptance * Arithmetic and comparisons are closed and verified. --- ## PR-06 — Control flow opcodes: jumps, conditional branches, structured “if” **Why:** `if` must be predictable and verifier-safe. ### Scope * Implement opcodes: * `JMP ` * `JMP_IF_TRUE ` * `JMP_IF_FALSE ` * Verifier rules: * targets must be valid instruction boundaries * stack height at join points must match ### Tests * nested if * if with empty branches * branch join mismatch rejected ### Acceptance * Control flow is safe; no implicit stack juggling. --- ## PR-07 — Calling convention v0: CALL / RET / multi-slot returns **Why:** Without a correct call model, PBS isn’t executable. ### Scope * Introduce `CALL ` * caller pushes args (slots) * callee frame allocates locals * Introduce `RET` * callee must leave exactly `return_slots` on operand stack at `RET` * VM pops frame and transfers return slots to caller * Define return mechanics for `void` (`return_slots=0`) ### Deliverables * `FunctionTable` indexing and bounds checks * Deterministic traps: * `TRAP_INVALID_FUNC` * `TRAP_BAD_RET_SLOTS` ### Tests * `fn add(a:int,b:int):int { return a+b; }` * multi-slot return (e.g., `Pad` flattened) * void call ### Acceptance * Calls are stable and stack-clean. --- ## PR-08 — Host syscalls v0: stable ABI, multi-slot args/returns **Why:** PBS relies on deterministic syscalls; ABI must be frozen and enforced. ### Scope * Unify syscall invocation opcode: * `SYSCALL ` * Runtime validates: * pops `arg_slots` * pushes `ret_slots` * Implement/confirm: * `GfxClear565 (0x1010)` * `InputPadSnapshot (0x2010)` * `InputTouchSnapshot (0x2011)` ### Deliverables * A `SyscallRegistry` mapping id -> handler + signature * Deterministic traps: * `TRAP_INVALID_SYSCALL` * `TRAP_SYSCALL_SIG_MISMATCH` ### Tests * syscall isolated tests * wrong signature traps ### Acceptance * Syscalls are “industrial”: typed by signature, deterministic, no host surprises. --- ## PR-09 — Debug info v0: spans, symbols, and traceable traps **Why:** Industrial debugging requires actionable failures. ### Scope * Add optional debug section: * per-instruction span table (`pc -> (file_id, start, end)`) * function names * Enhance trap payload with debug span (if present) ### Tests * trap includes span when debug present * trap still works without debug ### Acceptance * You can pinpoint “where” a trap happened reliably. --- ## PR-10 — Program image + linker: imports/exports resolved before VM run **Why:** Imports are compile-time, but we need an industrial linking model for multi-module PBS. ### Scope * Define in bytecode: * `exports`: symbol -> func_id/service entry (as needed) * `imports`: symbol refs -> relocation slots * Implement a **linker** that: * builds a `ProgramImage` from N modules * resolves imports to exports * produces a single final `FunctionTable` and code blob ### Notes * VM **does not** do name lookup at runtime. * Linking errors are deterministic: `LINK_UNRESOLVED_SYMBOL`, `LINK_DUP_EXPORT`, etc. ### Tests * two-module link success * unresolved import fails * duplicate export fails ### Acceptance * Multi-module PBS works; “import” is operationalized correctly. --- ## PR-11 — Canonical integration cartridge + golden bytecode snapshots **Why:** One cartridge must be the unbreakable reference. ### Scope * Create `CartridgeCanonical.pbs` that covers: * locals * arithmetic * if * function call * syscall clear * input snapshot * Add `golden` artifacts: * canonical AST JSON (frontend) * IR Core (optional) * IR VM / bytecode dump * expected VM trace (optional) ### Tests * CI runs cartridge and checks: * no traps * deterministic output state ### Acceptance * This cartridge is the “VM heartbeat test”. --- ## PR-12 — VM test harness: stepper, trace, and property tests **Why:** Industrial quality means test tooling, not just “it runs”. ### Scope * Add `VmRunner` test harness: * step limit * deterministic trace of stack deltas * snapshot of locals * Add property tests (lightweight): * stack never underflows in verified programs * verified programs never jump out of bounds ### Acceptance * Debugging is fast, and regressions are caught. --- ## PR-13 — Optional: Refactor Value representation (tagged slots) for clarity **Why:** If current `Value` representation is the source of complexity/bugs, refactor now. ### Scope (only if needed) * Make `Slot` explicit: * `Slot::I32`, `Slot::I64`, `Slot::U32`, `Slot::Bool`, `Slot::ConstId`, `Slot::GateId`, `Slot::Unit` * Multi-slot types become sequences of slots. ### Acceptance * Simpler, more verifiable runtime. --- # Work split (what can be parallel later) * VM core correctness: PR-01..PR-08 (sequential, contract-first) * Debug + tooling: PR-09, PR-12 (parallel after PR-03) * Linking/imports: PR-10 (parallel after PR-01) * Canonical cartridge: PR-11 (parallel after PR-05) --- # “Stop the line” rules 1. If a PR introduces an opcode without stack spec + verifier integration, it’s rejected. 2. If a PR changes bytecode layout without bumping version, it’s rejected. 3. If a PR adds a feature before the canonical cartridge passes, it’s rejected. --- # First implementation target (tomorrow morning, start here) **Start with PR-02 (Opcode spec + verifier)** even if you think you already know the bug. Once the verifier exists, the rest becomes mechanical: every failure becomes *actionable*. ## Definition of Done (DoD) for PBS v0 “minimum executable” A single canonical cartridge runs end-to-end: * `let` declarations (locals) * arithmetic (+, -, *, /, %, comparisons) * `if/else` control flow * `when` expression (if present in lowering) * function calls with params + returns (including `void`) * multiple return slots (flattened structs / hardware value types) * host syscalls (e.g., `GfxClear565`, `InputPadSnapshot`, `InputTouchSnapshot`) * deterministic traps (OOB bounded, invalid local, invalid call target, stack underflow)