fix(verify): prove i64 (and all-type) inlining via by-body call modeling — closes #151 (v1.1.4)#152
Merged
Merged
Conversation
…ing — closes #151 gale follow-up (#151): on v1.1.3 `inline_functions` reverted EVERY i64 inline — even a trivial `i64.add` callee — as "unproven", making the verified inliner a no-op for i64 modules and blocking the gale C↔Rust seam (z_impl_k_sem_give inlining gale_k_sem_give_decide -> i64). Ground-truthing falsified the "i64-specific" framing: an i32 control reverted too. Root cause is deeper — the Z3 translation validator modeled `call F` as an OPAQUE uninterpreted function `pure_call_F(args)`. That encoding proves two IDENTICAL calls equal (CSE / directize rely on it) but can NEVER prove a call equal to its own inlined body, so the original (with the call) and the optimized (with F inlined) never matched → every inline reverted. i32 was a no-op for the same reason; the report only saw i64 because that was the gale use case. Fix (loom-core/src/verify.rs): - When F is a pure, no-trap, leaf, straight-line, single-result callee (callee_inlinable_by_body whitelist), model `call F(args)` by SYMBOLICALLY EXECUTING F's own body on the arg bit-vectors (encode_inlinable_callee_result) instead of an opaque symbol. Original and optimized then compute the identical expression and Z3 proves them equal — the inline is KEPT WITH A REAL PROOF. Callees outside the whitelist fall back to the opaque encoding (at worst a spurious revert, never an unsound accept). A defensive width check rejects any wrong-width result. - Both the direct `call F` and the resolved `call_indirect` paths use the by-body model, keeping directize's call_indirect ⇔ call F equivalence intact (test_directize_folds_known_indirect_call). - VerificationSignatureContext now carries callee bodies (Arc, cheap to clone); inline_functions builds the context so the validator can reach them (previously it used new() with no context → opaque calls). - encode_simple_instruction: added the full i64 arithmetic / bitwise / comparison / eqz set + total width conversions (extend_i32_{s,u}, wrap_i64), mirroring the i32 arms at width 64. Previously every i64 op inside a block/loop/if fell through to an opaque BV32 — a latent verification gap beyond inlining. Soundness guard: test_inline_verifier_proves_correct_and_rejects_wrong_i64_inline locks in BOTH directions — a correct x+1 inline of add1 is proven equivalent, a wrong x+2 "inline" is rejected (counterexample). The previously-#[ignore]'d test_inline_pass_actually_inlines_i64_helper (#147 follow-up) is re-enabled and passing. 385 lib tests pass; trivial i64 + i32 repros now inline (valid output, 0 reverts); real gale wasm + full pipeline dogfood clean. The 7 pre-existing LICM/DCE integration failures (#150) are unrelated and unchanged. Closes #151. Trace: REQ-7
Trace: REQ-7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
gale #151 — verified i64 inlining
Bug-fix release v1.1.4. On v1.1.3,
loom optimize --passes inlinereverted every i64 inline — even a triviali64.addcallee — as "unproven", making the verified inliner a no-op for i64 modules and blocking the gale C↔Rust seam (z_impl_k_sem_giveinlininggale_k_sem_give_decide -> i64).Root cause (deeper than reported)
Ground-truthing falsified the "i64-specific" framing — an i32 control reverted too. The Z3 translation validator modeled
call Fas an opaque uninterpreted functionpure_call_F(args). That encoding proves two identical calls equal (CSE / directize depend on it) but can never prove a call equal to its own inlined body. So the original (with the call) and the optimized (withFinlined) never matched → every inline reverted. i32 was a no-op for the same reason; the report only noticed i64 because that was the gale use case.Fix
Fis a pure, no-trap, leaf, straight-line, single-result callee, modelcall F(args)by symbolically executing F's own body on the argument bit-vectors instead of an opaque symbol. Original and optimized then compute the identical expression → Z3 proves them equal → the inline is kept with a real proof. Callees outside the whitelist fall back to the conservative opaque encoding (at worst a spurious revert, never an unsound accept); a defensive width check rejects any wrong-width result.call Fand the resolvedcall_indirectpaths use the by-body model, keeping directize'scall_indirect ⇔ call Fequivalence intact.encode_simple_instructionnow models the full i64 arithmetic / bitwise / comparison /eqzset + total width conversions (extend_i32_{s,u},wrap_i64) — previously every i64 op inside ablock/loop/ifwas an opaque BV32 (latent gap beyond inlining).Soundness
test_inline_verifier_proves_correct_and_rejects_wrong_i64_inlinelocks in both directions: a correctx+1inline ofadd1is proven equivalent, and a deliberately wrongx+2"inline" is rejected (counterexample). The previously-#[ignore]'dtest_inline_pass_actually_inlines_i64_helper(#147 follow-up) is re-enabled and passing.Validation
Known-red gates (pre-existing, not introduced here)
Closes #151.
🤖 Generated with Claude Code