synth_verify/lib.rs
1//! Formal Verification for Synth Compiler
2//!
3//! This crate provides SMT-based translation validation and property-based testing
4//! to formally verify the correctness of WebAssembly-to-native synthesis.
5//!
6//! # Architecture
7//!
8//! The verification system proves that synthesized native code has
9//! semantically equivalent behavior to the input WASM code, discharging the
10//! QF_BV queries through a thin solver trait ([`solver::BvSolver`], #553):
11//!
12//! - **Default engine:** [`ordeal`] — pure Rust, certificate-checked
13//! (every `Unsat` verdict carries an LRAT proof validated by the trusted
14//! `ordeal-lrat` checker). No C++ toolchain required.
15//! - **Differential oracle** (feature `z3-solver`): Z3, the former engine.
16//! With both backends compiled in and `SYNTH_SOLVER_DIFF=1`, every query
17//! runs through both — a verdict disagreement is a hard error; an ordeal
18//! `Unknown` falls through to Z3's verdict.
19//!
20//! ## Backend-Agnostic Traits
21//!
22//! `SourceSemantics` and `TargetSemantics` traits allow any backend to provide
23//! SMT semantics. The ARM semantics are one implementation, behind the `arm`
24//! feature. All semantics encode into the solver-agnostic [`term::BV`] /
25//! [`term::Bool`] terms.
26//!
27//! ## Translation Validation
28//!
29//! For each synthesis rule WASM → target, we construct SMT formulas:
30//! - φ_wasm: Semantics of WASM operations
31//! - φ_target: Semantics of generated target operations
32//! - Prove: ∀inputs. φ_wasm(inputs) ⟺ φ_target(inputs)
33
34// Solver-agnostic terms + the thin solver trait (always available)
35pub mod solver;
36pub mod term;
37
38// Trap-preservation obligations over `ordeal::trap` (VCR-VER-002, #166): maps
39// WASM partial ops (div/rem, load/store, call_indirect, unreachable,
40// float→int trunc) to trap
41// conditions and gates a lowering on preserving them. Backend-agnostic (builds
42// on `term`), so always available like `wasm_semantics`.
43pub mod trap;
44
45// Static-data addressing validation (VCR-VER-003, #777 / #757): per-compilation
46// concrete byte-equality that every static-data reloc resolves to the
47// runtime-correct byte (active segments applied in declaration order,
48// later-wins). Catches the overlapping-segment wrong-segment miscompile at
49// compile time. Backend-agnostic concrete checking, so always available.
50pub mod addr;
51
52// Verification traits (always available)
53pub mod traits;
54
55// WASM semantics — source language for all backends (always available)
56pub mod wasm_semantics;
57
58// Proof-carrying specialization (VCR-PERF-002 / #494 Phase 2): value-range
59// facts ⇒ dead conditional-branch elision, each site behind a per-elision
60// ordeal obligation. Backend-agnostic (rewrites the WasmOp stream), so it is
61// always available like `wasm_semantics`.
62pub mod fact_spec;
63
64// ARM semantics (behind the arm feature)
65#[cfg(feature = "arm")]
66pub mod arm_semantics;
67
68// Translation validator (requires arm for the existing concrete implementation)
69#[cfg(feature = "arm")]
70pub mod translation_validator;
71
72// Validator-pattern prototype (issue #76 — CompCert-style certifying
73// validator scaffolding; see docs/validator-pattern.md).
74#[cfg(feature = "arm")]
75pub mod validator_pattern;
76
77// Expansion-level certifying validation for the i64 pseudo-ops (#667 move 2):
78// decodes the SHIPPED encoder's emitted Thumb-2 bytes and proves them
79// equivalent to the WASM op — see docs/validator-pattern.md.
80#[cfg(feature = "arm")]
81pub mod expansion_validator;
82
83// Property-based testing (requires arm: exercises the ARM synthesis rules)
84#[cfg(feature = "arm")]
85pub mod properties;
86
87#[cfg(feature = "arm")]
88pub use properties::CompilerProperties;
89
90pub use addr::{
91 AddrMismatch, DataSegment, RelocResolution, Verdict as AddrVerdict, resolve_owner,
92 validate_reloc_resolutions,
93};
94#[cfg(feature = "arm")]
95pub use arm_semantics::{ArmSemantics, ArmState};
96#[cfg(feature = "arm")]
97pub use expansion_validator::{
98 ExpansionError, ExpansionWitness, covered_i64_pseudo_selections, validate_expansion,
99};
100pub use fact_spec::{FactSpecResult, specialize_function};
101pub use solver::{BvSolver, CheckOutcome, OrdealSolver, new_solver};
102pub use term::{BV, Bool};
103#[cfg(feature = "arm")]
104pub use translation_validator::{
105 CallIndirectSpec, TranslationValidator, ValidationResult, VerificationError,
106};
107#[cfg(feature = "arm")]
108pub use validator_pattern::{
109 CertifiedSelection, SolverResultKind, ValidationError as PatternValidationError, Validator,
110 Witness, Z3ArmValidator,
111};
112pub use wasm_semantics::WasmSemantics;
113
114/// Run verification operations in a configured context.
115///
116/// With the default (ordeal) engine this is a plain call — the pure-Rust
117/// solver needs no global context. With the `z3-solver` feature compiled in,
118/// it additionally configures the Z3 thread-local context (30-second timeout,
119/// model generation) so the differential oracle is ready.
120pub fn with_verification_context<F, R>(f: F) -> R
121where
122 F: FnOnce() -> R + Send + Sync,
123 R: Send + Sync,
124{
125 #[cfg(feature = "z3-solver")]
126 {
127 with_z3_context(f)
128 }
129 #[cfg(not(feature = "z3-solver"))]
130 {
131 f()
132 }
133}
134
135/// Run verification operations with a configured Z3 context.
136///
137/// Z3 0.19 uses thread-local context — this function configures it
138/// with a 30-second timeout and model generation enabled.
139#[cfg(feature = "z3-solver")]
140pub fn with_z3_context<F, R>(f: F) -> R
141where
142 F: FnOnce() -> R + Send + Sync,
143 R: Send + Sync,
144{
145 let mut cfg = z3::Config::new();
146 cfg.set_timeout_msec(30000); // 30 second timeout
147 cfg.set_model_generation(true);
148 z3::with_z3_config(&cfg, f)
149}
150
151/// Create a Z3 solver with default configuration (differential-oracle use)
152#[cfg(feature = "z3-solver")]
153pub fn create_solver() -> z3::Solver {
154 z3::Solver::new()
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_default_solver_decides() {
163 with_verification_context(|| {
164 let mut solver = new_solver();
165 let x = BV::new_const("x", 32);
166 solver.assert(&x.eq(&x).not());
167 assert_eq!(solver.check(), CheckOutcome::Unsat);
168 });
169 }
170
171 #[cfg(feature = "z3-solver")]
172 #[test]
173 fn test_z3_context_creation() {
174 with_z3_context(|| {
175 let solver = create_solver();
176 assert_eq!(solver.check(), z3::SatResult::Sat);
177 });
178 }
179}