1535 Commits
Author SHA1 Message Date
Chris Tate 91c28ebf95 Merge pull request #901 from vercel-labs/ctate/prepare-v0-3-ee879b
Prepare v0.3.0
v0.3.0
2026-10-12 15:59:25 -05:00
Chris Tate ef25cf119d Prepare v0.3.0 2026-10-12 15:57:06 -05:00
Chris Tate 2107a3c841 Build every runtime pack without UBSan in dev flavors (#896) 2026-10-12 15:51:36 -05:00
Chris Tate 19856354d7 Build every runtime pack without UBSan in dev flavors 2026-10-12 15:15:34 -05:00
Chris Tate 93fb2060ee Read class property bags without creating them (#894) 2026-10-12 14:27:12 -05:00
Chris Tate bbf2c34778 process.constrainedMemory() and availableMemory() read the process's own cgroup, like libuv (#893) 2026-10-12 14:21:30 -05:00
Chris Tate cca079d705 Find multi-byte needles without a memchr call per first-byte hit (#892) 2026-10-12 14:20:36 -05:00
Chris Tate 7044549622 Reject absent string keys before probing the map (#891) 2026-10-12 14:11:29 -05:00
Chris Tate 8a3d747b1d Merge main into fix/read-only-class-bags 2026-10-12 13:20:11 -05:00
Chris Tate 8c035be7de Merge main into fix/constrained-memory-libuv 2026-10-12 13:18:23 -05:00
Chris Tate 8c4bb3277c Merge main into perf/string-search-pairs 2026-10-12 13:16:39 -05:00
Chris Tate 829a326bb7 Merge main into perf/string-maps 2026-10-12 13:14:33 -05:00
Chris Tate 054eb04220 Merge pull request #895 from vercel-labs/ctate/make-scriptc-compilation-0c86a3
Make scriptc compilation native-only
2026-10-12 13:12:25 -05:00
Chris Tate 5a1638ce55 Update the node-types fence snapshot for native-only hints 2026-10-12 13:10:43 -05:00
Chris Tate 7e6294ca0f Make scriptc compilation native-only 2026-10-12 12:25:30 -05:00
Malte Ubl 6ef5317935 Keep read-only class bag reads compact
The read-only bag accessors made each tracked-field read site a nested
ternary with two accessor calls, and each method probe evaluated the bag
twice. Large JavaScript programs have a branch per class and property
(and per-class key tables copy the computed fallback), so three.js grew
from 703 MB to 946 MB of IR and its in-process compile ran out of the
2 GB worker heap in CI.

Move the absent-bag handling into shared helpers instead:
%dyn.class.readPropertiesOrEmpty is rebuilt in finalize like
%dyn.class.readProperties but returns a fresh, unstored empty object for
an instance without a bag, and %dyn.class.bagHas/bagHasComputed test a
key on a possibly undefined bag. Every read site is one call again, and
the three.js IR is back to 719 MB (2.16 GB peak RSS, main 2.15 GB).
2026-10-12 10:21:53 -07:00
Chris Tate 35ab79ecc3 Merge pull request #890 from vercel-labs/ctate/prepare-v0-2-f94a93
Prepare v0.2.10
v0.2.10
2026-10-12 10:37:12 -05:00
Malte Ubl b943dd574f Keep reads through undefined receivers throwing
The read-only bag accessor returns the receiver itself for non-instances,
so an undefined bag can also mean an undefined receiver. Substitute the
empty bag only for instances: a read through undefined must still throw
Node's "Cannot read properties of undefined" TypeError (1544, 2162, 2449,
js-optional-class-fields).
2026-10-12 08:36:15 -07:00
Chris Tate d75d7a22f0 Prepare v0.2.10 2026-10-12 10:31:02 -05:00
Malte Ubl d5c31a933c Read class property bags without creating them
A class instance keeps its own dynamic properties in a lazily created bag
(`%dynProperties`). Untyped reads went through the bag accessor that
creates and stores the bag on first use: selecting a method checks the bag
for an own property shadowing it, and the named and computed fallbacks,
`in`/own probes, Reflect.get, for-in and accessor reads all looked their key
up there. So a pure read stored into the object. On a published object the
frozen-object guard rejects that store with "Cannot assign to read only
property '%dynProperties'", where Node reads without error.

Read-only bag accessors now never store. The untyped
%dyn.class.readProperties is rebuilt from the settled bag dispatch without
its lazy stores, and typed read helpers return a prototype-backed bag
instead of storing it. Every read path uses the read-only accessor: key
tests treat an absent bag as having no own keys, and lookups that must see
Object.prototype read from a fresh, unstored empty object. Writes keep the
storing accessor.

Found with tsc-ts at --checkers 8 on vercel/eve: a checker thread read
`host.commonSourceDirectory()` through an untyped interface value whose
object is the published Program.
2026-10-12 08:29:56 -07:00
Chris Tate a968b15166 Merge pull request #881 from vercel-labs/feat/program-lifetime-publication
Publish ProgramLifetime graphs: program-lifetime instances count 2^63 until published
2026-10-12 10:29:17 -05:00
Chris Tate 043c656bbd Merge main into feat/program-lifetime-publication 2026-10-12 09:38:24 -05:00
Chris Tate 91bd61f6c9 Merge pull request #888 from vercel-labs/perf/codegen9
Keep runtime throw paths out of line; signed conversions for array counters
2026-10-12 09:27:29 -05:00
Malte Ubl 09118a3c25 Read the process's own cgroup in process.constrainedMemory() like libuv
scr_constrained_memory read /sys/fs/cgroup/memory.max, the root of the cgroup mount, and answered
0 for "max". Node's process.constrainedMemory() is libuv's uv_get_constrained_memory (libuv 1.51
in Node 24.15): the cgroup named by /proc/self/cgroup, the smaller of memory.max and memory.high
(v2) or of memory.limit_in_bytes and memory.soft_limit_in_bytes (v1, falling back to the
controller's root), with "max" and v1's page-aligned LONG_MAX read as UINT64_MAX and an unreadable
file making the answer 0. The two only agreed inside a cgroup namespace; in a nested cgroup or a
systemd scope the native build reported the root's limit (17179869184 in a 16 GB sandbox where Node
answers 18446744073709552000, or 2147483648 under memory.high=2G), so programs that size
themselves by it ran out of memory.

process.availableMemory() follows uv_get_available_memory too: under a constraint no larger than the
machine's memory, the constraint minus the cgroup's current usage; otherwise MemAvailable, then
sysinfo's freeram.

The corpus program prints the exact constraint, so Node and the native build must read the same
cgroup.
2026-10-12 07:22:54 -07:00
Malte Ubl 5201ab9340 Find multi-byte needles without a memchr call per first-byte hit
scr_byte_find called memchr for the needle's first byte and memcmp at
every hit. Path searches such as indexOf('/./') or indexOf('//') hit the
first byte at every separator, so each search paid two library calls per
path segment.

Haystacks of up to 256 bytes now test eight candidate starts per step for
the first two needle bytes with a SWAR zero-byte test (no false negatives;
false positives are rejected by the byte checks) and compare the rest only
at real two-byte matches. Longer haystacks keep memchr but check the
second byte inline before memcmp.

The tsc-ts parser (tRPC, parse only) runs 1.1% fewer instructions; string
search there drops from 111M to 82M instructions.

The new corpus program covers both strategies, every match offset around
the 8-byte steps and the 256-byte cut, near misses, the 0x01 false
positive pattern, and non-ASCII needles and haystacks.
2026-10-12 07:22:54 -07:00
Malte Ubl da2494c154 Record the order baseline for the map filter corpus program 2026-10-12 07:22:53 -07:00
Malte Ubl b0fec9e55c Find string map keys without a stack frame
scr_map_entry_str serves every string-key Map/Set read of generated code.
For a key with a cached hash in linear or compact-bucket storage it is now
a leaf: the filter test, the probe and the identity comparison run without
saving registers. A hash match on another string object tail-calls the
byte comparison; uncached hashes and wide tables take the complete lookup.
2026-10-12 07:22:52 -07:00
Malte Ubl 726dc34971 Keep the map filter without extra passes
Growth rebuilds the bucket table from unchanged entries, so the filter it
already has stays valid; only compaction drops entries, and it now
recomputes the filter in its copy loop instead of a second pass.
2026-10-12 07:22:52 -07:00
Malte Ubl 5025a24dc6 Test the map filter inline at string-key lookups
String-key Map.get (reference and scalar values) and Map/Set has now share
the entry lookup through an internal sc_map_entry_str helper. On 64-bit
targets it reads the key's cached hash from its capacity word and returns
null when the map's filter lacks the key's bits, so most misses cost no
call. Keys without a cached hash and possible hits take scr_map_entry_str.
2026-10-12 07:22:52 -07:00
Malte Ubl f04681f0b6 Reject absent map keys through a filter in the map header
Every live entry's hash sets two bits of a new 64-bit ScrMap.filter, so a
lookup whose bits are not both set is absent without loading the bucket
table or the entries. String-keyed lookups in tsc-ts are mostly misses
(Map.get 67% on Excalidraw, has 87%), and the filter rejects 84-88% of
the misses on non-empty maps. Deletions leave their bits; rebuilds,
compaction and clear recompute the filter from the live entries.

Lookups also return the entry index straight from the probe instead of
reading the bucket again. The filter sits after nlive, so the backend's
%ScrMapIx fields for entries and the direct index move by one.
2026-10-12 07:22:52 -07:00
Chris Tate bfbe047cbb Read SCR_COLD as an attribute in the runtime ABI guard 2026-10-12 09:21:23 -05:00
Chris Tate 2391b0ebf3 Merge pull request #869 from vercel-labs/perf/parse-round4
Read charCodeAt at proven integer indices without a double round trip
2026-10-12 08:59:12 -05:00
Chris Tate 1f98312881 Merge pull request #880 from vercel-labs/feat/acyclic-contract
Acyclic contract type
2026-10-12 08:58:49 -05:00
Chris Tate bc9a33c9c7 Expect the runtime string-length bound in the bitwise range test 2026-10-12 08:54:54 -05:00
Chris Tate 936e9c5dae Merge main into perf/parse-round4 2026-10-12 08:51:31 -05:00
Chris Tate da309e1655 Identity bitwise forms over proven int32 operands return the operand (#878) 2026-10-12 08:49:35 -05:00
Chris Tate 7cf57f9ed5 Merge main into feat/acyclic-contract 2026-10-12 08:43:05 -05:00
Chris Tate d836ce0b83 try/catch discharges NoThrow (#879) 2026-10-12 08:41:35 -05:00
Malte Ubl e1ccfeeb84 codegen: signed conversions for array counters and lengths
Integer induction counters live in unsigned 64-bit slots, so every read
as a number was a uitofp i64 (a 6-7 instruction sequence on x86-64) and
every nested start an fptoui. The runtime's push path also round-tripped
the array length through a double for its bound check.

The values are at most 2^32 - 1: on 64-bit targets convert counters with
sitofp and loop starts with fptosi, convert array lengths and validated
indices through int64_t in scr_array.c, and test the push bound on the
integer length. 32-bit targets keep the unsigned conversions.

tsc-ts Excalidraw: uitofp i64 sequences 32.5 M -> 4.0 M, -0.66%
instructions on top of the throw-path change.
2026-10-12 06:40:50 -07:00
Malte Ubl b5e4bcb54e runtime: keep throw entry points cold and out of line
With runtime bitcode import, scr_throw_obj, scr_error_new and the other
throw entry points were inlined into the error paths of small compiled
functions. The inlined bodies needed callee-saved registers that every
call then saved and restored, and they made the helpers too large to
inline into their callers (1047 inlined throw bodies in tsc-ts).

Mark the 25 functions that only run while raising a JS exception
cold and noinline. The LLVM helper never imports noinline bodies, so
the throw paths stay calls. tsc-ts Excalidraw: -0.44% instructions.
2026-10-12 06:40:50 -07:00
Chris Tate 0b239089e7 Cover the integer-index charCodeAt helper in the borrowed-input ABI test 2026-10-12 08:25:43 -05:00
Chris Tate 85736170ae Merge main into feat/program-lifetime-publication 2026-10-12 08:13:15 -05:00
Chris Tate 0d2bcf9d97 Merge main into feat/acyclic-contract 2026-10-12 08:10:24 -05:00
Chris Tate 88b4e89e55 Merge main into feat/nothrow-try-catch 2026-10-12 08:07:54 -05:00
Chris Tate 0a4bf78d4a Merge main into perf/int32-or-zero-peephole 2026-10-12 08:05:16 -05:00
Chris Tate 44d22d785c Merge pull request #870 from vercel-labs/feat/contract-types
Contract types: NoThrow, Int32 and Immutable as checked type annotations
2026-10-12 07:57:28 -05:00
Chris Tate 83248651e2 Publish Immutable factory arrays only after provable index writes 2026-10-12 02:33:29 -05:00
Chris Tate f77d68f600 Keep the RC entry points of array elements in one shared descriptor (#882) 2026-10-12 01:59:44 -05:00
Chris Tate 48b5935337 Unshare repeated IR nodes before emission and contract checks 2026-10-12 01:47:05 -05:00
Chris Tate c5c6657c4e Drop guards on private constructor stores and borrow construction-only fields (#838) 2026-10-12 01:20:56 -05:00