Skip to content

JIT: fix quadratic code size at exits - #6342

Merged
pchiusano merged 3 commits into
trunkfrom
jit-shared-write-back
Nov 11, 2026
Merged

pchiusano merged 3 commits into
trunkfrom
jit-shared-write-back

Conversation

@pchiusano

@pchiusano pchiusano commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

When a native-compiled function exits back to the interpreter midway through its execution, it has to write any still-live variables back to the Unison stack. Previously that was inlined at each exit site, at a code size cost proportional to the number of variables being written, leading to a quadratic amount of code in some cases.

Now it's a linear amount of code for straight-line functions, solved by having these write backs to the stack share code in the obvious way. This change only kicks in for exits with > 4 live variables. Below that threshold it still writes them back inline, as before.

Results:

  • A tuple of 64 calls: 5.8 MB of IR and 5.6 s to compile, down to 0.66 MB and 0.44 s.
  • jit-tests.md under the stress combination (the stress-all and no-irhelpers rows that dominate the jit jobs): 222 s down to 99 s. Under eager: 99 s down to 58 s.
  • Runtime speed on jit-benchmarks.md is within 1% overall. The slowest row is 7% slower ("Loop state: a record updated each round"), and five more are 4-5% slower.

The second commit is a cleanup with no change to the generated code: the keys and lists on this path become strict pairs and Deques.

Tested with the whole JIT test matrix (19 rows) and, on the debug RTS, the eager, on-t1, stress-all and install-all rows plus jit-benchmarks.md under -DS. The numbers are in benchmarks/2026-11-11-shared-write-back.md, and the design is in the Write-back section of design.md.

…ment block

Every exit and unwind site stored its own live slots into the exit
routine's argument block. In straight-line code the live slots grow with
the function, so the stores were quadratic in its length: 88% of the IR of
the test transcript's largest modules.

A site with more than four live slots now branches to blocks shared with
the function's other sites, one per slot on top of each distinct set of
lower live slots. Each slot has a fixed place in the block, which the
routine reads when the descriptor's new flag bit is set.
UNISON_JIT_EXITS=call keeps every site storing its own.
SharedKey holds Pairs and Deques, liveSlots gives a Deque, and joinCall
builds the descriptor from Deques. The generated IR is unchanged.
@pchiusano
pchiusano marked this pull request as ready for review November 11, 2026 20:27
@pchiusano
pchiusano added this pull request to the merge queue Nov 11, 2026
@pchiusano
pchiusano removed this pull request from the merge queue due to a manual request Nov 11, 2026
@pchiusano
pchiusano added this pull request to the merge queue Nov 11, 2026
@pchiusano

Copy link
Copy Markdown
Member Author

GitHub actions had an outage again, so this died in the merge queue. Reenqueing...

Merged via the queue into trunk with commit 6e5ec7c Nov 11, 2026
13 checks passed
@pchiusano
pchiusano deleted the jit-shared-write-back branch November 11, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant