Commit Graph
1354 Commits
Author SHA1 Message Date
Malte Ubl 2278456bbd Pre-fault allocator memory on a helper thread (Linux)
First touch of fresh memory costs the allocating thread a page fault and
a page clear. Linux executables now hand that to a helper thread:

- Size-class slices raise their bump limits in steps (256 KiB on the
  small-page prefix, half a huge page after the switch), and each step
  asks the helper to fault in the next step ahead with
  MADV_POPULATE_WRITE, which commits zeroed pages without storing, so it
  cannot race with carving. A slice holds at most about one step of
  untouched committed memory; a slice's first chunk is not faulted ahead.
- glibc's heap gets a 16 MiB top pad and the helper faults in each brk
  extension as soon as it sees the break move (checked after every
  request, then polled every 1 ms while it keeps growing; an idle program
  does not wake it).

The fast paths are unchanged; pages the helper has not reached are still
faulted by the owner. The helper masks all signals. SCR_SA_PREFAULT=0
turns it off, SCR_SA_PREFAULT_HEAP=0 keeps glibc's heap defaults; kernels
without MADV_POPULATE_WRITE (< 5.14) fall back to the old policy, and
library archives keep it.

The slice-growth allocator check now carves past several huge-page steps
and checks every fresh block reads as zero (UBSan and TSan builds run
with the helper).
2026-10-11 12:38:24 -07:00
Chris Tate baeaf9c7e0 Merge pull request #854 from vercel-labs/perf/exception-flag-main
Exact per-thread pending word for worker executables, and a call-free macOS stack guard
2026-10-11 14:06:38 -05:00
Chris Tate c293844bd9 Merge pull request #853 from vercel-labs/perf/exception-checks-main
Fewer termination polls in worker programs
2026-10-11 13:42:45 -05:00
Chris Tate b2bb6fa173 Merge main into perf/exception-checks-main 2026-10-11 13:38:06 -05:00
Chris Tate 0ab65a79b0 Merge pull request #836 from vercel-labs/perf/parmem
Support os.availableParallelism() in static programs
2026-10-11 12:55:51 -05:00
Chris Tate 60d5b258df Merge pull request #860 from vercel-labs/perf/detached-exit
Free the address space after process.exit off the critical path (`--detached-exit`)
2026-10-11 12:19:16 -05:00
Malte Ubl 00f88680cd Merge remote-tracking branch 'origin/main' into HEAD 2026-10-11 10:17:26 -07:00
Malte Ubl a34d018497 Merge remote-tracking branch 'origin/perf/exception-checks-main' into HEAD 2026-10-11 10:16:45 -07:00
Malte Ubl 445730ee38 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 10:16:37 -07:00
Malte Ubl fe79aea42e Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 10:15:10 -07:00
Chris Tate 2db40a34e8 Merge pull request #859 from vercel-labs/perf/worker-fd-table
Grow the descriptor table before worker threads share it
2026-10-11 12:07:56 -05:00
Chris Tate 727bdb7227 Merge pull request #849 from vercel-labs/perf/gc-widen-faster
Widen the old level's growth fraction two doublings at a time
2026-10-11 12:07:34 -05:00
Chris Tate e503d26b17 Merge pull request #842 from vercel-labs/perf/exact-index-checks
Cheaper checks for unproven typed-array and string indexes
2026-10-11 12:06:42 -05:00
Chris Tate c0cedd769c Merge pull request #840 from vercel-labs/perf/borrow-optional-chain-receivers
Borrow the bound receiver of optional chains
2026-10-11 12:06:17 -05:00
Chris Tate 12da5538f7 Merge pull request #834 from vercel-labs/perf/live-typed-ref-access
Access live array references one element at a time
2026-10-11 12:05:44 -05:00
Malte Ubl a1a484c9c3 Merge remote-tracking branch 'origin/perf/worker-fd-table' into HEAD 2026-10-11 09:36:55 -07:00
Malte Ubl 69aedab4f8 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-11 09:36:47 -07:00
Malte Ubl ba7c1ed2b3 Merge remote-tracking branch 'origin/perf/exception-checks-main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 09:36:39 -07:00
Malte Ubl 0e4beb15a2 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 09:36:31 -07:00
Malte Ubl d56348effd Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 09:36:22 -07:00
Malte Ubl 98dc0f1314 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-11 09:36:04 -07:00
Malte Ubl e52c7dbbbd Merge remote-tracking branch 'origin/perf/fold-constant-globals' into HEAD 2026-10-11 09:36:03 -07:00
Malte Ubl c4cb109c38 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 09:35:55 -07:00
Malte Ubl 9ae2acdd39 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-11 09:35:37 -07:00
Malte Ubl cc13a7eff9 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-11 09:35:13 -07:00
Chris Tate 6cc426c341 Merge pull request #863 from vercel-labs/land/approved-stack
Land the approved performance stack (#829, #830, #832, #833, #835, #837, #839)
2026-10-11 11:32:15 -05:00
Malte Ubl e88e2adb17 Expect a borrowed result from an appending identity callee
Counting array appends as edge-preserving makes `keep(item, items) { items.push(item); return item; }` a borrowed-return function: it preserves every heap edge, borrows every reference parameter and returns one of them. Its borrowing body now retains only the appended element and returns the parameter +0, and the owned adapter retains the result before releasing its parameters. An append never releases an element (the slow path stores past the old length), so the caller's owner keeps the result alive.
2026-10-11 09:11:47 -07:00
Malte Ubl 5c3f6f80e0 Land the approved performance stack (#829, #830, #832, #833, #835, #837, #839)
These PRs were merged into their stack base branches instead of main. This merges perf/alloc-hugepages (with the CI fixes below) into main. Conflicts: order-parity.json (union of entries); scr_alloc.c/scr_alloc.test.ts, where main already has the worker exit trim (#858, adapted to the pre-hugepages allocator): resolved by taking the hugepages allocator and re-applying the original trim commits written on top of it (ea88b87c + 4816696d, clean apply on an identical base).
2026-10-11 08:57:42 -07:00
Malte Ubl a7e91f86ac Record the preflight/order baselines for exact-index-round-trip and final-field-borrows 2026-10-11 08:55:59 -07:00
Chris Tate b2283af904 Merge pull request #831 from vercel-labs/fix/sandbox-bootstrap-polly
Install Polly in the managed sandbox bootstrap
2026-10-11 10:52:14 -05:00
Chris Tate c6822dbbf6 Merge pull request #858 from vercel-labs/perf/worker-exit-trim
runtime: return an exiting worker thread's free pages to the system
2026-10-11 10:51:46 -05:00
Malte Ubl cb9de5c1f9 Merge the fixed perf/cycle-growth-cap into perf/gc-widen-faster 2026-10-11 08:28:18 -07:00
Malte Ubl 39c6a4a033 Merge perf/typed-array-reads into perf/cycle-growth-cap
Brings in cc7c6cb7 (inline truncation decided for the host when no triple is given), which this branch predates; without it the native bootstrap's LLVM comparison with the Node seed fails on x86-64 Linux.
2026-10-11 08:27:58 -07:00
Malte Ubl 7a983d1b0f Record the preflight/order baseline for 4598-os-available-parallelism 2026-10-11 08:26:10 -07:00
Malte Ubl 50bc1d2240 Read cgroup files with one read, as libuv does
os.availableParallelism() follows libuv's uv_available_parallelism, whose uv__slurp reads each /proc and cgroup file with a single read and fails on a read error; the helper looped until the buffer filled and ignored read errors, so a long /proc/self/cgroup could resolve a controller Node does not see. A cgroup CPU quota below one CPU truncates to 0 and stays ignored rather than clamped to 1: libuv 1.51 (Node 24) through 1.52 applies a quota only when it is positive, and Node then answers the affinity count.
2026-10-11 08:26:08 -07:00
Malte Ubl 81f8502387 Merge the fixed perf/alloc-hugepages into perf/exact-index-checks
Conflicts, all resolved to this branch's side:
- emitter.ts (numeric-table get, map index probe), expr-bytes.ts, local-array-reads.ts, string-reads.ts: the base only respelled the size-to-double conversions in the old range checks (uitofp nneg, now sitofp); exactIndexLines replaces those checks entirely. The base's other hunks in these files (host inline truncation, the remaining sitofp conversions) merge as they are.
- int32-slots.ts: #837 and #843 both extracted dynamicFieldStores with identical bodies; keep this branch's signature, whose optional records/unions maps also serve #837's single-argument callers.
2026-10-11 08:25:48 -07:00
Malte Ubl a310e02f73 Merge the fixed perf/alloc-hugepages into perf/parmem 2026-10-11 08:17:24 -07:00
Malte Ubl fac59cfeea Merge the clang 18 size-conversion fix from perf/length-to-double-nneg 2026-10-11 08:14:06 -07:00
Malte Ubl 2077bc9ee6 Spell non-negative size conversions as sitofp for clang 18
The `nneg` flag on `uitofp` is LLVM 19 syntax. Sanitized builds hand the textual module to the host clang, and Ubuntu 24.04's clang 18 rejects it ("expected type"), which failed every Linux sanitized Effect shard. These operands are size_t values below 2^63, so `sitofp` converts them identically, is accepted by every supported LLVM, and still lowers to one cvtsi2sd on x86-64 (LLVM 19+ canonicalizes it to `uitofp nneg` where it can prove the sign).
2026-10-11 08:13:58 -07:00
Malte Ubl 3e83a7b38c Declare scr_detached_exit in scr_runtime.h
The emitter defines @scr_detached_exit for --detached-exit executables, and the runtime ABI test requires every scr_* symbol the backend can emit to have a prototype or extern data declaration in the header.
2026-10-11 08:13:26 -07:00
Malte Ubl 7afe786b3a Declare scr_stack_guard_get with the header's i64 return
scr_runtime.h declares uintptr_t scr_stack_guard_get(void), an i64 in the C ABI, but the Darwin worker stack check's cold fallback declared and called it returning ptr, which the runtime ABI test rejects. Declare i64 and convert the result with inttoptr.
2026-10-11 08:13:23 -07:00
Malte Ubl fb291122f5 Expect only cycle members to be traced in the self-hosting analysis cases
Since #837 a type is traced only if it lies in a cycle-capable component or reaches an intrinsic reference; a type that merely holds a cycle cannot be reached back from it. In both backend-analysis cases `record:outer` only holds `record:cycle`, so the Node oracle and the native compiler agree on ["record:cycle"] and the expectations still listed `record:outer`.
2026-10-11 08:13:08 -07:00
Malte Ubl 5ed8842d43 Merge perf/typed-array-reads into perf/alloc-hugepages
Brings in cc7c6cb7 (inline truncation decided for the host when no triple is given), which this branch predates; without it the native bootstrap's LLVM comparison with the Node seed fails on x86-64 Linux.
2026-10-11 08:12:14 -07:00
Malte Ubl bfbff5397c Install Polly in the custom sandbox image
The custom Dockerfile.sandbox image builds the LLVM helper against the same LLVM 22 CMake exports as the managed bootstrap, which reference the Polly libraries; without libpolly-22-dev CMake configure fails.
2026-10-11 08:09:10 -07:00
Chris Tate 003265a1df Merge pull request #852 from vercel-labs/fix/publish-leaks
Tell LeakSanitizer that published objects are immortal
2026-10-11 09:10:50 -05:00
Chris Tate 8d5b8a59af Merge pull request #851 from vercel-labs/perf/map-clear
runtime: make Map/Set clear and delete cost O(live), not O(peak)
2026-10-11 09:10:17 -05:00
Malte Ubl 4f89dfd19d tests: workers that drop tens of megabytes and later workers reusing their arenas
Three workers each hold and drop about 80 MB of small objects and exit;
three more then allocate as much again in the arenas the first ones
returned (on Linux the first ones hand those pages back when they exit).
Records the fixture's preflight/order baseline.
2026-10-11 07:08:22 -07:00
Malte Ubl 211b3f22b3 runtime: return an exiting worker thread's free pages to the system
A script thread that exits parks its allocator arena with every freed
block still resident, so the memory of a finished worker stays with the
process until another thread claims the arena. On Linux, a parking thread
that carved at least 16 MiB now hands back every page of its arena that
holds only free blocks (MADV_DONTNEED), relinks the remaining free blocks
in address order, and keeps the returned page runs as zeroed regions that
the next owner carves before the slice frontier. It also trims glibc
malloc. SCR_SA_STATS=1 reports what each park returned; SCR_SA_TRIM=0
turns it off.
2026-10-11 07:08:22 -07:00
Malte Ubl 6a9475e931 Free the address space after process.exit off the critical path (--detached-exit)
`scriptc build|run --detached-exit` opts an executable into handing the address-space teardown after process.exit to a helper process on Linux. The helper shares the address space (CLONE_VM), blocks signals, closes every descriptor but the read end of a pipe and waits for EOF on it. The kernel releases an exiting process's memory (exit_mm) before its files (exit_files), so EOF means the helper holds the last reference; it then exits and the kernel frees the pages there instead of before the parent sees the exit (30-40 ms per GB of 4 KiB pages; tsc-ts 20-110 ms). The same latency mold hides with --fork.

It is a build-time choice, not a run-time knob, because the helper keeps the executable mapped (overwriting the binary right after it exits can fail with ETXTBSY) and the memory charged to the cgroup for up to ~100 ms, and the teardown CPU time leaves the parent's rusage. The program object defines `scr_detached_exit = 1`, overriding the runtime's weak default of 0, so no runtime flavor or linker flag is added. The option is part of the early executable cache and route keys; executables only, no effect on other platforms.
2026-10-11 07:05:29 -07:00
Malte Ubl ea7ec561da Grow the descriptor table before worker threads share it
Every worker mailbox is a pipe. Once a process passes 64 descriptors the kernel grows its descriptor table, and while other threads share the table expand_fdtable first waits a full RCU grace period. tsc-ts on a 16-vCPU host stalled 11-25 ms on the thread starting its 14th checker (16 parse threads plus 13 checkers), on the critical path. Worker builds now grow the table to 256 entries at load, while the process has one thread, which never waits; the table never shrinks.
2026-10-11 06:42:16 -07:00