fix(decoder): float-typed global accesses loud-skip — the last silent float path (GI-FPU-001, #369)#648
Merged
Merged
Conversation
…inits were silently zeroed (GI-FPU-001, #369) GI-FPU-001's decode-level loud-skip (PR #371) and the single-function bail (#554, PR #556) cover every scalar float OP — but global.get/ global.set are type-agnostic ops that decode fine, and a float-typed global's f32.const/f64.const initializer is never captured (init_i32 only decodes a leading i32.const), so its slot is silently zeroed. Verified on main: (global f32 (f32.const 2.5)) + global.get compiled rc=0 on BOTH ARM paths with the 0x40200000 bit pattern absent from the output — the read returns 0.0, wasmtime returns 2.5. riscv/aarch64 were already loud only by accident (GlobalGet itself is unsupported there). Fix: both decode entry points (decode_wasm_module + the lighter decode_wasm_functions) track f32/f64-typed globals in the FULL index space (imported globals first) and flag any function whose GlobalGet/ GlobalSet touches one — routing it through the existing loud-skip / honest-bail machinery. The two CLI diagnostics now also name GI-FPU-001. Float locals/params remain compilable on purpose: without float ops (all flagged) they are pure 4/8-byte bit moves — bit-correct, and loom output may declare unused float locals. Oracles: new decoder tests (float-global get/set flag + GI-FPU-001 in the reason; imported-global index shift; i32 globals unflagged) — red on origin/main; frozen anchors 10/10 byte-identical; workspace tests rc=0; fmt + clippy -D warnings clean. Refs #369, #619. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 8, 2026
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
avrabe
added a commit
that referenced
this pull request
Jul 8, 2026
…age — decoder captures both words, startup materializes the R9 table (#652) The decoder's `init_i32` captured only a leading `i32.const`; an i64 global's init decoded to `None` and every consumer's `unwrap_or(0)` silently ZEROED the slot. #645 fixed the get/set PAIR lowering and the width-aware slot layout, but a nonzero `(global (mut i64) (i64.const X))` read before any set still returned garbage — the init existed nowhere in the output. Decoder: `WasmGlobal.init_i32` is RENAMED to `init: Option<GlobalInit>` (I32/I64) so no consumer survives by wildcard. f32/f64 inits stay `None` on purpose — float global access is the GI-FPU-001 (#369/#648) loud-skip lane and capturing bits here must not quietly unskip it. Consumers audited (all of them, post-rename): - `identify_stack_pointer_global`: matches `GlobalInit::I32` only (an SP is an i32 address; i64 inits are data, never promoted). - `--native-pointer-abi` `__synth_globals` region: stays 4-byte i32 slots; wide globals are already REFUSED up front (#643 bail — the decline-loudly arm), so only I32 inits can land. Byte-identical for i32-global modules (verified against origin/main). - Self-contained Cortex-M image (the #649 repro shape): globals were never materialized AT ALL — R9 was never even initialized. Now `generate_minimal_startup` gets the table words (#643 summed-width layout, i64 = two words lo-first), points R9 at `0x2000_0000 + linmem_size`, and stores every word before calling the user function; RAM sizing + the NoBits region cover the table. Both the all-exports and single-function builders. Tables past the STR.W #imm12 range decline loudly. No globals => the historical 28-byte startup blob and the whole ELF stay BYTE-IDENTICAL (verified). Red -> green: scripts/repro/i64_global_init_649_differential.py runs the default cortex-m4f image's REAL reset path in unicorn (artifact-derived, nothing fabricated), then the stateful export sequence vs one wasmtime instance. origin/main: FAIL (4 divergences — init reads return vector- table garbage). This branch: PASS (init reads, i32-after-i64 offset canary, #645 set-then-get pin, control). Gates: frozen anchors 10/10, #643 oracle PASS, cargo test --workspace green, fmt + clippy --workspace -D warnings clean. Fixes #649. Refs #645, #643, #648, #369. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
avrabe
added a commit
that referenced
this pull request
Jul 8, 2026
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 the last silent-float gap of GI-FPU-001 (#369; the #619 frontier survey). jess tracks the arc as AFD-024.
Where GI-FPU-001 stood on main (verified per backend, v0.33.1)
PR #371 (decode loud-skip) + PR #556 (#554 single-function bail) already made every scalar float op loud. Verified on origin/main with a 12-module matrix (arith f32/f64, compares, trunc/convert, load/store, const, both reinterprets, float local, float global) × (ARM default, ARM
--relocatable/direct,-b riscv,-b aarch64):warning: skipping function 'f': contains an unsupported operator (F32Add) …/ single-functionError: … refusing to emit a silent miscompile.-t cortex-m4f)GlobalGet(0) … (GI-FPU-001)--relocatable(direct)-b riscv/-b aarch64GlobalGetitself is unsupported there)Root cause:
global.get/global.setare type-agnostic ops that decode fine, butinit_i32only captures a leadingi32.const— anf32.const/f64.constinitializer recordsNoneand the slot is zeroed (init_i32.unwrap_or(0)).Fix
Both decode entry points (
decode_wasm_module+ the lighterdecode_wasm_functions— the CLI compiles through both) track f32/f64-typed globals in the full index space (imported globals first) and flag any function whoseGlobalGet/GlobalSettouches one, routing it through the existing #371/#556 loud-skip / honest-bail machinery. The two CLI diagnostics now also name GI-FPU-001 explicitly.Oracles
frozen_codegen_bytes); the only float fixture in-tree is the -b aarch64 silently miscompiles f32 ops (emits 'mov w0,w1' / drops constants) instead of erroring like other unsupported milestone-1 ops #554 loud-skip repro.cargo test --workspacerc=0 (106 suites, 0 failures),cargo fmt --checkclean,cargo clippy --workspace --all-targets -- -D warningsrc=0.Sibling finding (out of scope, filing separately)
(global i64 (i64.const 4886718345))+global.getis also silently zeroed — sameinit_i32-only mechanism, integer class (#643 landed pair get/set but not non-i32 initializers). Tracked in a follow-up issue.What GI-FPU-002 (real VFP, #619) will need
f32.*/f64.*arms inconvert_operator(+WasmOpfloat variants beyond the dead prototypes), floatconstpayload capture, and float global-init capture (un-flagging the accesses this PR flags).select_with_stack(S0–S15/D0–D15 register class alongside the integer allocator; the existingf32_vfp_encoding_test/f64_vfp_encoding_testencoder is ready), FPU gating per target (m4f=fpv4-sp, m7=fpv5-sp, m7dp=fpv5-d16; honest reject on M0/M3 stays).Tag_FP_arch/Tag_ABI_VFP_argsbuild attributes so the mode is verifiable from the artifact (arm backend uses soft-float for all f32/f64 — hardware FPU never used (cortex-m4f/m7/m7dp identical); blocks REQ-PIX-001 hard-float #369's ask).mps2-an386hard-float semihosting differential (Make VFP f32/f64 reachable on the thumb-2 CLI path (Cortex-M4F) — encoder exists but decoder drops float ops; the M4F float frontier vs the new Cranelift arm32 backend #619 offers the probe) + falcon-v1.56 numeric gate.Refs #369, #619, #554.
🤖 Generated with Claude Code