The cycle collector widens the growth fraction that schedules full passes
up to 16 times the heap left by the last pass while passes find little.
Lowering a large program leaves hundreds of megabytes of cyclic garbage,
and whether a scheduled pass reclaims it before the build's peak depends
on where the growth rule fires: small changes to the compiler moved the
peak of a tsc-ts build between 1.66 and 2.05 GB.
The native compiler now sets the growth cap to 2 at startup, as
SCR_CYCLE_GROWTH_CAP=2 does, so a full pass runs once the heap has grown
to three times its size after the last one. scr_cyc_set_growth_cap sets
the cap for a program from its own code; the environment variable still
takes precedence, and child processes are not affected.
On hosts that can fork, the native compiler now emits a large module's
function bodies on worker processes (SCRIPTC_EMIT_WORKERS, 4 by default).
The function list splits into contiguous ranges, each emitted by a forked
child of the emitter, and the ranges merge in function order into the text
one emitter gives. A range whose worker fails is emitted in the parent.
Modules under 2,000 functions, Node and Windows keep the serial path.
A range's emitter records an ordered journal (emit-journal.ts):
- every helper it numbers from a counter (the dyn conversions and
matchers, message listeners and readers, JSON writers, publication
walkers, stream adapters and resolve thunks), recorded when its number
is taken, with the entry point and arguments that create it;
- the first request of each key of a module-level table (declarations,
literals, C strings, unit and absent instances, retype guards, regex and
template-strings instances, map descriptors, alias tags) made outside a
helper's definition;
- unkeyed sites (inline caches, speculative loop sites) taken outside a
definition. A speculative loop site's global is now `@sc_spec_bails_N`,
named like the other numbered sites.
The merging emitter replays each range's journal in order. Replay calls
the same entry points, so every helper gets the number and definition the
serial emission gives it, and table entries land in the serial order. The
range's text is then renamed to the merged numbers in one pass. Sets and
flags the ranges set (await callees, function values, RC helper requests)
merge after the replay. LlvmTargetOptions.functionChunks runs the same
merge in one process, for tests.
The fork, exit, wait and lock primitives come from the native host
(host.c, process-fork.ts); the native compiler passes them to the emitter
as LlvmTargetOptions.forkPrimitives.
The frontend's syntax trees and program state refer to each other, so
reference counting frees almost none of them when a build disposes the
frontend. Whether a cycle pass reclaimed them before the build ended
depended on where the collector's schedule happened to be. Passes during
the frontend found its data live and widened the growth fractions, so the
next pass waited until the heap had grown well past its size at disposal,
and the dead frontend and the emitter's garbage stayed until the build
ended.
The native compiler now runs scr_collect_cycles_phase right after it
disposes the frontend, through a host function. The runtime entry point
sweeps, then restarts the growth fractions and the death rule's backoff
from the heap the sweep leaves, as after a productive pass. Under Node
the hook is unset.
* feat: ship the self-hosted native scriptc CLI
- Run the shared compiler and CLI natively, including library builds and compile-time evaluation.
- Install native platform packages and ship relocatable standalone distributions.
- Preserve program semantics and reuse validated build artifacts for fast rebuilds.
* fix: complete native CLI integration and validation
- Preserve library diagnostics and run recursive compiler work on the main stack.
- Restore packaged helper permissions and exercise native npm and Wasm library builds.
- Recover interrupted Sandbox status probes within the original command deadline.