Commit Graph
190 Commits
Author SHA1 Message Date
Chris Tate 2ae08e4002 Route more packages and values through Node islands (NI-4c–4g) (#1904) 2026-10-28 21:53:40 -05:00
Chris Tate 061020b2f8 Add --exceptions=rare to unwind through recursive functions (#2481) 2026-10-28 15:17:22 -05:00
Chris Tate 377af617d3 Add --exceptions=rare to unwind through recursive functions
With --exceptions=rare, recursive functions and functions up to three
calls above a throw also propagate exceptions by unwinding on Linux, so
calls to them need no pending-flag test. Functions with a throw of their
own keep the flag. The default, common, keeps the current rules, and
builds without the flag emit the same code as before.

The option is part of the executable cache key; the bootstrap's cached
route leaves builds with it to the full compiler, and coverage and
library builds refuse it.
2026-10-28 14:28:28 -05:00
Malte UblandClaude Opus 5.5 d4445ec628 Merge remote-tracking branch 'origin/ni/i6-subclasses' into ni/i7-routing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:57:10 -07:00
Malte UblandClaude Opus 5.5 753532a3da Merge origin/main into fix/pi-wp2-stdlib-node
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:33:39 -07:00
Malte UblandClaude Opus 5.5 10f974aab9 Merge ni/i6-subclasses into ni/i7-routing
Resolve tests/harness/npm.test.ts by keeping main's npm-suite split and
porting this branch's island KNOWN entry and stderr frame normalisation
into tests/harness/npm-suite.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 08:59:57 -07:00
Malte Ubl e5f57e2093 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-28 08:43:13 -07:00
Malte Ubl 29a31446d3 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-28 08:43:06 -07:00
Malte UblandClaude Opus 5.5 9edf1d3aa3 Merge ni/i6-subclasses into ni/i7-routing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:42:18 -07:00
Malte UblandClaude Opus 5.5 39326c55be runtime: ship child.off/signalCode in a gated unit
Adding the six new entry points (scr_child_off_spawn/_exit/_close/_error,
scr_child_has_signal_code, scr_child_signal_code) to scr_child.c changed
the executable of every program that links the unit, although none of
them calls the new functions and IR and LLVM stayed identical: the extra
functions grow the unit's `.eh_frame` contribution, and the linker's
frame bookkeeping for the retained sections changes with it (the first
visible difference is the phdr sizes at byte offset 208). Appending them
after the unit's own functions does not help — the unit's frame section
still differs, so all 89 corpus programs that link scr_child.o got a
different binary.

Move the entries into scr_child_lifecycle.c, a unit of their own
selected only by programs that use them (feature childLifecycle, the
scr_dyn_handle_props.c pattern: the matrix's optional source list, the
RUNTIME_FEATURE_RULES rule for child.offSpawn/offExit/offClose/offError/
signalCode, and the link-feature plumbing through the executable cache,
CcOptions and RUNTIME_UNITS). scr_child.c keeps its original content and
layout; the new unit reads its registries through scr_child_private.h,
which holds the per-platform ScrChild layout both units compile against.

A program that does not call the new entries links exactly the units it
did before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:17 -07:00
Malte UblandClaude Opus 5.5 efc22a2fd3 Box user module namespaces as dynamic values
A `file:` module namespace handed to typed code now boxes as Node's
namespace object instead of the "converting typed values to 'unknown'"
fence: `const u: unknown = ns`, `take(ns)`, a `Promise<typeof
import("./m")>` widened to `Promise<unknown>` (or `Promise<unknown> |
undefined`), and `await import("./m")` in an `unknown` slot all see one
non-extensible null-prototype object per module whose own properties are
the module's exports in Node's sorted order. They are enumerable
read-only accessors whose reads call the compiled namespace getter, so
they stay live, and the object carries Symbol.toStringTag "Module",
Node's strict-mode write TypeErrors, the not-extensible add error and
the ToPrimitive throw. `node:` builtins have no compiled export table
and keep their SC1101 fence.

The object is built by a new gated runtime unit,
packages/runtime/src/scr_module_namespace_dyn.c, selected by the
`moduleNamespaceDyn` runtime feature (`module.namespaceDyn` in the IR).
It adds no field to ScrDynHandleOps and no branch to a shared unit: the
core reaches it through the ordinary SCR_DYN_OBJ paths, so key listing,
for-in, `in`, JSON, identity, property reads and assignment all reuse
the existing dispatch. The compiler reuses the getter lifted for
module.namespaceValue (existing programs' IR is unchanged) and adds
moduleNamespacePromiseDynValue, one lifted async mapper per module for
the promise slots.

Zero cost: a program that never boxes a namespace links exactly what it
did. The zero-cost gate reports every program identical, with
byte-identical IR and LLVM; the runtime pack's only difference is the
new unit (0 changed runtime functions), and among the namespace corpus
programs only 8412 links it.

Evidence: tests/corpus/8412-module-namespace-untyped is a differential
program against Node in both the plain and sanitized lanes, and the
diagnostics snapshots and TypeScript 7 baselines stay unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:45 -07:00
Chris Tate d9481023c3 Annotate functions a stale profile misses from its block counts
A profile annotates a function only while its control flow hashes as it did
when the profile was collected, so compiler and source changes leave the
changed functions without counts. A --profile-use build now reads a
block-count file beside the profile (app.blocks beside app.profdata). Right
after the annotation before inlining, the helper aligns each uncounted
function's blocks with its recorded blocks by label kind and successor
count. Paired blocks take their recorded counts and their edges the recorded
edge counts, by target or, between targets whose labels changed, by
position. LLVM's flow inference fills in the rest, avoiding the edges the
function's own llvm.expect weights make unlikely. The helper then sets the
entry count, branch weights and hot or cold attribute as the profile
annotation does.

SCRIPTC_RECORD_PROFILE_BLOCKS names the file a --profile-use build writes
for the functions its profile annotates; such a build reuses no cached
artifacts. The block file keys every cache, the build report counts the
functions it annotated, and the staleness warning counts only the functions
left without counts. A single-module build with a context-sensitive profile
no longer counts annotated, mismatched and missing functions a second time
after inlining. scripts/refresh-cli-profile.mjs records the file in the
second phase and build-native-cli.mts reads it beside the profile.
2026-10-28 04:50:01 -05:00
Chris Tate 1278729267 Merge main 2026-10-27 23:21:09 -05:00
Chris Tate 248030cdd8 Merge main 2026-10-27 19:47:24 -05:00
Chris Tate 7d670f0e2a Merge main 2026-10-27 18:53:59 -05:00
Chris Tate 8104795c17 Merge main 2026-10-27 18:15:25 -05:00
Chris Tate 1a86f94239 Merge main 2026-10-27 16:45:14 -05:00
Chris Tate 8f3a8e3e63 Merge main 2026-10-27 09:58:30 -05:00
Malte Ubl 195ed79f00 Merge remote-tracking branch 'origin/main' into perf/static-objects
# Conflicts:
#	tests/harness/npm.test.ts
2026-10-27 07:21:50 -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 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 Ubl 7f0488a411 Merge remote-tracking branch 'origin/main' into perf/static-objects 2026-10-27 05:35:56 -07: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 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 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 659a74acc4 Merge main 2026-10-27 02:27:10 -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 140990d257 Add --large-pages: move executable code onto transparent huge pages
A large program's hot code spans hundreds of 4 KiB pages, more than the
instruction TLB holds. tsc-ts has 12 MB of code, and 90% of its
main-thread samples at --checkers 1 fall in 274 distinct code pages.

`scriptc build --large-pages` links the new runtime unit
scr_text_pages.c, whose constructor moves the executable's code onto
2 MiB transparent huge pages before main. It copies the executable
segment, from its first to its last 2 MiB boundary, into anonymous memory
advised MADV_HUGEPAGE, makes the copy read-only and executable, and moves
it over the original range with one mremap, so addresses and instructions
stay the same. The move runs only on Linux, only when THP is "always" or
"madvise", and only in executables. If a step fails, the code stays
file-mapped. Like Node's --use-largepages, it is off by default: without
the flag the unit is not linked, and executables are unchanged.

The move costs about 3 ms per 10 MiB of code at startup, and all of the
moved code stays resident as private memory. The flag is part of the
executable cache key and the runtime-pack selection, and is refused for
non-executable output, like --detached-exit.
2026-10-26 23:41:46 -05:00
Malte Ubl b2fc8c1cd8 Merge remote-tracking branch 'origin/ni/i6-subclasses' into HEAD 2026-10-26 11:28:52 -07:00
Malte Ubl 3a5aab5b2f Merge remote-tracking branch 'origin/ni/i1-stack-bound' into HEAD
# Conflicts:
#	packages/compiler/src/cli/command.ts
#	packages/compiler/src/executable/executable-cache.ts
#	packages/compiler/src/frontend/lowering/builtins/module-resolution.ts
#	packages/compiler/src/frontend/lowering/lowerer.ts
#	packages/compiler/src/index.ts
#	packages/runtime/src/scr_json.c
#	tests/harness/npm.test.ts
2026-10-26 11:22:14 -07:00
Malte Ubl 5817be4d0f Merge remote-tracking branch 'origin/main' into ni/1-libnode-loader
# Conflicts:
#	packages/compiler/src/diagnostics/diagnostic.ts
#	packages/compiler/src/ir/function-references.test.ts
2026-10-26 11:08:44 -07:00
Chris Tate 06f7b8631f Merge perf/shape-overlay-cost 2026-10-26 11:53:34 -05:00
Chris Tate c3b265f68c Merge main 2026-10-26 04:39:20 -05:00
Chris Tate 0ba138b57e Merge main 2026-10-26 03:25:54 -05:00
Chris Tate 98ed422405 Merge ni/i6-subclasses and cascade ES-module routing 2026-10-26 01:00:45 -05:00
Chris Tate 7e3d10fa13 Lay out the native compiler's functions by the code each kind of run executes (#2265) 2026-10-26 00:29:02 -05:00
Chris Tate d48e060076 Merge main 2026-10-25 22:34:40 -05:00
Chris Tate f591e8e6d7 Print how a --profile-use build's profile matched
`scriptc build --profile-use` prints one line on stderr with the number of
functions the profile annotated, the number whose control flow changed since
it was collected (mismatched), and the number it has no record of. When more
than 10% of the functions the profile describes are mismatched, it also prints
a warning to collect the profile again. The build still succeeds.

Both the compiler library's build and the native compiler's build report the
match. Cached builds report the match of the build that produced them: the
native codegen and object caches store it beside the cached objects, and the
executable cache stores it in its entry.
2026-10-25 22:14:25 -05:00
Chris Tate cc2266ab1d Add --cpu-dispatch for x86-64 executables (#2288) 2026-10-25 21:36:00 -05:00
Chris Tate 59e78f298a Name the package declaration and its patch when a declared-type check fails (#2268) 2026-10-25 21:20:34 -05:00
Chris Tate 28b76f7b25 Merge ni/i1-stack-bound 2026-10-25 21:08:37 -05:00
Malte UblandClaude Opus 5.5 7687cf2d26 Type island packages from their declarations in failed builds and generics
When a native build failed and the packages it compiled with runtime fences
then failed as islands too, the native build's diagnostics were reported.
That build types those packages from their JavaScript, where declared-only
types (interfaces, `Static<T>`-style aliases) are `any`, so typed user code
reported cascades the island build does not have (a schema library's `TSchema`).
The island build's errors are reported instead.

In a generic function instance over island types (`<T extends TSchema>`),
a `T` value bound to dyn now crosses into the island the way `any` does,
instead of being refused as a native object.

Corpus 6829 types user code with an island package's interfaces, generic
aliases and a generic function; node-island.test.ts pins the reported
diagnostics of a failed build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:38:29 -07:00
Chris Tate 8981b5c0a2 Resolve computed createRequire specifiers at run time (#2079) 2026-10-25 20:29:51 -05:00
Chris Tate 07c5e79ffd Add --cpu-dispatch: x86-64-v3 copies of the program's code chosen at startup
With --cpu-dispatch, an x86-64 executable carries two copies of each
program function: the baseline code and a copy compiled for x86-64-v3.
main reads CPUID and XGETBV once. Calls between the program's functions
go to the caller's copy. A function that is also named outside the
copies (a function value, a call from main, a runtime callback or a
coroutine body) keeps a dispatcher under its own name, which tests the
level and tail-calls one copy. Functions without such a use get no
dispatcher, so their copies have the callers the original had and LLVM
inlines them the same way.

The executable still runs on any x86-64 CPU. SCRIPTC_CPU_LEVEL caps the
level at run time. The option is off by default, applies to executables
without a --target-cpu level, and is part of the executable cache key.
bench:runtime takes --baseline-cpu-dispatch and --candidate-cpu-dispatch,
and the LLVM differential suite runs each program at levels 0 and 3 with
SCRIPTC_CPU_DISPATCH=1.
2026-10-25 20:00:16 -05:00
Chris Tate c3ac3f6882 Merge main 2026-10-25 18:52:38 -05:00
Malte UblandClaude Opus 5.5 56e8417746 Name the package declaration and its patch when a declared-type check fails
A value that contradicts a type an npm package declares used to throw
"expected number at $, got undefined" and nothing else. Such a check now
names the declaration and the type patch that admits the value:

    profilekit@3.1.0 declares Profile.email as string (index.d.ts:7)
    if that declaration is wrong, patch it in package.json: ...

Origins are found for values a package function returns under a written
return type, for properties of records typed by a package-declared type
(record projections into such a type are keyed by it), and for
declaration-boundary casts, whose origin the projection records from the
declaration-world checker. Generic declarations get none: their types
come from the program's own instantiation.

Checked conversions carry dynCheck.origin, an index into
IrModule.declOrigins; the site's existing cold exception block calls
scr_type_origin_note (new runtime unit behind the typeOrigins feature),
which appends the note to the pending TypeError and derives the selector
suffix and the admitted kind from the failure. Union extractions get the
note per arm at compile time. The hot path is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 15:06:50 -07:00
Chris Tate e7a088f904 Key the executable cache by the function layout's contents
The early executable cache key covered the profile but not the function
layout, so a build that added, changed or edited a layout could restore
the executable built with the previous order. The key now includes the
digest of the layout file's bytes. An unreadable layout fails the build
with SC3002 before the cache lookup.
2026-10-25 16:58:11 -05:00
Malte Ubl b04fea812d Merge remote-tracking branch 'origin/fix/js-dynamic-lowering' into HEAD
# Conflicts:
#	packages/compiler/src/frontend/pipeline.ts
#	packages/compiler/src/frontend/shape-inference/overlay.ts
#	packages/compiler/src/frontend/shape-inference/static-objects.ts
#	packages/compiler/test/shape-overlay-render.test.ts
2026-10-25 13:46:26 -07:00