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.
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.
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.
An untyped write of a present declared field of a live record capsule now
stores into the native record through a per-shape `set` hook (checked like
the commit, refusing to the snapshot path for values the field cannot hold),
so the capsule keeps answering from the record instead of turning into a
permanent snapshot. Object.assign from a plain object takes the same path,
and checked conversions and matches read a capsule through a fresh,
uncached snapshot.
A checked conversion of a record capsule into a record type whose storage
is a same-offset prefix of the capsule's record (and whose extra fields hold
no references) passes the pointer: both names keep one object, writes show
through either, and the record remembers its own capsule parameters, so
every later box of it and the narrower type's reflective surfaces answer
for the whole object (decision D3). Other cross-layout conversions keep the
refreshed copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
The nested trampoline never returns (setcontext back), so the redzones
around its locals stayed poisoned in the shadow of Node's stack, and
Node's own frames tripped over them later (stack-buffer-underflow in
__bzero, sanitized corpus 6822/6823/6831/6832/6894 on macOS). Linux's
ASan clears a context's stack in its swapcontext interceptor; macOS's has
none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`skip || (flag ? warnA() : warnB())`, with void calls in both arms, as
React's development warnings write it, failed with SC9001 "ternary must
not be void": the statement lowered the logical as a value, and a
conditional of void calls has none. A logical whose result is discarded
now lowers as `if (!skip) ...` (or `if (skip) ...` for `&&`) when its
right operand needs no value: its type maps to void, or it is a logical
or comma expression whose last operand needs none. This holds in
statements and in a for loop's initializer and update, and for a
generic callback's conditional whose type parameter maps to void.
Closes#2322
Co-authored-by: MathurAditya724 <57684218+MathurAditya724@users.noreply.github.com>
The runtime vendored zlib 1.3.1 and archived it at -Os, and host C-lane
builds linked the system libz, so deflate, raw deflate and gzip output
differed from Node's at every level. The vendored copy is now Node
v24.22.0's deps/zlib (Chromium's zlib, 1.3.2.1-motley-285e94b), built at
-O3 with the units and defines Node's zlib.gyp selects per target: the
SSSE3 Adler-32 and SSE2 inflate and slide-hash code on x86-64, the NEON
and ARMv8 CRC-32 code on arm64, and the portable code on wasm32. The SIMD
units check the CPU at run time and give the same output, so compressed
bytes match Node's on every target.
zlibInputs (runtime-pack-common/scripts/zlib-inputs.mjs for the runtime
packs, vendor-inputs.ts for Zig cross builds and the sanitized runtime)
holds the per-target table, and a unit test keeps the two identical.
Every C-lane driver now links the vendored objects instead of -lz.
Chromium's BSD license ships beside zlib's in the runtime packs.
Corpus 8405 compares compressed bytes with Node across levels,
strategies, memLevel, windowBits, dictionaries and flushing streams;
8406 covers chunked inflation with small input pieces and output chunks.
An integer clone reads a number array's element as an 8-byte double. A
number array proven to hold only int32s keeps 4-byte int32 slots
(SCR_ELEM_I32), so the clone read such an array at the wrong width: it
returned wrong values, and near the array's end it read past the slots.
The clone now tests the array's element kind: int32 slots load as 4-byte
int32s, double slots load as doubles and keep the exact-int32 test, and
any other storage bails to the ordinary body.
New corpus program: integer-clone-int32-slots.ts.
BSD script takes the command after the transcript file, needs a stdin
that is not a socket, and echoes the EOF it forwards from /dev/null.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>