Commit Graph
264 Commits
Author SHA1 Message Date
Chris Tate d5ac31f812 Raise through cached unwind plans on x86-64 Linux
scr_unwind_raise walks the stack with plans cached per return address.
The first raise through a return address finds the function's FDE
through the .eh_frame_hdr of the object that contains it, runs the CFI
up to the call, and finds the call site's landing pad in the LSDA; the
plan records the frame's CFA rule, where its callee-saved registers are
saved, and the landing pad. Later raises look the plans up, restore the
registers, and jump to the first landing pad. Plans are inserted under
a lock and read without one.

Landing pads continue the unwind with scr_unwind_resume instead of
`resume`, so the walk goes on from their frame. A frame whose CFI,
pointer encodings or personality the plans do not express, or a return
address without an FDE, leaves the unwind to libgcc's forced unwind
from the frame the walk started at, as before. Other Linux targets keep
libgcc's unwinder; their scr_unwind_resume calls _Unwind_Resume.

bench:runtime gains layered-errors, a workload whose errors are thrown
below small functions that hold references and caught per entry in a
loop, and the corpus gains unwind-landing-pad-chains, which pins landing
pads that continue the unwind, including in frames LLVM inlines into a
caller whose handler takes the exception.
2026-10-28 00:22:04 -05:00
Chris Tate 93081ea679 Host the runtime's event loop on Node's libuv once Node loads (NI-3) (#1696) 2026-10-27 22:19:38 -05:00
Chris Tate ca4a162e35 Merge main 2026-10-27 20:40:14 -05:00
Chris Tate 95e715febd Optimize the speed runtime units with a committed runtime profile
The LLVM helper's runtime-unit now optimizes a unit itself (--opt-level),
with IR instrumentation (--profile-generate) or a profile (--profile-use),
through the same pipeline as program code. The Linux x64 pack's speed
units are compiled to unoptimized bitcode and optimized there with
packages/runtime-pack-common/profiles/linux-x64-gnu.profdata.gz, so one
LLVM writes and reads every profile. Release and library flavors are
unchanged. The pack manifest records how the profile matched the units.

Code the runtime profile finds cold is optimized for speed, not size: a
runtime function the training programs rarely call can be hot in another
program. scr_message.c is optimized without the profile.

scripts/refresh-runtime-profile.mjs trains the profile on the runtime
benchmark workloads and, optionally, a tsc-ts checkout, and keeps only the
records of functions the instrumented runtime units define
(merge-profiles --keep-functions). Tests check that the profile still
matches the runtime and names only runtime functions.
2026-10-27 20:11:44 -05:00
Chris Tate b16c739d34 Classify zero-cost gate differences instead of failing every runtime change (#2271) 2026-10-27 19:27:25 -05:00
Chris Tate d2595a4a70 Merge main 2026-10-27 18:49:24 -05:00
Chris Tate 08fbecb7e7 Excuse only reproduced differences as nondeterministic, and read vendor archive symbols
A program is nondeterministic only when every artifact that differs also
differs between two baseline builds; a deterministic difference in another
artifact still fails the gate. The runtime-only check now reads the symbols
of changed vendor archives as well as runtime objects.
2026-10-27 17:55:44 -05:00
Chris Tate 1a86f94239 Merge main 2026-10-27 16:45:14 -05:00
Chris Tate bb0513c03c Collect the native compiler's profile in two phases
`scripts/refresh-cli-profile.mjs --context-sensitive` runs the training twice. The second run uses a compiler that the first profile optimizes and that is instrumented again after inlining, and it merges those counts into the first profile. Builds with the result also order each function's blocks by the counts and split cold blocks off (context-sensitive profiles). `--base-profile` runs only the second phase. `scripts/build-native-cli.mts` builds such a compiler when `SCRIPTC_NATIVE_PROFILE_GENERATE` and `SCRIPTC_NATIVE_PROFILE_USE` are both set. The release workflow's cli-profile job collects the profile this way, with a 120-minute timeout.
2026-10-27 11:39:28 -05:00
Chris Tate 3f81117a2d Record each reachable-emit unit's lowering as plain data, checked by a serial mode (#2410) 2026-10-27 10:07:09 -05: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 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 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 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 df77671e33 Merge main 2026-10-26 23:13:34 -05:00
Chris Tate 80b851d65f Write untyped stores into live records and keep one object across prefix casts (#2366) 2026-10-26 22:56:27 -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 ad29d598f5 Merge main into the untyped-store branch 2026-10-26 21:44:47 -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 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 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 e58def0299 Update tests for the record-ops store hook and the untyped-writes benchmark
- forin-keys.test.ts matches the record-ops table with its eighth field, the
  store hook, which is null for a record no untyped code writes.
- bench/record-untyped-writes writes typed records through `unknown` and
  `Object.assign` views, so it links dynamic-tier symbols; DYNAMIC_WORKLOADS
  lists it.
- The sanitized self-hosting emission test gets a 600 s timeout. It takes
  about half of the default 300 s on an idle 16-core host, with or without
  these changes, and exceeded the default on a loaded CI shard.
2026-10-26 21:04:31 -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 2b8873bb92 Keep the native compiler's profile matching its functions (#2357) 2026-10-26 18:07:49 -05:00
Malte Ubl e99d3445bf Merge remote-tracking branch 'origin/ni/1-libnode-loader' into HEAD 2026-10-26 15:57:24 -07:00
Chris Tate 5ab76a4d54 Keep the native compiler's profile matching its functions 2026-10-26 15:31:14 -05:00
Chris Tate 2e627dab79 Merge main 2026-10-26 14:45:37 -05:00
Chris Tate b4e9e134f1 Merge main and address review feedback 2026-10-26 13:48:37 -05:00
Malte Ubl 141279b74c Merge remote-tracking branch 'origin/ni/1-libnode-loader' into ni/13-libnode-packs 2026-10-26 11:15:43 -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 68e2556fda Merge main 2026-10-26 12:24:22 -05:00
Chris Tate 34c2f9851d Have the native CLI packages depend on their LLVM helper and runtime pack (#2333) 2026-10-26 11:47:34 -05:00
Chris Tate 26b2fdcc67 Read IR nodes' fields by name in three llvm-emit passes (#2336) 2026-10-26 10:17:48 -05:00
Malte UblandClaude Opus 5.5 5aa6699b3d Build the candidate's own compiler source in the zero-cost gate
The self program compiles the compiler source of the checkout the programs
come from (the baseline by default), so a change adding compiler code the
self-hosted frontend cannot lower passed the gate and failed CI's
self-hosting tests. With the self set, the gate now also builds the
candidate's packages/compiler/src/native/cli.ts with the candidate (LLVM
only, not compared) and fails when that build fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 07:09:02 -07:00
Chris Tate 5a14c0a3dc Merge main 2026-10-26 08:37:13 -05:00
Chris Tate f9b5533ee6 Read IR nodes' fields by name in three llvm-emit passes 2026-10-26 08:13:15 -05:00
Chris Tate d1a0bea84e Have the native CLI packages depend on their LLVM helper and runtime pack
Each @scriptc/cli-<platform> package bundled its platform's LLVM helper
and runtime pack, which are also published as @scriptc/llvm-<platform> and
@scriptc/runtime-<platform>. The CLI packages now depend on those packages
at the same version (workspace:*, which pnpm pack writes as the exact
version) and leave them out: cli-linux-x64-gnu goes from 115 MB to 60 MB
packed.

- build-native-cli.mts --platform-packages, which each CLI package's
  build:native passes, writes a manifest without llvm_package, runtime_pack
  and runtime_packs.
- loadNativeToolchain resolves a missing helper or host runtime pack as
  Node resolves the package from the compiler's directory: nested in the
  platform package, beside it (hoisted, or pnpm's virtual store) or higher
  up. It requires the compiler's version, and an absent or mismatched
  package fails with an error that names it.
- installNativeCli records where the package manager put both packages in
  the installed manifest, with the same checks.
- verify-native-cli.mjs requires the exact dependencies of a platform
  package and checks the resolved packages' versions.
- package-native-cli.mjs bundles the helper and runtime pack into the
  standalone archives, which stay self-contained.
- smoke-native-install.mjs installs the helper and runtime pack tarballs
  with the platform package; the bootstrap's exported package drops its
  bundled copies first.
- release.yml publishes the CLI packages only once every helper and
  runtime pack package resolves on npm, and scriptc only once the CLI
  packages resolve, each wait at most 25 minutes. A rerun skips the
  uploads npm already accepted and waits again.
2026-10-26 07:48:11 -05:00
Chris Tate 641f779fa3 Store identical runtime artifacts once and strip the copied CLI executables (#2301) 2026-10-26 07:06:31 -05:00
Chris Tate d98fce9e27 Merge main 2026-10-26 06:45:43 -05:00
Chris Tate a758ff1b4e Select sanitized runtime variants by unit, and skip stripping without llvm-strip
The native sanitizer build chose the variants to compile by artifact path.
Since the runtime pack stores identical objects once, two variants of a
unit can share a path, so it compiled such a unit for both variants and
linked it twice (duplicate symbols in the sanitized native bootstrap).
selectedRuntimeUnits now returns each selected unit with its variant;
selectRuntimePackArtifacts and the sanitizer both use it.

Staging the native CLI's assets ran llvm-strip unconditionally and failed
on hosts without it. Without llvm-strip on PATH, tsc-ts now keeps its
symbols and the build prints a warning; the release hosts install LLVM.
2026-10-26 06:02:01 -05:00
Chris Tate c3b265f68c Merge main 2026-10-26 04:39:20 -05:00
Chris Tate ae3d388869 Merge main 2026-10-26 03:53:02 -05:00
Chris Tate 71de76b813 Order the native compiler's cold functions by how many builds run them 2026-10-26 02:02:18 -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 a8080733df Build the bootstrap test's own seed without the function layout
The bootstrap test builds its own seed when CI shares none, and that build
still applied the committed function layout, so its LLVM listed functions in
a different order from the native compiler's own emission. Both seed builds
now share one environment, which leaves the layout out.
2026-10-25 23:42:51 -05:00
Chris Tate f2c53ff2f8 Build the bootstrap seed without the function layout
The native bootstrap compares the seed's LLVM, which the Node-hosted
compiler emits, with the LLVM the native compiler emits for itself. The
seed's build applied the committed function layout and the native
compiler has none, so the two listed the same functions in different
orders. The layout only orders functions, so the seed is built without it.
2026-10-25 23:32:41 -05:00
Chris Tate dfe89d7d7b Store identical runtime artifacts once and strip the copied CLI executables
Runtime packs: variants of a unit that do not read their defines, and
flavors that compile a unit the same way, produce identical objects and
bitcode. The pack build now points those manifest entries at one file
(shareIdenticalArtifacts) and deletes the copies. Each link selects one
flavor and one variant per unit, so no link names a shared file twice. The
linux-x64-gnu pack keeps 1,191 of its 1,448 artifact files.

Native CLI packages:
- The comptime evaluator builds with -g0 and the executable strip flags,
  and the Windows build no longer ships its .pdb.
- The copied tsc-ts executable is stripped with llvm-strip, which rewrites
  the linker-signed code signature of Mach-O executables.
- On arm64 macOS, verify-native-cli.mjs --run checks the code signatures
  of the compiler, tsc-ts and the comptime evaluator.
- SCRIPTC_NATIVE_KEEP_SYMBOLS=1 keeps the symbols of all three.
- The copied runtime sources and declarations get LF line endings. A
  Windows checkout converts them to CRLF, so the Windows CLI shipped files
  that differed from @scriptc/runtime's.
2026-10-25 21:55:14 -05:00