Feat/fext recursion wiring - #831
Conversation
|
Benchmark Results for unmodified programs 🚀
|
2f8ac34 to
3bf6794
Compare
There was a problem hiding this comment.
⚠️ AI-generated review. Produced by an automated agent and posted from this account — not written by hand. Findings below are leads to verify, not confirmed conclusions.
This PR wires a riscv64 FEXT accelerator into FieldExtension, bans FEXT ops under continuation (μ = 0 on the four FEXT AIRs via build_epoch_airs, used by both prove_epoch and the epoch verify path, so the ban binds the verifier), and substitutes four FieldExtension::fma calls in reconstruct_deep_composition_poly_evaluation_pair that are value-identical to the arithmetic they replace over the post-#826 fused loop, with no new proof-derived indexing or transcript movement. Two blockers: the stack cannot prove the guest it just accelerated, and much of the new API has no callers.
prover/src/continuation.rs:440 (with crypto/crypto/src/field_ext.rs:132, prover/src/tables/trace_builder.rs:3388) — the two halves contradict. Fp3Fma for riscv64 is gated on target_arch alone, no feature, so the recursion guest ELF emits fext_load/fext_fma/fext_store on every FRI query (GoldilocksExtension = Degree3GoldilocksExtensionField, so the guest Verifier picks the accelerated impl, not the software default). Yet build_traces returns Err(FextInContinuation) whenever l2g_memory_bookend is set and any FEXT op exists, and this PR forces the four FEXT AIRs empty every epoch. The recursion guest is proven only through continuation — prove_outer_and_commit (recursion_smoke_test.rs:107) calls prove_and_verify_continuation, and OuterMode is only ExecuteOnly/Prove — so once make compile-recursion-elfs picks this up, outer prove errors out. CI misses it: test_recursion_prove_empty/test_recursion_prove_1query are #[ignore]d, the merge-queue filter -E 'test(test_prove_elfs_all_instructions_64_full) | test(test_recursion_execute)' --run-ignored ignored-only matches neither, and the execute-only tier passes because the executor has no continuation-mode FEXT guard. test_recursion_prove_1query should run against a rebuilt ELF before landing, plus one of: carry field storage across epochs (FEXT_PAGE bookend under continuation), feature-gate the guest impl so the continuation-proven guest falls back to software Fp3, or hold the verifier wiring. The merge-queue -E also needs a prove-tier recursion test.
crypto/crypto/src/field_ext.rs:37 — ProdAcc/prod_acc_new/prod_acc_add/prod_acc_finish, ext_mul, the guest GuestAcc double-buffer with its H_T/H_ACC0/H_ACC1/H_ZERO handles, and ~155 lines of executor differential tests have zero callers; a workspace grep for prod_acc, ext_mul, GuestAcc outside field_ext.rs and fext_tests.rs is empty. daa03a8 wired prod_acc_* into the old per-column fold and fma into the composition-parts fold (verifier.rs:742-753 and :768 at c14ce62); 4fb663b's conflict resolution against main's fused reconstruct_deep kept only the four fma sites. Three docs now describe unshipped code: the module doc ("the generic verifier can call FieldExtension::fma(..) / ext_mul(..)"), the ProdAcc doc (removing the per-op LOAD/STORE roundtrip), and the fext_tests.rs header ("the exact syscall sequences the in-guest STARK verifier emits"). Either re-wire the resident accumulator onto the fused loop shape — needs a two-factor variant, prod_acc_add being a*b*c while the fused accumulations are 2-factor — or delete the machinery, the handles, and fext_prod_acc_chain_matches_reference/fext_ext_mul_via_unwritten_zero_matches_reference, and fix the docs. Unreferenced guest code with cross-call handle-state invariants (two live GuestAccs corrupt each other) invites a bad reuse.
prover/src/tests/fext_page_tests.rs:257 — the last-row half of fext_page_rejects_non_bit_sel_same covers the exact hole the new constraint closes (idx 6 is exempt there) but never evaluates constraint 11: it reads the tampered cell back and asserts sel * (1 - sel) != 0, i.e. that (-1)·2 ≠ 0, true regardless of the AIR. Changing idx 11 to emit_base_rows(.., RowDomain::except_last(1), ..) would reopen the hole with this test still green. The binding property is metadata, not evaluation (emit_is_bit → emit_base → end_exemptions == 0), so assert assert_eq!(FextPageConstraints.meta()[11].end_exemptions, 0), or build the wraparound frame (last, 0) by hand as fext_page_forbid_empty_rejects_active_row does and check base[11] != 0. The row-0 half (sel_same = 2) is fine.
Nits:
- prover/src/test_utils.rs:1016 —
ForbidEmptynames the opposite of its behaviour (its own doc: "forcing the table to be empty");ForceEmptyis accurate. - prover/src/test_utils.rs:1024 —
let mu0_idx = self.base.meta().len();insideevalre-runs the base body through aMetaBuilderand allocates + sorts aVec<ConstraintMeta>per evaluation, againstmeta()'s "runs once at setup — never on the per-row prover path" contract; the index is a per-table constant, pass it from thecreate_*_forbiddenfactories. - Only
create_fext_page_air_forbiddentests thatμ = 0fires on an active row;create_fext_load/fma/store_air_forbiddenpassfext_*_cols::MUunexercised, so a wrongmu_colafter a column reshuffle silently no-ops the ban while positive continuation tests stay green (μ = 0 holds trivially on empty tables). - crypto/stark/src/verifier.rs:947 —
h_sum += h_i_upsilon * gammais ext × ext in the same per-query function and still on the software path, while the comment two hunks above says ext × ext routes through the accelerator; convert it or note why not.
No description provided.