Put the AArch64 branch-range fallback on the path the JIT compiles through (#486) - #493
Merged
Merged
Conversation
) GitHub #486's reported failure still reproduces on main. 33,000 memory read-modify-writes inside an `if` compile to about 1.12MB, which puts the conditional branch past imm19, and `wasmoon run` fails with the same error the reporter filed: AArch64CompilationFailed(cause=EmissionFailed( cause=BranchOutOfRange(offset=1115408, bits=19))) There are two ways to turn AArch64 VCode into machine code and the fallback was on the one the JIT does not use. `emit` wrapped `emit_verified` in a compact-then-wide retry and is reached only from `diagnose_aarch64_target`, i.e. `wasmoon explore`. `compile` — what `compile_aarch64_target` and therefore the JIT calls — called `emit_verified` directly, so an overflow from the compact pass propagated instead of being retried at the wide width. That the two disagree is also what hid it: the natural way to investigate takes the working path, and `explore` compiled the failing module without complaint. The name is what made it easy to get wrong. `emit_verified` reads like the checked, proper emit, so it is the obvious thing for a pipeline stage to call, while being one attempt at a fixed branch width. It is now `emit_single_pass`, the retry is `emit_with_branch_fallback`, and both `emit` and `compile` go through that. `emit` keeps the `verify_frame` it owes its callers; `compile` still skips it, since `plan_frame_verified` established that invariant already. This is the third time the same shape has happened here: #487 widened only the fixups from `CodeBuffer::emit_branch`, #490 routed the direct patches in `emit_edges.mbt` through the fixup path, and this is the entry point above both of them. `run` also discarded the reason. The compiler hands it to the `Failed` stage event, but the CLI passed a no-op callback and logged the bare string "JIT compilation failed" — the reporter saw a detailed message only because they embed wasmoon as a library. It now forwards the message. An automated regression was written and withdrawn on cost: a branch only outgrows imm19 across a megabyte of code, so no fixture can be both large enough and cheap, and driving one through `compile` took the native suite from 5:10 to 16:32. scripts/gen_wide_branch_wat.py carries the reproduction instead, as long_branch_wbtest.mbt already does for the encoding half. Verified: the 1.12MB and 1.5MB modules now compile and agree with the interpreter (512 and 701); workspace 2634/2634 native and 1028/1028 wasm-gc; corpus 258/258 files and 62563 assertions in both modes.
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.
GitHub #486's reported failure still reproduces on
main:33,000 memory read-modify-writes inside an
ifcompile to about 1.12 MB, which puts the conditional branch past imm19's ±1 MB reach.Root cause
There are two ways to turn AArch64 VCode into machine code, and the fallback added for #486 is on the one the JIT does not use.
aarch64_target.emitdiagnose_aarch64_target→wasmoon exploreaarch64_target.compilecompile_aarch64_target→ the JIT andwasmoon runemitwrappedemit_verifiedin a compact-then-wide retry.compilecalledemit_verifieddirectly, so aBranchOutOfRangefrom the compact pass propagated instead of being retried at the wide width.That the two disagree is also what hid it: the natural way to investigate is
wasmoon explore, which takes the working path.explorecompiled the failing module without complaint whilerunfailed on it — that inconsistency is what located the seam.The name is what made it easy to get wrong.
emit_verifiedreads like the checked, proper emit, so it is the obvious thing for a pipeline stage to call, while in fact being one attempt at a fixed branch width.This is the third time the same shape has happened on this issue: #487 widened only the fixups from
CodeBuffer::emit_branch, #490 routedemit_edges.mbt's direct patches through the fixup path, and this is the entry point above both of them.Change
emit_verified→emit_single_pass, documented as one attempt at a fixed width.emit_with_branch_fallback;emitandcompileboth call it.emitkeeps theverify_frameit owes its callers;compilestill skips it, sinceplan_frame_verifiedestablished that invariant.runforwards theFailedstage message. The compiler already computed why it gave up; the CLI passed a no-op progress callback and logged the bare stringJIT compilation failed. The reporter of Wasm frontend threads every local through each control merge, causing AArch64 code-size explosion #486 saw a detailed message only because they embed wasmoon as a library.On the missing regression test
One was written and withdrawn on cost. A branch only outgrows imm19 across a megabyte of code, and at four bytes per instruction that is upward of 262,144 instructions however the fixture is built — the span is the code size, so there is no small-but-far version. Driving that through
compileas chained adds, withverify_allocation=falsesince allocation checking dominates at that size (ISS-371), took the native suite from 5:10 to 16:32. Eleven minutes on both CI platforms for every PR is not worth one test.scripts/gen_wide_branch_wat.pycarries the reproduction instead, with sizes and expected results in its docstring — the same arrangementlong_branch_wbtest.mbtalready documents for the encoding half.Verification
moon checkclean from a coldmoon clean.What this does not close on #486