Skip to content

compiler: own programs with ProgramBox instead of unsafe free calls (#646 stage one) - #668

Closed
maschwenk wants to merge 2 commits into
mainfrom
max/program-owner
Closed

maschwenk wants to merge 2 commits into
mainfrom
max/program-owner

Conversation

@maschwenk

@maschwenk maschwenk commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

stage one of the lifetime work. design PR, i want a call on the handle shape before touching the ~187 call sites

why

new_program handed back a leaked &'static Program and five places freed it by hand with an unsafe call. the data shared between a build's versions had a third free that every caller had to order after the last program themselves

what changed

  • new_program / update_program / reuse_program return a ProgramBox. deref is the same &'static Program as before, leak() is for the cli and the test harness (they never freed anyway), drop does what free_program did
  • the shared processed-file data is an Arc on Program, the last version to drop frees it. free_unshared_program and SharedProgramData are gone
  • the five free sites (watcher, build task, orchestrator, programOwner, transpileModule) just drop the owner
  • notes/mem-program-owner.md has the drop order per holder

tradeoff

the view is still &'static, so safe code can still copy it out and read it after the owner drops. same hole as before, one owner instead of five free calls. one order moved: the lsp frees the shared data with the last program now, before the regions, not when the base region goes. poison runs are clean

the question

stage two makes the view a borrow. that touches ~187 sites in tsrs_ls, tsrs_incremental and the checker pool, and the &'static self methods on Program and the pool's back reference are where a lifetime has to appear. lifetime-carrying view, or an arc-like owner that the pool and the incremental program clone?

numbers

perf gate run twice over the 19 projects. instructions flat (0.0% on the big ones, ±1% on the sub-second ones where the counter moves that much on its own). peak rss within 1%, one +2.6% on date-fns that was -0.4% on the rerun. wall time ±5% both directions with no repeat outlier, same spread a no-op PR gets on these runners. one arc clone and one dec per program, the hot path is unchanged

rebases over #638 and #645 with a small conflict on the #[expect] reasons

@codspeed

codspeed Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

1 warning

⚠️ 4 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 12 untouched benchmarks
⏩ 4 skipped benchmarks1


Comparing max/program-owner (5f50a29) with main (cb5f848)

Open in CodSpeed

Footnotes

  1. 4 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Performance change vs main

Workflow run · main cb5f848c5190 → PR merge 36c8f4ea3ac9 (head 5f50a29bb168)

Clef gate: Running benchmarks because the decision was unavailable (ValueError).

Release builds, default checker count; -p <project> --noEmit --incremental false --pretty false. Ten alternating main/PR timing pairs and three RSS runs per binary on each project's runner. Instructions are user-space instructions retired across all threads in three separate alternating pairs at the default checker count.

Project Wall time Δ Peak RSS Δ Instructions Δ
vscode -0.5% -0.1% -0.0%
xstate-main -3.1% +0.8% +0.1%
webpack -1.4% +0.0% +1.0%
mui-docs +0.6% +1.4% +0.1%
Compiler +0.2% +2.0% +0.0%
Compiler-Unions -0.9% +1.1% -0.0%
cal-diy +0.6% -0.4% -0.0%
formbricks-web +0.2% +0.1% -0.0%
supabase-studio +0.1% +0.1% +0.1%
t3code-server -2.4% +0.3% +0.5%
mikro-orm +0.1% -0.3% +0.0%
next-packages-next +0.5% -0.1% -0.3%
next-root -1.2% +0.5% +0.1%
storybook +0.7% -0.1% +0.5%
nuxt -0.2% -0.2% +0.1%
playwright -0.9% -0.1% +0.8%
drizzle-orm +0.8% +0.1% -0.8%
typeorm +1.0% +1.7% -0.3%
date-fns +0.6% -1.1% +0.3%

Negative means the PR used less; positive means it used more. Deltas are medians of paired PR/main ratios for wall time and instructions. Shared CI machines and parallel scheduling add noise to small changes.

maschwenk and others added 2 commits October 22, 2026 15:57
Stage one of #646. new_program, update_program and reuse_program return a
ProgramBox whose Drop does what free_program did; the processed-file data a
full build's versions share is an Arc in Program, freed with the last version.
The five free sites (watch, build task, orchestrator, LSP program owner,
transpileModule) drop the owner instead; the CLI and the test harness leak it
as before. The &'static Program view is unchanged.
@Boshen
Boshen force-pushed the max/program-owner branch from a7767b3 to 5f50a29 Compare October 22, 2026 08:15
@Boshen

Boshen commented Oct 22, 2026

Copy link
Copy Markdown
Member

This is still calling .leak().

@Boshen Boshen closed this Oct 22, 2026
@Boshen
Boshen deleted the max/program-owner branch October 22, 2026 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

2 participants