compile: four more Espresso workarounds from fuzz round 2; matmul saturation characterized - #116
Merged
Merged
Conversation
…uration characterized Emitter/lowering workarounds, each probed to its exact boundary: - round: the ANE kernel corrupts |x| >= 1024 (round(1024)=1025, round(1025)=1026, round(-2047)=-2048 - it adds 0.5 where fp16 cannot represent the tie). Every fp16 value >= 1024 is already integral, so the emitter lowers select(|x| < 1024, round(x), x): exact on the full range. This also heals the reduce->round->muls chain, whose tail was dropped by the same scale-slot fusion family as #112. - muls(k=0): mul-by-zero after a reduce crashes ANECCompile; emitted as sub(x, x) - identical zeros for every finite x, compiles everywhere. - empty programs: a graph whose output IS an input lowers to an empty MIL body, which crashes Espresso with 'unordered_map::at: key not found'. compile() now wraps such graphs in an exact scalar mul(1.0) at the GRAPH level (emitter-level guards broke output-port naming, and Espresso strips no-op reshapes back to the empty body). Characterized and modeled in the fuzzer's oracle rather than worked around (see the new issue): matmul results saturate to inf above ~32752 = fp16_max/2 for every K (K<=32 accumulation is wide - the earlier fp16-accum hypothesis was wrong); integer reduce sums lose exactness crossing 2048. The fuzzer's accumulation screen bounds sit below both cliffs. Two transpose-fed matmul findings return inf even below the threshold and remain open. 11 regression tests in test_espresso_workarounds.py; 187 tests across the ONNX/fuzz/workaround suites; 6 of 8 round-2 finding specs replay as PASSING (the two open matmul cases are tracked in the issue); fresh 400-graph batches on two seeds produce only the open matmul class.
All round-2 measurements came from one machine (M5 / H17s); ANE generations differ at the datapath level (see the guide's datapath chapter), so: - Every workaround comment now states the measured family, and why the workaround FORM is safe everywhere regardless (select-round, stable softplus, sub(x,x), and the mul(1.0) guard are semantically exact on a correct kernel too - the only cross-family question is whether they compile, which is now tested: cross_compile_check over all of them for H13-H16s, added to test_espresso_workarounds). - cross_compile_check gets the same empty-program guard as compile() (the cross-family matrix exposed that it bypassed it). - The fuzzer's oracle docstring says its cliffs (matmul saturation at ~32752, integer reduce exactness at 2048) are H17s measurements, and that a community run diverging on another chip is per-family DATA, not noise.
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.
Triage of all 7 findings from the second fuzz batch. Verdict: four real Espresso bugs fixed with probed workarounds, two oracle-model corrections, and one genuinely open corruption class, now tracked with replayable reproducers.
Fixed (emitter/lowering workarounds, each probed to its boundary)
roundcorrupts |x| ≥ 1024 (round(1024)=1025,round(1025)=1026,round(−2047)=−2048— it adds 0.5 where fp16 can't represent the tie). Since every fp16 value ≥1024 is already an integer, the emitter lowersselect(|x| < 1024, round(x), x)— exact over the full range. This also heals thermax→round→mulsdropped-tail chain.muls(0)after a reduce crashes ANECCompile — emitted assub(x, x).unordered_map::at: key not found): a graph whose output is an input lowers to an empty MIL body.compile()now wraps such graphs in an exact scalarmul(1.0)at the graph level (three emitter-level guard attempts documented why: output-port naming breaks on rename, Espresso strips no-op reshapes, and late-registered blob weights don't resolve).roundis half-away-from-zero.Characterized, not worked around (measured, in the new issue)
infat K=64. K≤32 accumulation is wide (my earlier fp16-accumulation reading was wrong — corrected in comments). The fuzzer's accumulation screen bounds sit below the cliff.Open
Two transpose-fed matmul findings return
infwith mathematically bounded sums (≤17,321 under any ordering — no correct path reaches saturation). Replayable specs + characterization in the issue; that's the next thread.Verification (on-device, M5)
11 regression tests in
test_espresso_workarounds.py(all pass); 187 tests across ONNX/fuzz/workaround suites; 6/8 finding specs replay as PASSING post-fix; fresh 400-graph batches on two seeds surface only the open matmul class; off-device selection, ruff, pyright clean.