feat(codegen): heterogeneous funcref tables — runtime type check via the type-id sidecar (#676) - #696
Merged
Merged
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
avrabe
force-pushed
the
feat/676-heterogeneous-tables
branch
from
July 10, 2026 16:58
618695a to
e52957b
Compare
…the type-id sidecar (#676) A HETEROGENEOUS funcref table (mixed signatures — falcon's fused 41-slot dispatch table) can never satisfy the closed-world type check, so every call_indirect through it loud-declined (20 falcon funcs). WASM Core §4.4.8 makes the mismatch a RUNTIME trap, so the sound lowering is the runtime check itself: the object now carries a type-id sidecar (.synth.table_type_ids — one LE u32 STRUCTURAL signature class id per slot, region order; structurally-equal types share one dense 1-based id, the meld 31-decls/25-distinct shape; id 0 reserved for null slots) which the extended R11 layout contract places at R11 + sum(all table sizes)*4, mirroring the pointer region slot for slot. The dispatch inserts, between the #642 bounds guard and the pointer load, on both Thumb-2 and A32: mov ip, idx, lsl #2 ; add ip, r11, ip ; ldr ip, [ip, #type_off] cmp ip, #expected_class_id ; beq ok ; udf The compare subsumes the #664 null trap (id 0 never equals an expected id >= 1), so heterogeneous dispatches emit null_check=false. Encoding ranges decline loudly (sidecar offset > LDR imm12, class id > 255). Homogeneous tables emit type_check=None + no sidecar section — bytes identical BY CONSTRUCTION (the #650 offset-0 / #664 null_check=false trick): whole-ELF cmp verified against origin/main on the #642/#650/#664 fixtures x cortex-m3/r5, frozen anchors 10/10, workspace green. The estimator is untouched (CallIndirect is direct-selector-only, excluded from the #511 agreement oracle). New CI-gated differential (call_indirect_676_differential.py, Thumb-2 + A32): mixed 5-slot table (two classes interleaved + structural-dup type + nulls) — matching-class calls equal wasmtime, wrong-class ("indirect call type mismatch"), null and OOB indices all stop at a UDF; wasmtime's trap REASONS are asserted per category. Non-vacuous red: a build without the check CALLS the wrong-typed function and returns a wrong value. Red at compile on origin/main (capability upgrade). Object-level contract locked in cargo CI (heterogeneous_table_676.rs: sidecar ids [1,2,1,0,0] + no-sidecar for the homogeneous fixtures). Lineage: #642 guards (#646), #650 multi-table (#653), #664 null slots (#669) — this closes the terminal layer of falcon's call_indirect story. Closes #676 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
avrabe
force-pushed
the
feat/676-heterogeneous-tables
branch
from
July 10, 2026 17:26
e52957b to
1673483
Compare
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.
Closes #676 — the terminal layer of falcon's
call_indirectstory, after #642 guards (#646), #650 multi-table (#653) and #664 null slots (#669).What
A heterogeneous funcref table (mixed signatures — falcon's fused 41-slot dispatch table) can never satisfy the closed-world type check, so all 20 dispatch functions loud-declined. WASM Core §4.4.8 makes a
call_indirecttype mismatch a runtime trap, so the sound lowering is the runtime check itself:.synth.table_type_ids— one LE u32 structural signature class id per table slot, region order. Structurally-equal duplicate types share one dense 1-based id (the meld 31-decls/25-distinct shape); id 0 is reserved for null slots. The extended R11 layout contract (documented onCallIndirectGuards) places it atR11 + sum(all table sizes) * 4, mirroring the pointer region slot for slot; the runtime/harness copies the section's words verbatim — it never re-derives ids.null_check: false. Encoding ranges decline loudly (sidecar offset > LDR imm12 4095, class id > 255).type_check: Noneemits zero bytes (the arm backend links only table 0 (R11); call_indirect through table index ≥1 loud-declines — blocks multi-table fused components (falcon: 20 funcs) #650 offset-0 / call_indirect on a table with null (uninitialized) funcref slots loud-declines — needs sound null-slot lowering (falcon: 20 funcs, the residual after #650) #664null_check: falsetrick) and no sidecar section is emitted.Oracles
call_indirect_676_differential.py(Thumb-2cortex-m3+ A32cortex-r5): mixed 5-slot table, two signature classes interleaved + a structural-duplicate type + null slots. 28/28 cases: matching-class calls equal wasmtime; wrong-class, null and OOB indices all stop at a UDF; wasmtime's trap reasons are asserted per category (indirect call type mismatch/uninitialized element/undefined element). Non-vacuous red: a build without the check calls the wrong-typed function and returns a wrong value.SIDECAR MISSING/SYMBOL MISSING→ORACLE: FAIL.cmpof the thumb-2: call_indirect emits no bounds-check and no type-check — OOB/wrong-type index does an uncontrolled indirect branch instead of trapping (WASM 4.4.8) #642/arm backend links only table 0 (R11); call_indirect through table index ≥1 loud-declines — blocks multi-table fused components (falcon: 20 funcs) #650/call_indirect on a table with null (uninitialized) funcref slots loud-declines — needs sound null-slot lowering (falcon: 20 funcs, the residual after #650) #664 fixtures (cortex-m3 + cortex-r5,--relocatable) against an origin/main-built binary — 6/6 IDENTICAL. Frozen anchors 10/10. Encoder unit tests pin the exact splice (shared prefix/suffix byte-equal,None≡ pre-call_indirect into a HETEROGENEOUS funcref table (mixed signatures) loud-declines — needs a runtime type-id check; the terminal layer of falcon's dispatch (20 funcs) #676 bytes).heterogeneous_table_676.rs): heterogeneous fixture compiles with sidecar ids[1, 2, 1, 0, 0](structural dedup + null id 0) on both targets; the three homogeneous fixtures emit no sidecar section.CallIndirectis direct-selector-only, excluded from the test(vcr-oracle): estimator↔encoder agreement oracle for the optimized path (#498, #242) #511 estimator↔encoder agreement oracle's covered set; no estimator code changed.cargo test --workspacegreen,cargo fmt --check+cargo clippy --workspace --all-targets -- -D warningsclean; thumb-2: call_indirect emits no bounds-check and no type-check — OOB/wrong-type index does an uncontrolled indirect branch instead of trapping (WASM 4.4.8) #642/arm backend links only table 0 (R11); call_indirect through table index ≥1 loud-declines — blocks multi-table fused components (falcon: 20 funcs) #650/call_indirect on a table with null (uninitialized) funcref slots loud-declines — needs sound null-slot lowering (falcon: 20 funcs, the residual after #650) #664 differentials re-run green on this commit.🤖 Generated with Claude Code