Commit Graph
5556 Commits
Author SHA1 Message Date
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 fefbf3ac35 Reuse keep-alive connections across fetch() requests
The native fetch dialed a new connection for every request and closed it
after the response, so a loop of requests to one origin paid a TCP
connect, and for https a TLS handshake, per request. Node's fetch keeps
connections alive through undici's dispatcher. A direct request now goes
through a keep-alive agent private to fetch (scr_http_fetch_request): an
idle connection for the same host, port and bare hostname serves the next
request, there is no limit on connections per origin, and an idle
connection closes after 4 s or the server's Keep-Alive hint less one
second, without keeping the process alive. Proxied requests still dial
their own connection.

When a response ends, the transfer resumes its socket if it had paused it
and stops pausing and resuming it, since the agent may hand the socket to
the next request. scr_http_request_agent_ex and http.Agent creation share
their agent logic with the new entry point.
2026-10-26 22:25:51 -05:00
Chris Tate 55cf42e9c0 Install ld.lld in the Sandbox toolchain so Sandbox links match CI (#2375) 2026-10-26 22:14:48 -05:00
Chris Tate da7aa5377a Key the gate's baseline cache on the linker clang finds (#2376) 2026-10-26 22:13:58 -05:00
Chris Tate 31f07e2d73 Read class instance fields inline at dynamic-tier property sites (#2374) 2026-10-26 22:13:38 -05:00
Chris Tate 80138011ed Allocate template literals at their exact length (#2369) 2026-10-26 21:55:50 -05:00
Chris Tate c9585ae48f Fail type checking when the generated IR field readers miss a field (#2370) 2026-10-26 21:52:07 -05:00
Chris Tate 079bad80be Leave out a header the compiler rejects in the implicit toolchain probe (#2371) 2026-10-26 21:47:29 -05:00
Malte UblandClaude Opus 5.5 b4ba4d38ac Key the gate's baseline cache on the linker clang finds
Linux links use ld.lld when clang finds it and the system linker
otherwise, and the two produce different executables. A baseline record
made with one must not settle candidates linked with the other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:43:02 -07:00
Malte UblandClaude Opus 5.5 9c42c8d74b Install ld.lld in the Sandbox toolchain so Sandbox links match CI
CI's pinned LLVM ships ld.lld, and the compiler links Linux executables
with it whenever clang finds it. The Sandbox image and the managed-image
bootstrap installed clang-22 without lld-22, so Sandbox builds linked
with GNU ld: static hello came out at 99 KB instead of 65 KB, and
runtime-tree-shaking failed only in Sandboxes. Install lld-22 in both
and check that clang finds it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:40:54 -07:00
Chris Tate 2cbce33cd3 Read class instance fields inline at dynamic-tier property sites
An instance of a compiled class reaches untyped code as a capsule, which
a property site sent to its helper on every read: the helper normalizes
the capsule, tests each class's key in turn, and loads the field.

A read site now emits the helper's plain field branches inline. A
capsule whose key pointer is one of those classes' keys loads the field
from its instance and shares the inline hits' conversion. A branch is
emitted only when its field holds a plain dyn cell and every subclass
the helper's normalization can rebox the key to reads the same field
from the same slot. Getters, bag properties, unset fields and other
capsules keep the helper.
2026-10-26 21:39:18 -05:00
Chris Tate d99f355182 Give the native layout and metadata emission test 600 seconds (#2368) 2026-10-26 21:36:39 -05:00
Chris Tate 2795957867 Step untyped loops over the program's generators on the typed path (#2302) 2026-10-26 21:36:11 -05:00
Chris Tate 06bd585a53 Test two inline-cache entries inline at dynamic-tier property sites (#2367) 2026-10-26 21:20:00 -05:00
Chris Tate 2c09b499b2 Check a nested record of scalar fields by its own fields
The generator listed a field as a scalar when its walk read nothing, so
a nested record holding only numbers and booleans got a ValuesNotRead
check on the record itself, which fails because the record is an object,
even after regenerating. A field is now listed as a scalar only when its
type holds no string and no object; such a record is checked through its
own fields, so a field added to it or a scalar in it that starts to hold a
string still fails type checking with its name.

The generator exports generateIrFields(irText) for the new test, which
generates the readers for an ir.ts with such a record and type-checks
them. The generated file for the current ir.ts is unchanged.
2026-10-26 21:18:38 -05:00
Chris Tate e4fe30e084 Leave out a header the compiler rejects in the implicit toolchain probe
The implicit toolchain probe preprocesses every system header that the
runtime and vendored sources spell in one source file. When the compiler
rejected one of them, the whole probe failed, and the build continued
without the native cache and without program sharding.

When the compiler rejects the probe source, each header is now
preprocessed on its own, and a header that the compiler rejects on its
own is left out of the dependency set, as an absent header is. The probe
still fails when the compiler rejects every header.
2026-10-26 21:16:15 -05:00
Chris Tate 07f159accd Fail type checking when the generated IR field readers miss a field
fields.generated.ts reads IR nodes' fields by name, so a field ir.ts gains
after the last `pnpm generate:ir-fields` is skipped by the walkers until
someone regenerates the file; `--check` in CI reports it, but only on the
run that looks.

The generator now ends the file with type-level checks, one per node kind
and per nested record the walkers enumerate: a field outside the list the
file was generated from fails type checking with the field's name, and so
does a field read as a scalar (no string, no object) that can now hold a
string or an object. Fields the file reads already fail type checking when
they are removed or change type. The checks are types only and emit no
code.
2026-10-26 21:06:07 -05:00
Chris Tate 48ccee523e Allocate template literals at their exact length
A template literal of strings and numbers reserved 32 bytes for each
number, the longest number text, and kept that capacity for the result's
lifetime: an 8-byte name built from two numbers used a 96-byte block. The
numbers now format into a stack buffer first, so the result is allocated
once at its exact length.
2026-10-26 21:01:36 -05:00
Chris Tate 87565abc16 Merge main 2026-10-26 20:57:59 -05:00
Chris Tate 9d0ca5f641 Give the native layout and metadata emission test 600 seconds
The sanitized run of "native LLVM layout and metadata emission (llvm)"
takes 170 to 300 seconds in its CI shard beside the shard's other tests,
against the suite's 300-second test timeout. On a quiet 8-vCPU machine it
takes 130 to 160 seconds.
2026-10-26 20:50:23 -05:00
Chris Tate 6beed6cb01 Test two inline-cache entries inline at dynamic-tier property sites
A dynamic-tier property site tested only the first entry of its cache
inline and called scr_js_get_probe (or scr_js_store_probe) for anything
else. A site that sees two layouts, such as object literals of one
record built in two key orders, then took the out-of-line probe on
every access to objects of its second layout.

Sites now test the first two entries inline, own or prototype, for
reads and replacing writes. The probe still answers the third and fourth
entries, misses and everything that is not plain data.
2026-10-26 20:47:23 -05:00
Chris Tate 807f5f6712 Shard the zero-cost gate and reuse baseline work across runs (#2365) 2026-10-26 20:29:33 -05:00
Chris Tate cd606e96ff Key baseline cache entries by their program's inputs
A recorded baseline entry now holds a hash of its program's inputs: the
entry file and its relative imports (paths and contents) and the
optimization level. --baseline-cache uses an entry only when the program
still hashes the same, so a program or an import that changed since the
record builds its baseline, instead of a recorded build error or artifact
hash of the old sources settling the comparison as both-fail or identical.
Records take schema 2; older records are ignored.
2026-10-26 19:53:49 -05:00
Malte UblandClaude Opus 5.5 d0047c813f Shard the zero-cost gate and reuse baseline work across runs
The full gate takes 30 to 75 minutes on one Sandbox, which made it the
longest step of a validation round. It can now be split and merged:

- --shard=i/n splits the bench and corpus programs (the self-hosted
  compiler runs in shard 1, or alone with --set=self), and
  scripts/zero-cost-merge.mjs merges the shards' --json reports into one
  verdict, recomputing the compile-time intervals over every shard.
- --record-baseline-cache records the baseline's artifact hashes once;
  --baseline-cache skips the baseline build of every program whose
  candidate artifacts hash the same. The record is tied to the built
  baseline checkout, toolchain and paths.
- The candidate-source self build overlaps the comparison builds on
  hosts with room for it.

pnpm test:sandbox can reuse a prepared snapshot's LLVM helper and runtime
pack (SCRIPTC_SANDBOX_NATIVE_REFERENCE) when their inputs are identical,
and retries rate-limited Sandbox creation with backoff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 17:43:44 -07:00
Chris Tate 2c5b3b48d4 Version loops around nested loops only for accesses inside them (#2364) 2026-10-26 19:35:06 -05:00
Chris Tate ad7846bcfa Search ASCII strings from a start index without index conversion (#2362) 2026-10-26 19:18:50 -05:00
Chris Tate 79fc99ef43 Build node:zlib on the zlib Node bundles (#2363) 2026-10-26 19:10:48 -05:00
Chris Tate ca1a6b5bed Version loops around nested loops only for accesses inside them
A typed-array loop version runs a checked copy and one or two fast copies
of the loop, and each copy repeats every nested loop together with the
nested loop's own version. A loop whose guard proves only accesses outside
its nested loops, which run once per pass, now runs as written when a
nested loop accesses typed arrays; the nested loops still version their own
accesses. The loop versions as before when the guard proves the bounds of
an access inside a nested loop, or, in a worker program, the ownership of
an access inside a nested while loop, which never versions for ownership
alone.

Spectral-norm's paired row sums no longer copy their column loop for the
row loop's two stores. The worker program's au and atu functions are
102 KB of LLVM text instead of 236 KB each, and the program 519 KB instead
of 786 KB.

Corpus typed-array-nested-loop-versions covers row loops that store past
the end or at negative, fractional and NaN rows, views the nested loop
writes, and a nested while loop.
2026-10-26 19:05:12 -05:00
Chris Tate 7be9803076 Build node:zlib on the zlib Node bundles
Re-lands #2355, reverted in #2358. #2361 fixed the CI failure it exposed.
2026-10-26 18:40:07 -05:00
Chris Tate 4dddb94373 Revert "Build node:zlib on the zlib Node bundles" (#2358) 2026-10-26 18:38:38 -05:00
Chris Tate 80a5d9a820 Read missing elements through non-null asserted operands as Node does (#2331) 2026-10-26 18:35:37 -05:00
Chris Tate ce616f44dd Guard <riscv_vector.h> by target in the implicit toolchain probe (#2361) 2026-10-26 18:30:27 -05:00
Chris Tate 1245b18469 Search ASCII strings from a start index without index conversion
String.prototype.indexOf took its byte-search fast path on a proven-ASCII
receiver only when the start index was not positive. A positive start went
through the general path: ToIntegerOrInfinity, the surrogate-needle check,
and UTF-16 to byte conversions in both directions, although byte offsets
are UTF-16 indexes in an ASCII string. The fast path now covers every start
index: a positive start truncates and clamps to the length, where only the
empty needle is found, and the search runs from that byte offset.
2026-10-26 18:22:22 -05:00
Chris Tate 8464f2a7c5 Check the native stack guard in one function per call cycle (#2360) 2026-10-26 18:20:07 -05:00
Chris Tate f9258423f1 Run native async generators as stackless coroutines (#2332) 2026-10-26 18:11:00 -05:00
Chris Tate 2b8873bb92 Keep the native compiler's profile matching its functions (#2357) 2026-10-26 18:07:49 -05:00
Chris Tate 707b790603 Inline small leaf functions called only from loops (#2359) 2026-10-26 18:04:38 -05:00
Chris Tate 19b0b40486 Address review feedback 2026-10-26 18:01:27 -05:00
Chris Tate f003bb4d68 Guard <riscv_vector.h> by target in the implicit toolchain probe
The implicit toolchain probe includes every system header that the
runtime and vendored sources spell, under __has_include. clang ships
<riscv_vector.h> for every target, and through clang 18 including it
without the RISC-V vector extension is an #error. Since the vendored
zlib's adler32_simd.c spells it, the probe failed with clang 18, which
disabled the native cache and program sharding: a sanitized dev build
then compiled the whole program as one translation unit.

The probe now includes <riscv_vector.h> only where __riscv_vector is
defined, and the probe's header test covers it and a riscv64 target.
2026-10-26 17:58:37 -05:00
Chris Tate c75c95d864 Check the native stack guard in one function per call cycle
Every unbounded recursion repeats some cycle of the call graph, so a
stack check in one function of each cycle bounds it. stackCheckedFunctions
now picks a feedback vertex set of the stack-check graph greedily (the
function with the most caller-callee pairs first) instead of checking
every function on a cycle, and adds a check where a run of unchecked cycle
functions would exceed 32 calls, so the stack used between two checks
stays of the same order as before.

Worker entry polls follow: a synchronous function on a cycle that may
throw needs no entry poll, since a descent that never returns ends at a
checked function of its cycle. NoThrow contracts keep the model in which
every function on a cycle checks, a superset that does not depend on the
choice.
2026-10-26 17:47:58 -05:00
Chris Tate 580f405727 Inline small leaf functions called only from loops
A small helper called once per element, such as

    function pick(list: Gene[], rand: number): number {
      for (const gene of list) if (rand < gene.c) return gene.sc;
      return list[list.length - 1]!.sc;
    }

called from a loop over the elements, is often past LLVM's inline cost
threshold once its loop versions and checks are emitted (fasta's
randomCode costs 430 against a threshold of 225), so every element pays a
call, the spills around it, and checks the caller's loop cannot hoist.

findLoopCallees (loop-callees.ts) finds functions that are synchronous,
non-generator, capture nothing, call nothing (no call, callValue,
virtualCall, dynCall, dynInvoke, new, newValue, newPromise, ffiCall or
await), have at most 12 statements and one loop, and have one or two
direct call sites in the module, each inside a loop of its caller. The
emitter marks those calls `alwaysinline` (a call-site attribute, so the
function's definition is unchanged). Library modules and debug builds
keep LLVM's cost decision.
2026-10-26 17:33:58 -05:00
Chris Tate 7e225c5d87 Merge main 2026-10-26 17:33:06 -05:00
Chris Tate a82a0dda93 Revert "Build node:zlib on the zlib Node bundles"
This reverts #2355. The sanitized three.js nodes CI job has failed on every main commit since it landed.
2026-10-26 17:05:37 -05:00
Chris Tate 3c83fcb89c Allocate object groups that die together in arenas (#2348) 2026-10-26 16:54:31 -05:00
Chris Tate 2d557bc87b Test a scalar optional left of ?? in JavaScript on the typed path (#2345) 2026-10-26 16:54:11 -05:00
Chris Tate 6bdd3e39ca Fix the self-hosting build of the arena runtime 2026-10-26 16:20:12 -05:00
Chris Tate 1b2e591a02 Keep the typed ?? path to literal defaults 2026-10-26 16:18:58 -05:00
Chris Tate c5e8a876ec Store ordinary objects as a layout plus value slots (#2353) 2026-10-26 16:18:38 -05:00
Chris Tate 45d81216c0 Name modules by the same relative path on Windows
stableFileName used node:path's relative(), which returns paths with `\`
separators on Windows. The module tags hash that name, so Windows builds
named the functions of every non-entry module differently from Linux
builds, and a profile or layout collected on Linux no longer matched them.
Structural positions used the same name.

portableRelativePath computes the relative path with the platform's rules
and writes it with `/` separators, as tsgoPath does for checker paths.
Linux and macOS names are unchanged.
2026-10-26 16:05:57 -05:00