Skip to content

synth's requirements for a Z3 replacement — QF_BV one-shot validation; the static-link build is the real pain, not the solver #246

Description

@avrabe

Filed from synth (issue-hunt loop, not gale) in response to the "should we replace Z3?" question. These are synth's concrete requirements so an ecosystem solver decision — which loom is already partly making (#231 forwards Z3-discharged wsc.facts invariants to synth; #169 tracks Rocq gating) — is grounded in what synth actually needs.

TL;DR

synth's Z3 usage is the simplest possible SMT fragment, and the pain is the build, not the solver. Any decision should optimize for hermetic/reproducible builds and CI stability, because the solver capability is a non-issue at synth's fragment.

What synth uses Z3 for

synth-verify is a translation validator: for each lowering it asserts wasm_op_semantics ≠ arm_instruction_sequence_semantics and expects UNSAT (i.e. they're equivalent). Concretely:

  • Theory: QF_BV only (quantifier-free bitvectors). Active op surface in the validator: bvadd/bvsub/bvmul/bvudiv, bvand/bvor/bvxor, bvshl/bvlshr/bvashr, bvrotr, extract/concat, zero_ext/sign_ext, and signed+unsigned comparisons (bvult/bvule/bvugt/bvuge/bvslt/bvsgt). 32- and 64-bit widths.
  • Query model: one-shot check-sat + a counterexample model. No quantifiers, no arrays, no floating-point, no Optimize, no incremental push/pop/assumptions (verified by grep over synth-verify/src).
  • Pulled as the z3 crate 0.19 with features = ["static-link-z3"] — i.e. it compiles Z3 from C++ source via z3-sys's build.rs.

Why replace it (the actual pain)

It is a build/hermeticity problem, not a solver-quality problem:

  • z3-sys build.rs breaks local cargo test --workspace (panics around build.rs:374); synth's own docs tell contributors to --exclude synth-verify locally.
  • CI's Z3 Verification job flakes and the "comprehensive verification" step runs ~16 min and intermittently times out (seen this week on a RISC-V-only PR that can't even affect ARM semantics — pure infra flake, forced a re-run).
  • The from-source C++ compile is the long pole and the fragile part. The solver answers synth's QF_BV queries instantly and correctly; z3-the-build is what hurts.

Requirements for a replacement (synth side)

MUST

  1. Decide QF_BV check-sat with a returned model (counterexample) for the op set above, 32/64-bit.
  2. Hermetic, reproducible build — no from-source C++ compile in the critical path (prebuilt/vendored binary, or pure-Rust, or shell-out to an already-present solver binary). This is the whole point.
  3. Fast enough to gate CI without multi-minute timeouts at synth's query sizes (per-op equivalence checks are tiny).
  4. Permissive license (Z3 is MIT today — don't regress).
  5. Stable, thin Rust API (build a BV term graph, assert, check, read model).

NICE

  • Solver-agnostic interface (so the choice isn't a one-way door).
  • Deterministic results across platforms (macOS/Linux CI parity).

NOT required today (don't over-buy): quantifiers, arrays, floating-point, optimization, incremental solving. One flag for the future: the #369 FPU work (hard-float f32/f64) may eventually want QF_FP verification — worth keeping on the radar, but it is not a current dependency and shouldn't gate the decision.

Directions (synth has no strong preference — loom decides the ecosystem story)

Coordination ask

Whatever loom picks for discharging wsc.facts invariants (#231), synth would prefer the same solver/interface so the ecosystem has one solver story and one build story. If loom standardizes on an SMT-LIB2-text contract, synth can adopt it directly for translation validation.

Refs: loom #231, loom #169; synth #242 (VCR North Star), synth #73, synth #369.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions