Commit Graph
4034 Commits
Author SHA1 Message Date
Chris Tate 753c64febc Test for-in key presence in the overflow map instead of a fresh key list (#1859) 2026-10-22 16:35:49 -05:00
Chris Tate bb34d61b55 Read optional elements at a proven integer index without a round trip (#1845) 2026-10-22 16:32:32 -05:00
Chris Tate ee865bcb0d Support the legacy createCipheriv modes and crypto.checkPrime/generatePrime (#1832) 2026-10-22 16:32:12 -05:00
Chris Tate 48a08dce8e Store arrays of ProgramLifetime instances in 4-byte slots on Linux (#1847) 2026-10-22 16:29:42 -05:00
Chris Tate d2408a09ad Match Node for reduce accumulators and callback arguments.length (#1828) 2026-10-22 16:27:50 -05:00
Chris Tate f45d8ba6dd Version while loops over typed arrays on their counters' bounds (#1841) 2026-10-22 16:27:30 -05:00
Chris Tate 7ef356137f Read the union payload inline when comparing it with a number or boolean (#1839) 2026-10-22 16:26:44 -05:00
Chris Tate e74bd658fd Inline Map retains and Map.size in release builds (#1862) 2026-10-22 16:24:56 -05:00
Chris Tate 41af221ed1 Version functions on number parameters that bound their loops (#1853) 2026-10-22 16:21:01 -05:00
Chris Tate 4d049fbb8c Merge pull request #1850 from vercel-labs/ctate/prepare-v0-4-37aaa7
Prepare v0.4.2 release
v0.4.2
2026-10-22 15:09:47 -05:00
Chris Tate f7ef188d96 Give the sanitized bootstrap seed job swap (#1864) 2026-10-22 14:34:41 -05:00
Chris Tate 5aae5531e0 Give the sanitized bootstrap seed job swap
The sanitized seed compiles the compiler's 700 MB LLVM module with one clang
process under AddressSanitizer. The compile peaks near 14 GB while the Node
process that emitted the module still holds about 6 GB, which exceeds the
runner's 16 GB of memory. The job now enlarges swap to 24 GB before it builds
the seed, as the sanitized emitter job does.
2026-10-22 14:30:37 -05:00
Chris Tate 83b2b34b6e Inline Map retains and Map.size in release builds
Release builds no longer import runtime bitcode, so program code called scr_map_retain_v for every Map read out of a container and scr_map_size for every size read. A Map retain now uses the emitted increment helper, as string retains do, and Map.size reads the live count through an internal helper.
2026-10-22 14:19:04 -05:00
Chris Tate d83c676705 Store compact values of string-key maps in 4-byte slots on Linux 2026-10-22 14:08:00 -05:00
Chris Tate b074a5e20f Test for-in key presence in the overflow map instead of a fresh key list
for-in over a pure index-signature record (`Record<string, number>`) skips
keys deleted during the walk by testing each snapshotted key for presence
before visiting it. The test built the overflow map's whole key list and
searched it with includes() on every visit, so a walk over n keys took
O(n^2) time: 200 walks over 2000 keys took 8.7 s, against 31 ms under
Node. The test is now recordOvfHas, the overflow map's hashed presence
check, which answers the same question in constant time.

The new benchmark workload dictionary-walks walks, prunes and merges
Record<string, number> dictionaries with for-in and Object.keys/values/
entries. It takes 7.1 s before this change and 23 ms after (Node 116 ms).
Corpus program 7011 pins for-in's visit order and its handling of keys
deleted, re-added and added during the walk.
2026-10-22 13:49:49 -05:00
Chris Tate 27bc23b3c2 Include the refreshed optimization profile in v0.4.2 2026-10-22 13:24:44 -05:00
Chris Tate 8d4ee22009 Refresh the native compiler's optimization profile (#1858) 2026-10-22 13:23:05 -05:00
Chris Tate e39ddf3d00 Refresh the native compiler's optimization profile 2026-10-22 13:18:04 -05:00
Chris Tate b4a3bace60 Read compact array slots in dense loop fast copies, and their flag only on 64-bit targets 2026-10-22 13:08:42 -05:00
Chris Tate 6681b9407e Version functions on number parameters that bound their loops
A number parameter that every call site proves an int32 reaches the
function's i32 entry. One that some caller passes as an unproven number,
such as a value read from a worker message or parsed from text, kept its
double, so every loop it bounds compared an integer counter with a double
limit and the values derived from it stayed doubles.

When such a parameter bounds a numeric loop (arithmetic, element and
field reads and writes, Math calls, no allocation or other calls), the
function now gets a copy of its body. Its own body first tests
each such parameter for an int32 that is not -0 and, when all pass, calls
the copy with `p | 0`, the same double. The copy's only caller proves
those parameters int32s, so it gets the i32 entry and integer loops.
Other values run the body as before.

Only plain synchronous functions of at most 2048 IR nodes are versioned:
not class members or generated helpers, not capturing, not the entry,
not dynamic-tier, not calling themselves, and at most 16 per module. A
function holding the only call site of another function is kept whole
unless that callee only computes a number or boolean from scalar reads:
a second call site can stop LLVM from inlining the callee and leave the
records it builds in memory. Library builds and modules that observe
call stacks are left as they are.
2026-10-22 11:46:21 -05:00
Chris Tate 9aaebbab25 Prepare v0.4.2 release 2026-10-22 11:14:27 -05:00
Chris Tate 5266cb7cbe Check with tsc-ts 0.0.7 and batch contextual types and signatures (#1844) 2026-10-22 10:50:12 -05:00
Chris Tate eef3471159 Stop asking batch methods the checker server answers as unknown
tsgo has no getContextualTypes or getResolvedSignatures. Each prefetch
asked for them anyway, and a failed chunk was split and retried down to
single nodes, so a tsgo build paid about two failed requests per
prefetched node. The checker facade now remembers a batch method the
server answers with "unknown API method" and asks those queries node by
node from then on.
2026-10-22 10:46:38 -05:00
Chris Tate 4d7de03054 Store arrays of ProgramLifetime instances in 4-byte slots on Linux 2026-10-22 10:46:16 -05:00
Chris Tate b774307a4d Merge main 2026-10-22 10:46:00 -05:00
Chris Tate 5bbf2c97b8 Encode posted records from their fields, and queue a message in one block (#1824) 2026-10-22 10:25:40 -05:00
Chris Tate 08081ca1a2 Encode each field of a posted record completely before the next
A typed record encoded from its field hooks queued a plain-object field on
the visit stack and went on to the next field, so getters on later fields
ran before the properties of earlier ones were read. Node's serializer
finishes each field, nested properties included, before the next.

The record path now drains the visit stack down to where it started after
each field, so properties are read in Node's order. Corpus 7139 posts a
record whose fields hold getter-bearing objects and prints the read order.
2026-10-22 10:22:22 -05:00
Chris Tate de6ef6590f Read optional elements at a proven integer index without a round trip
An element read that may be undefined, lowered inline from `%arr.idxOr`
(`a[i]` as `T | undefined`, and the read in `a[x] = a[y]!`), took an
integer index only from a loop's integer slot. Any other index, such as a
double counter with a proven integer range or `k | 0`, was converted and
tested for an exact integer on every read. The read now takes the same
exact integer as a borrowed read does (`exactInteger`), on 64-bit targets.
The loop-induction treatment above the hole-free prefix still applies only
to a loop's integer slot.
2026-10-22 10:12:31 -05:00
Chris Tate e4dc0900d7 Fence arguments.length in function expressions that call themselves by name
A function expression stored in a function type with more parameters
folds `arguments.length` to that type's parameter count, the count every
call through the slot passes. A named expression that calls itself
through its own name passes only its declared parameters there, so no
constant answers both calls. Such a read now reports SC1090 with the
reason, and expressions that do not refer to their own name keep the
fold.
2026-10-22 10:09:08 -05:00
Chris Tate 03495c2198 Index dispatch receivers by name instead of testing each one (#1814) 2026-10-22 10:07:29 -05:00
Chris Tate ca579641a4 Index quoted get: and set: method names under their own keys 2026-10-22 10:04:30 -05:00
Chris Tate 44bd4e1d6e Run counters that step beside a counted loop's counter as integers (#1829) 2026-10-22 10:00:28 -05:00
Chris Tate aaedce7b4a Keep a callback's captured scalars in registers in loops that call it (#1818) 2026-10-22 09:59:36 -05:00
Chris Tate 05acb4d247 Update the DiffieHellman runtime test for the prime search
test_dh.c includes scr_dh.c and still called sd_generate_safe_prime
without its new top_two argument, so dh.test.ts failed to compile. It
now passes false, as the DiffieHellman paths do, and a new case checks
generatePrime's search: the sizes and add forms with one possible
result, top-two-bit safe-prime starts, and checks above OpenSSL's round
count.
2026-10-22 09:58:47 -05:00
Chris Tate b82c70d5de Bound a follower's trip count only by a safe-integer span
The trip count that bounds a follower's range was the driver's span over
its step, computed in doubles. A span above 2^53 - 1 can round down: from
-(2^53 - 1) to below 2^52 - 1 the span 3 * 2^52 - 3 becomes 3 * 2^52 - 4,
so a follower that reaches 2^53 got the range limit 2^53 - 1 and ran as an
unchecked integer. Such a span now bounds no trip count, and the follower
is tested at run time (or the loop is refused when every value is known).
Below it the quotient rounds to at least its floor, and the follower's
products and sums are exact within the safe range.
2026-10-22 09:56:19 -05:00
Chris Tate cd2b1e2574 Keep callback loop versions from nesting
A callback loop version emits its loop twice. A loop inside either copy
could be versioned again, so a nest of loops that each call a callback
built just before them (`const g = adder(i)`) doubled at every level: a
three-deep nest was emitted seven times per function.

A loop inside a callback version, or the versioned loop itself emitted
again for each copy, is no longer versioned, so a loop nest is emitted at
most twice. The outer loop keeps its version; inner loops keep their
calls as they are.
2026-10-22 09:55:01 -05:00
Chris Tate 0ec1d47b4d Build typed 'message' listener parameters from the message (#1813) 2026-10-22 09:51:04 -05:00
Chris Tate ddf7eb8a2b Check with tsc-ts 0.0.7 and batch contextual types and signatures
tsc-ts 0.0.7 answers getContextualTypes and getResolvedSignatures, so
NODE_BATCH_QUERIES is on: lowering asks the checker for the contextual
types of identity-flow sites, array literals and conditionals, and the
resolved signatures of calls, in batches instead of node by node.

The release-age exclusions move to tsc-ts 0.0.7 and its platform
packages, and add @scriptc/threads 0.4.1, which tsc-ts 0.0.7 depends on.
2026-10-22 09:50:32 -05:00
Chris Tate 8487742019 Read the shared decoded value after an untyped listener, and skip discriminated unions
- Once a 'message' listener without a typed entry has decoded the
  message, the listeners after it read that decoded value, so a typed
  listener sees what an earlier untyped listener changed, as in Node.
- A union with a literal discriminator gets no typed message entry: its
  checked conversion also tests the discriminator, which the message
  readers do not.

Corpus 7361 checks a typed listener after an untyped one that changes
the message, and a listener on a discriminated union.
2026-10-22 09:48:41 -05:00
Chris Tate a020e8ea6c Name source files relative to the entry's directory in structural positions (#1840) 2026-10-22 09:45:58 -05:00
Chris Tate b80baf92ef Filter regex start positions by their leading character sets (#1784) 2026-10-22 09:45:21 -05:00
Chris Tate 2d6ef06fcb Version while loops over typed arrays on their counters' bounds
Typed-array loop versions applied only to for loops, so a scanner written
with while loops, such as `while (pos < end && bytes[pos] === 32) pos++`,
kept one bounds test per access. A while loop counter is now a number
local that the loop changes only by positive integer increments (`c = c +
k`, `c++`, `c = (c + k) | 0`) written as statements of its body, blocks,
branches and nested while loops. An access at `counter + d` after a
condition compared the counter below a loop-invariant bound, with no
increment since, is in bounds for the whole loop when the guard finds the
counter's entry value an exact integer of at least -d and the bound at
most the length minus the access's extent. A counter with a `| 0`
increment also needs its loops' bounds at most 2^31 minus its increments,
so it never wraps.

A fixed check (a statically ranged index) now needs an end below 2^31.
The counter of a loop to a number[]'s length whose length the loop may
change reaches 2^32 - 2, and its typed-array accesses got a guard no
typed array passes and a second copy of the loop.
2026-10-22 09:40:22 -05:00
Chris Tate 279cdcffae Name source files relative to the entry's directory in structural positions
Structural positions started with a file's absolute path, so lowered
names (closures, recursive records and unions, and every name keyed by a
type that holds one) changed with the directory the program was compiled
in. Union arms are ordered by their types' keys, so a program compiled in
a new temporary directory could also get its union arms in another order.
union-retype.test.ts compiles each program in a fresh temporary
directory, and in about one run in ten the arms of the visitor's unions
came out in an order where no retag keeps its tags, which failed "lowered
identity retags share the box".

Positions now name a file by its path relative to the entry's directory
(default library files keep their base name), so the same program gets
the same names wherever it sits.
2026-10-22 09:39:07 -05:00
Chris Tate 091f280cec Read the union payload inline when comparing it with a number or boolean
`u === 0`, `u !== true` and `Object.is(u, x)` against a scalar arm tested
the union's tag inline but read its payload through scr_union_get_f64 or
scr_union_get_bool. The call passed the union's stack box to the runtime,
so LLVM could not promote the box to registers, and an element read of a
`number[]` compared with a number kept its value, tag and box in memory.

The comparison now loads the payload slot directly, as other union
extractions already do, and the box no longer escapes.
2026-10-22 09:32:19 -05:00
Chris Tate 8b4333b166 Record the order-parity baseline for the byte-mode chunk fence fixture (#1838) 2026-10-22 09:28:54 -05:00
Chris Tate ed66338cb6 Record the order-parity baseline for the byte-mode chunk fence fixture 2026-10-22 09:25:44 -05:00
Chris Tate 9433c2ad35 Reuse the class dispatch passes' per-function scans while bodies are unchanged (#1833) 2026-10-22 09:25:09 -05:00
Chris Tate 0318671352 Reuse the class dispatch passes' per-function scans while bodies are unchanged 2026-10-22 09:17:49 -05:00
Chris Tate 20f4d1ff12 Support the legacy createCipheriv modes and crypto.checkPrime/generatePrime 2026-10-22 09:08:35 -05:00
Chris Tate 60c51b9535 Merge main 2026-10-22 09:08:21 -05:00