Commit Graph
6750 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 37f752ab89 Merge the restacked base into ni/i10-js-island-requires
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:35 -07:00
Malte UblandClaude Opus 5.5 27fd36402b Merge the restacked base into ni/i9-recorded-deferral
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:33 -07:00
Malte UblandClaude Opus 5.5 a11b82dd8e Merge the restacked base into ni/i8-exit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:32 -07:00
Malte UblandClaude Opus 5.5 b97b161c08 Merge the restacked base into ni/i7-routing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:30 -07:00
Malte UblandClaude Opus 5.5 76a60ff53b Merge the restacked base into ni/i6-subclasses
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:29 -07:00
Malte UblandClaude Opus 5.5 3a8aee738a Merge the restacked base into ni/i5-shared-modules
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:22 -07:00
Malte UblandClaude Opus 5.5 603fec49b9 Merge the restacked base into ni/i4-deferred
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:03 -07:00
Malte UblandClaude Opus 5.5 562b56a54c Merge the restacked base into ni/i3-exports
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:41:02 -07:00
Malte Ubl caee8dfef2 Merge the restacked base into ni/i2-handles
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	packages/compiler/src/executable/executable-cache.ts
#	packages/compiler/src/index.ts
2026-10-27 05:41:01 -07:00
Malte UblandClaude Opus 5.5 02a322e712 Merge ni/13-libnode-packs (with ni/1-libnode-loader)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:40:40 -07:00
Malte Ubl f9419b5d47 Merge remote-tracking branch 'origin/ni/1-libnode-loader' into HEAD
# Conflicts:
#	packages/compiler/src/index.ts
2026-10-27 05:40:18 -07:00
Malte Ubl 26557112f1 Merge remote-tracking branch 'origin/main' into lf/n2-fallback
# Conflicts:
#	packages/compiler/src/frontend/lowering/lowerer.ts
#	packages/compiler/test/canon-normalize.test.ts
2026-10-27 05:40:00 -07:00
Malte UblandClaude Opus 5.5 4d5127944e Merge main into the zero-cost triage branch
Main moved the per-program report lines into zero-cost-compare.mjs and
added sharding and a baseline cache. The runtime-only and
nondeterministic verdicts now print through programLines, count as
compared in merged shard reports, and the runtime pack diff is skipped
when only recording a baseline cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:38:08 -07:00
Malte UblandClaude Opus 5.5 03ca571663 Merge main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:36:35 -07:00
Malte Ubl 7f0488a411 Merge remote-tracking branch 'origin/main' into perf/static-objects 2026-10-27 05:35:56 -07:00
Malte UblandClaude Opus 5.5 041565d7f2 Split the slowest sanitized suites by case and raise the sanitized bootstrap's stack
Under ASan, self-hosting-llvm-emitter, three-library, npm-static and npm
each take 20 to 40 minutes as one Vitest file, so a sharded run cannot
finish before them.

- self-hosting-llvm-emitter splits into its emitter-cases-and-programs
  and fixed-point halves; a shard builds the seed once for the halves it
  owns. three-library skips the optimization its shard does not own.
  Both put their tests in a describe block, which skipUnownedTest
  partitions.
- CI runs both in every shard's case-sharded side queue
  (scripts/ci-test-plan.mjs); pnpm test:sandbox case-shards them with npm
  and npm-static.
- npm.test.ts keeps a skipped placeholder in a shard that owns no
  program, so an empty shard does not fail the file.

The sanitized native-driver bootstrap ran its instrumented compiler with
the default 8 MiB stack. Its frontend phase overflows it (SC3004) on main
as well, outside CI's ulimit for the emitter phase. The test now raises
the soft stack limit to the hard one for every sanitized compiler process
it starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 05:32:27 -07:00
Chris Tate 635f732a34 Free a worker's group-arena pool when the worker exits
Each thread keeps up to 8 MB of arena chunks and up to 64 dropped arenas
for reuse (scr_arena.c). Nothing freed them when a worker thread finished,
so every worker that used a group arena left its pool behind: 40 workers
run one after another, each dropping a 12 MB arena, kept 328 MB resident.

scr_arena_thread_exit frees the calling thread's pooled arenas and chunks.
The worker thread calls it after its context cleanup, before it posts the
exit event. The new harness test runs 24 such workers in sequence and
checks that the resident set grows by less than 32 MiB over the last 20
(160 MiB before this change, 1 MiB after).
2026-10-27 07:17:08 -05:00
Chris Tate e84d332264 Link AbortController without the fetch unit
AbortController and AbortSignal lowered to fetch.abort* entry points, so
a program that only used them linked the fetch unit with its socket,
HTTP, TLS and zlib dependencies. They now lower to the abort.* entry
points on every target, a new abortSignal feature links scr_abort.c (the
fetch unit's signal implementation built without its transport, which
WASI already ships) and the EventTarget unit, and programs that also
fetch keep the fetch unit's copy.
2026-10-27 07:05:47 -05:00
Chris Tate 9ca9c7af9d Run JavaScript web streams on a native ReadableStream
In JavaScript sources, ReadableStream, ReadableStreamDefaultReader,
ReadableStreamDefaultController, CountQueuingStrategy and
ByteLengthQueuingStrategy (globals and node:stream/web exports) are class
values over a new runtime unit, scr_webstreams.c, which follows Node 24's
webstreams step by step: underlying sources, queuing strategies, default
readers, tee, ReadableStream.from and async iteration settle their promises
in Node's order. scr_dyn_handle_class.c gives a dyn handle tag a
constructor value with a prototype, brand checks and Node's constructor
errors. Stream state carries cycle headers, so the collector reclaims
streams whose callbacks close over their controllers.

Promise steps run as native promise reactions, a variant of scr_async.c
that only programs with web streams link; every other program links the
default object. TypeScript sources keep their static web stream lowerings.
Byte streams, BYOB readers, pipeTo and pipeThrough throw SC2020.

node24-web-stream-readers and node24-web-stream-readable-lifecycle now
match Node.
2026-10-27 06:53:26 -05:00
Chris Tate f65c5518bf Record each reachable-emit unit's lowering as plain data, checked by a serial mode
A unit's record holds its IR in the compact encoding, the entries it added
to the helper, lifted-function and other appendable lowerer tables, and the
reachability, generic demand, statement and flag events it made. A unit has
no record when it grew a table no record carries, changed a field, replaced
an entry it did not add, or emitted a diagnostic. Every lowerer collection
and scalar field is classified, and a test fails on one that is not.

SCRIPTC_LOWER_JOURNAL=serial records every unit and replaces its results
with the ones its record carries. scripts/recorded-lowering.mjs
(pnpm test:recorded-lowering) compares corpus programs' LLVM text with and
without the mode. SCRIPTC_LOWER_JOURNAL_DUMP=<dir> also turns the mode on
and appends each unit's record to <dir>/records.jsonl. Without either,
lowering is unchanged.
2026-10-27 06:43:19 -05:00
Chris Tate f4a4cf8652 Merge main 2026-10-27 04:32:00 -05:00
Chris Tate f73722c2ef Add an opt-in --static flag for Linux executables (#2398) 2026-10-27 04:31:35 -05:00
Chris Tate 3335cc8ba4 Leave the dispose list out of arena constructors when every arena is closed (#2393) 2026-10-27 04:26:03 -05:00
Chris Tate 549bec79f8 Name the source file in empty split codegen parts (#2397) 2026-10-27 04:12:57 -05:00
Chris Tate 7bd12ea89f Add a representation report and a heap census for ranking value-layout work (#2390) 2026-10-27 04:01:41 -05:00
Chris Tate 8cdf783023 Find glibc's NSS loader in the --static link map
The NSS check scanned the linked executable for the `libnss_` literal, so
a program with that text in a string of its own was refused. A --static
glibc link now writes a link map, and the executable is refused when the
map places code from libc.a's nss_module.o or nsswitch.o in it (lld's
kept input sections, or GNU ld's memory map with a nonzero size).
2026-10-27 04:00:21 -05:00
Chris Tate 2739da5fc0 Skip the helper version probe and unchanged files' digests on rebuilds (#2396) 2026-10-27 03:59:01 -05:00
Chris Tate d4185bcd8e Add an opt-in --static flag for Linux executables
- `scriptc build --static` (and the `staticLink` compile option) links glibc
  executables as static PIE on the build host's own glibc target; musl
  executables are already static and accept the flag.
- An executable that keeps glibc's NSS module loader (host, user and group
  name lookups) is refused with SC3002, since a static glibc executable
  loads those modules from the run host.
- Other targets, cross-compiled glibc targets, --sanitize and non-executable
  output are refused; static and dynamic builds use separate cache entries.
2026-10-27 03:34:16 -05:00
Chris Tate 7c770d0887 Name the source file in empty split codegen parts
When --split codegen keeps the module whole, the extra outputs are new
empty modules, and their source file name defaulted to the module
identifier: the path of the private copy of the LLVM input, which has a
random name. That name reached each part's STT_FILE symbol, so two
release builds of one program differed in .strtab. The empty parts now
take the module's source file name, as the first part has it.
2026-10-27 03:30:15 -05:00
Chris Tate 60372c09a4 Declare the heap census writer in scr_lib.h
The LLVM backend can emit scr_census_write, which a census program
defines and the runtime calls through a weak reference, so the header
declares it as it does scr_detached_exit.
2026-10-27 03:28:32 -05:00
Chris Tate 9179a881a4 Skip the helper version probe and unchanged files' digests on rebuilds
Every native build started the LLVM helper once for `version` before
emitting, and hashed every file the frontend read, including the supplied
declaration files that do not change between builds.

- verifyNativeHelper takes an optional record of helpers that already
  answered `version` as expected. The native compiler keeps it in its cache
  (family "helper"), keyed by the helper's and the compiler's file identity,
  the compiler version and the target. The probe runs again when the helper
  file changes. The package name and version are still checked every time.
- FrontendInputTracker takes the snapshot of the cache entry a build
  replaces. A file whose settled identity has not changed since that
  snapshot keeps the digest recorded there, which is the trust
  frontendInputsStillMatch already gives an unchanged identity.

Builds produce the same executables.
2026-10-27 03:24:16 -05:00
Chris Tate ed54f4f0c8 Leave the dispose list out of arena constructors when every arena is closed
An arena-aware constructor of a record shape or class with reference
fields tests whether the current arena is closed on every allocation, and
appends a dispose entry when it is not. When every root and call region in
the program opens a closed arena, the current arena is closed or absent,
so planArenas sets ArenaPlan.allClosed and those constructors leave out
the test and the dispose list.
2026-10-27 02:59:35 -05:00
Chris Tate 429aa8273d Keep the heap census hook out of library archives
Libraries never build the census (heapCensusOf), but the runtime's weak
scr_census_write reference made library archives list an external
symbol outside their declared set. Declare and call it only in programs.
2026-10-27 02:49:05 -05:00
Chris Tate cb572a7b67 Pad ASCII strings without indexing their UTF-16 units
`toUpperCase` and `toLowerCase` now record that a result of their ASCII
path is ASCII. `padStart` and `padEnd` take the UTF-16 lengths of the
receiver and the fill the way `length` does: a proven-ASCII or short
string is counted directly instead of claiming a string-index entry. A
one-unit fill repeats without a division, a one-byte fill with memset,
and a result padded from an ASCII receiver and fill is recorded as ASCII.

`word.slice(0, 3).toUpperCase().padStart(4, "-")` previously built an
index entry for the three-byte receiver on every call.
2026-10-27 02:44:56 -05:00
Chris Tate 723975d01c Prove that the arrays InlineArray fields hold are never written
The constructor copies the array it is passed into the instance, and
other reads of the field give a new array over the elements, while under
Node.js the field holds that array itself. The contract check now proves
the two agree, and reports SC7009 where it cannot:

- The arrays fields hold are never written. A whole-program graph of the
  places arrays are held (locals, parameters, results, class and record
  fields, and the elements of arrays, Maps and Sets by where those are
  held) follows every array a field read may give, and reports a write to
  one, or one that reaches a dynamic value, a call through a function
  value or a library call other than printing and JSON.stringify without
  a replacer. Functions that return their argument give each caller its
  own argument back. The report names the field read.
- The array passed to the constructor is one nothing else keeps: a new
  array (a literal, a copy, or the result of a function that returns only
  such arrays or its argument), a local array at its last use (after calls
  that only read it or write it in place, or in a loop that then assigns
  the local a new array), a parameter whose every caller passes such an
  array, an array a field already holds, or a const array that is never
  written, which is then followed like a field's.

The review fixes from before stay: `new` evaluates its arguments into
locals unless they are all locals and constants, so that the allocation
reads the length after every argument has run; the constructor uses the
array parameter only to store it; and the dynamic view of an instance
reads the field as an array of its elements.
2026-10-27 02:38:15 -05:00
Chris Tate 659a74acc4 Merge main 2026-10-27 02:27:10 -05:00
Chris Tate 6451e846c6 Reset the prefault helper's state in forked children (#2391) 2026-10-27 02:10:51 -05:00
Chris Tate 2bbb855f1f Record the preflight and order baseline of the compressed fetch body program 2026-10-27 02:09:51 -05:00
Chris Tate 5a1bc2a6d5 Drain a compressed fetch body that is still buffered when the response ends
Since the transfer drops its socket at the response's 'end', resuming a
read no longer reached the decoder: sf_response_resume_if_ready returned
before sf_inflate_resume once the transfer held no paused socket. A
compressed response whose whole body arrived before it was read then kept
its remaining decoded output buffered, and later reads stayed pending. The
buffered input and decoder output now drain on demand after 'end' as well
as while the socket is paused.
2026-10-27 01:56:31 -05:00
Chris Tate 60d5dc8ab1 Count class-value constructions in the census, and key caches on both diagnostics
- The heap census also counts instances a class value constructs: the
  construct thunk that `newValue` calls counts them after its allocation,
  as `new` does at its own.
- A build with SCRIPTC_HEAP_CENSUS set is its own executable cache entry
  (`heapCensus` in the early cache's key and route key), and one with
  SCRIPTC_REPR_REPORT set does not read the early cache, since a hit
  emits no module and so writes no report. The CLI's startup route defers
  both to the full compiler.
- Test: a warm cache still builds the census executable, counting
  `new` and class-value constructions, and still writes the report.
2026-10-27 01:54:48 -05:00
Chris Tate 773eb15a52 Spread a string into its code points array (#2389) 2026-10-27 01:51:43 -05:00
Chris Tate 331d3804a8 Decode short non-ASCII strings directly in charCodeAt (#2388) 2026-10-27 01:44:17 -05:00
Chris Tate 59ec7998fe Reset the prefault helper's state in forked children
fork() copies only the calling thread, so a child forked while the
allocator's prefault helper held scr_sa_pf_lock waited on that lock forever
at its first fault-in request, and a child that released the range the helper
was faulting in waited forever for the helper to finish it. Native builds'
emit workers hung this way, with the parent waiting in waitpid.

The helper's first start now registers pthread_atfork handlers. The prepare
handler takes the lock across the fork and the parent handler releases it.
The child handler resets the ring, the busy range, the waiter state and the
heap polling state, reinitializes the lock and both conditions, and marks the
helper not started, so the child's first request starts a helper of its own.

check_fork_while_prefaulting in scr_alloc.test.c forks while the helper
faults in 64 MiB, over 64 rounds; each child releases that range, faults in a
range of its own and must exit within five seconds. ThreadSanitizer builds
skip it.
2026-10-27 01:35:32 -05:00
Chris Tate 8b256639f4 Add a representation report and a heap census for ranking value-layout work 2026-10-27 01:31:00 -05:00
Chris Tate 4b8ed48a8a Spread a string into its code points array
`[...text]` lowered to an array literal that spread the array of text's
code points: the literal's array copied the code point strings, retaining
each one, and the code point array then released them. An array literal
whose only element is a spread of a string is now the code points array
itself, as `Array.from(text)` already was. Both are fresh arrays.
2026-10-27 01:19:32 -05:00
Chris Tate 618243caa8 Generate code concurrently for optimized executables that stay one partition (#2387) 2026-10-27 01:10:09 -05:00
Chris Tate 47904df3c2 Decode short non-ASCII strings directly in charCodeAt
charCodeAt on a non-ASCII string mapped the UTF-16 index to a byte offset
through a string index entry, which the first read claimed and the
string's release purged. For a string of one character, such as each
character of a for-of loop that codePointAt(0) classifies, the entry cost
more than the read.

A non-ASCII receiver of up to 16 bytes is now decoded from its start on
each read, as scr_str_utf16_len already counts short receivers. Longer
receivers keep the index entry and its cursor.
2026-10-27 01:07:00 -05:00
Chris Tate 31fe5c5a8d Convert the base of an offset typed-array index once (#2385) 2026-10-27 00:56:23 -05:00
Chris Tate f64b25d103 Add --large-pages to move executable code onto huge pages (#2386) 2026-10-27 00:36:40 -05:00
Chris Tate 72732110ff Check offset typed-array indexes against the sum for fractional bases and keep the base the sum read
An index `base + c` converted `base` and took a fractional base as an
invalid index. A fraction smaller than the sum's precision is lost in the
sum, as in `1e-20 + 1`, which is the index 1. A base that is not an
integer now goes to the exact check of the sum itself.

The conversion also read the base local again when the access was
emitted. A write evaluates its right-hand side between the index and the
store, so `values[at + 1] = ((at = 5), 31)` wrote at the new base. The
sum now records the base value it was computed from, and the index
converts that value.

The corpus program covers both cases, and the emission test checks that
a write converts the base its sum read.
2026-10-27 00:28:04 -05:00