Repository navigation
#369 marked closed, but the real falcon-flight-v1.123 fused core still skips 26/156 on --relocatable cortex-m7dp (incl. run-stabilization); trunc_sat unsupported + a pressure-dependent 'integer popped f32' class #782
Description
Activity
- added 4 commits that reference this issue
on Jul 17, 2026 Shipped in v0.48.0. Both parts closed:
- #782a (feat(#782a): implement the WASM trunc_sat family — nontrapping saturating float→int (i32 forms on ARM32, all 8 on aarch64) #803): the
trunc_satfamily un-dropped — i32 forms on ARM32 (bare RZ VCVT), all 8 forms on aarch64 (FCVTZS/FCVTZU); full NaN→0 / ±inf / boundary table verified vs wasmtime. - #782b (fix(#782b): float select + explicit float return — the 'integer popped f32' class is not pressure, it's the clamp idiom (+2 adversarial finds) #801): the 'integer popped an f32' skip class was NOT register pressure (disproven by the real falcon bytes) — it was two missing lowerings (untyped f32
select, explicit floatreturn), now shipped. Getting the real bytes also caught + fixed two soundness bugs red-first: a wide i64-selecthi-half silent miscompile on every target, and a hard-float signature-only ABI hole.
Remaining (honest residual, NOT a regression): ARM32 i64
trunc_satforms LOUD-DECLINE by name (the i32 forms and all aarch64 forms are complete). Tracked for v0.49 L1 (falcon-skip-to-zero). If falcon still skips a specific op, please drop the shape and it becomes the next lane.- #782a (feat(#782a): implement the WASM trunc_sat family — nontrapping saturating float→int (i32 forms on ARM32, all 8 on aarch64) #803): the
Re-verified against the real build on synth v0.49.0 (meld v0.41.3, falcon-flight-v1.124 sha
c8db2dfd…,-t cortex-m7dp --relocatable --native-pointer-abi): 26 → 16 skips, andrun-stabilizationnow lowers. The f32-select+ float-returnfixes cleared the whole "integer popped f32" class — that's the flight-control step compiling on the M7. Big step, thank you. Dropping the exact residual shapes for the falcon-skip-to-zero lane:Float (6)
- 4× GI-FPU-002 VFP register-file exhaustion — 2× f32 (S0–S15 all live), 2× f64 (D0–D7 all live). "no VFP spilling yet" — deep float expressions. Biggest remaining class.
- 2× GI-FPU-001
i64.reinterpret_f64/f64.reinterpret_i64unsupported (the f64↔i64 bitcast; VMOV D-reg ↔ GPR pair). trunc_sat is fixed; this reinterpret pair remains.
i64/f64 ABI + regalloc (7)
- 2× arm: i64 binop with an i64 param silently miscompiles — param read from wrong registers (both paths) #518 i64/f64 param in a frame-backing fn (param_slots drops the high half)
- 2× arm: functions needing the AAPCS stack-arg path (>8 scalar params, or any 64-bit stack param) are skipped, not lowered #503 call arg 4 on the stack is i64/f64 (only i32 stack args supported)
- 1× register exhaustion: no free callee-saved to hold a call result while reloading a preserved param
- 1× register exhaustion: i64 spill-slot pool exhausted
- 1× VCR-RA-003 register-allocation validation FAILED —
JoinValueNotAvailable { reg: R8, join_block: 30 }(your verified-RA gate flagging a compiler bug, not a program error — refused to emit rather than miscompile; Optimized path: small leaf functions clobber callee-saved r4-r8 without a saving prologue (latent ABI bug) #490/v0.11.40 arm: spill-slot collision miscompiles dissolved k_mutex_unlock — live mutex-ptr arg clobbered by unpend() result, lock_count=0 store misses (silicon deadlock) #331/join-availability)
Addressing (3)
- 3× dissolved --relocatable objects carry full wasm linmem (64KB .data) + absolute MOVW relocs — MCU-unshippable + link-fragile (gale mutex silicon fault) #345
LdrSymliteral pool out of range —imm12= 9244 / 5328 / 10196 > 4095 for__synth_globals(large-module global offsets in the fused core).
The RA gate catching
JoinValueNotAvailableon the real bytes is exactly the red-first value you built it for. Same offer as before — happy to hand you any specific function's op stream. Repro is deterministic (public falcon-v1.124 asset + meld v0.41.3).Minimal repro for the #345
LdrSymliteral-pool-out-of-range shape (3 of the remaining 14 falcon skips). It's a function-size effect, not a global-count one — a large function places the literal pool holding the__synth_globalsaddress >4095 B from theldr, so theimm12can't reach it:(module (global $g (mut i32) (i32.const 7)) (func (export "big") (result i32) (local $x i32) (local.set $x (global.get $g)) ;; the __synth_globals literal load, near the start ;; ~3000 straight-line ops to push the pool past 4 KB of machine code: (local.set $x (i32.add (local.get $x) (i32.const 1))) ;; ... repeat ~3000x ... (local.get $x)))
synth compile big.wasm -t cortex-m7dp --relocatable → skipping 'big': LdrSym literal pool out of range (#345): imm12=12012 > 4095 for symbol __synth_globals(Confirmed on v0.49.0. Generator: 3000
(i32.add … (i32.const i))between theglobal.getand the return.)Why falcon hits it: the 3 affected functions are its large ones (the fused control/estimator bodies), where a single monolithic literal pool at the function end is out of
imm12range from an early symbol load.Fix directions: (a) emit
__synth_globals(and other far symbols) as amovw/movtimmediate pair instead of a pc-relativeldr— no pool, no range limit; or (b) constant-island placement — flush the literal pool mid-function whenever the pendingldr→pool distance approaches the 4095 limit (the standard LLVM/GAS large-function approach). (a) is simpler and pool-free.Deterministic + tiny; happy to adjust the shape if you want a specific size boundary.
- added 5 commits that reference this issue
on Jul 29, 2026
Thanks for the big float-completion push (v0.41–v0.45) — the synthetic differentials (156/156 f32, 126/126 f64) are great. But verifying against the real shipped falcon core (relay invited this on jess#144) shows #369 is not complete for falcon yet. Filing precisely, evidence-first.
Real-build measurement
falcon-flight-v1.123.wasm(relay release asset, shae4622a12…) →meld v0.41.1 fuse --reproducible→ the fused Core module.synth v0.45.1,compile -t cortex-m7dp --relocatable --native-pointer-abi --shadow-stack-size 4096.--relocatablewithout--native-pointer-abi: 24/183.) Identical on the pre-loom fused module, so it is not a loom/meld artifact.Breakdown of the 26
GI-FPU-002: an integer operation popped an f32 (VFP) stack value — invalid wasm or an unlowered float op reached the integer pathrun-stabilization(the flight-control step). See caveat below.GI-FPU-001: unsupported operatori32.trunc_sat_f32_s,i32.trunc_sat_f64_s,f64.reinterpret_i64GI-FPU-002: VFP register file exhausted(S0–S15 / D0–D7 all live, no VFP spilling)#503(i64/f64 stack arg)#518(i64/f64 param in a frame-backing fn)#345(LdrSymliteral pool out of range, imm12 > 4095 for__synth_globals)Confirmed minimal repro (GI-FPU-001,
trunc_sat)The trapping
trunc_f32_svariants lower, but the saturatingtrunc_sat_*variants don't. Rust emitstrunc_satforascasts (default since thesaturating-float-to-intstabilization), so falcon hits these (8 occurrences in v1.123: 7×i32.trunc_sat_f64_s, 1×i32.trunc_sat_f32_s). On VFP this is a directVCVT(ArmVCVT.S32.F32already saturates), so it should be cheap to add.Honest caveat on the dominant class
I could not minimize the 12× "integer popped f32" yet: the obvious candidate (
i32.reinterpret_f32, 97 uses in the module) lowers fine in isolation:So it's pattern/register-pressure dependent, not one operator. Happy to bisect with you against the fused module — tell me the most useful next probe (e.g. a
--dumpof the offending function's op stream).jess impact
run-stabilizationis the falcon flight-control step; while it doesn't lower, falcon cannot reach the M7 on-target (REQ-PIX-001 / DD-018--relocatable+TCB-link path). Not urgent to you if it's fast to close, but it's the gate for our on-target bring-up. #369 is currently marked closed — this is the real-fused-core residual against it.Repro is fully deterministic (public falcon asset + meld). Glad to help minimize or test a fix.