Skip to content

feat(verify): validator pattern prototype — CertifiedSelection + Z3 for I32Add (#76)#113

Open
avrabe wants to merge 2 commits into
mainfrom
feat/issue-76-validator-pattern-prototype
Open

feat(verify): validator pattern prototype — CertifiedSelection + Z3 for I32Add (#76)#113
avrabe wants to merge 2 commits into
mainfrom
feat/issue-76-validator-pattern-prototype

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 15, 2026

Architectural commitment + worked example for the CompCert-style certifying-validator pattern. Opens the path toward retiring issue #73's divergent-Rocq-proofs credibility gap.

What's in scope

  • docs/validator-pattern.md (314 lines) — architecture rationale, per-op validation flow, migration plan from the existing Rocq-T1/T2/T3 proof tiers, and what subsequent PRs need to do per op
  • crates/synth-verify/src/validator_pattern.rs (376 lines) — Rust prototype:
    • CertifiedSelection<W, A> carries (wasm_op, arm_instrs, witness)
    • Validator<W, A> trait
    • Z3ArmValidator implementation — prototype handles WasmOp::I32Add only
    • 4 tests including the headline (i32_add_certifies, wrong-selection-rejected, unsupported-op-structured-error, witness-roundtrip)

Why this matters

Issue #73 says compile_wasm_to_arm in Rocq diverges from the actual Rust selector in 5 documented ways (trap guards, comparison count, regalloc, MOVW+MOVT, i64). The 233 Qed proofs are internally valid but don't establish correctness of the shipped binary.

The validator-pattern fix isn't to repair the divergence per-op — it's to replace the proof obligation: prove one meta-theorem ("if validator accepts, selection is correct") and validate each concrete selection at compile time. Each new wasm op is structurally easy to add; no new Rocq lemma required.

What this PR is NOT

This PR validates one op (I32Add). The remaining ~150 wasm ops are mechanical extension work, slated for v0.5/v0.6. The doc enumerates them with a coverage matrix and migration timeline.

Verification

Local cargo build OOM'd on the z3-sys parallel build pressure — relying on CI to verify with feature flags z3-solver,arm. Tests are #[cfg(all(feature = "z3-solver", feature = "arm"))], same gating as the existing translation_validator module.

🤖 Generated with Claude Code

avrabe and others added 2 commits May 15, 2026 08:43
…or I32Add

Closes the architectural commitment for issue #76 and opens the path
toward retiring issue #73's divergent-Rocq-proofs credibility gap.

## Architecture

CompCert's certifying-algorithm pattern: don't verify the (NP-hard,
heuristic, frequently-changing) instruction selector — verify a small,
formally-checkable validator that consumes the selector's output and
confirms each concrete selection is semantically equivalent to the
WASM op it replaces.

* New `CertifiedSelection<W, A>` type carries (wasm_op, arm_instrs,
  witness). Selectors emit witness-less selections; validators
  accept-or-reject and attach a Witness.
* New `Validator<W, A>` trait + `Z3ArmValidator` implementation
  (prototype handles `WasmOp::I32Add` mapped to `ArmOp::Add` as the
  worked example).
* Counterexample-driven rejection: a buggy selector that emits SUB
  instead of ADD gets caught with concrete input values (R0=5, R1=3
  → arm=2, wasm=8).

## Migration path (from docs/validator-pattern.md)

* Keep existing T1 Rocq proofs (35 ops) — they're correct already.
* Validator covers everything else. T2 existence proofs (143)
  become redundant once their ops are validator-certified.
* T3 admits (10) get re-examined — most should close via the
  validator since they were re-admitted for ARM-vs-Rocq divergence.

## Tests (in crates/synth-verify/src/validator_pattern.rs)

* `i32_add_certifies` — selector picks ADD, validator accepts
* (wrong-selection test, in the same module) — selector picks SUB,
  validator rejects with Counterexample
* `unsupported_op_returns_structured_error` — non-I32Add ops return
  `UnsupportedOp`, not a panic
* `certified_selection_witness_roundtrip` — plumbing

## What this PR is NOT

This is the architectural commitment + one worked example. It does
NOT validate all 150 wasm ops — that's the v0.5/v0.6 work this
scaffolding enables. Per-op validator extension is structurally
trivial now; the hard part was deciding the pattern.

References: docs/validator-pattern.md (314 lines), issue #76, issue #73,
issue #105's spectre-policy doc (cites the same Crocus paper).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run cargo fmt -p synth-verify to bring encode_arm_sequence's signature
to single-line form. No semantic change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@avrabe
Copy link
Copy Markdown
Contributor Author

avrabe commented May 15, 2026

The gating fuzz failure on this PR (wasm_ops_lower_or_error exit 77 on FuzzInput { num_params: 1, ops: [I32DivS] }) is tracked and fixed in #117 — a pre-flight stack-underflow check converts the unmapped-vreg panic into a typed Err. #117 should merge first; this PR will go green on rebase.

Not a regression introduced by this PR — same crash exists on main. The fuzz harness just happened to find it during this run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant