Commit Graph
3145 Commits
Author SHA1 Message Date
Chris Tate c9fe46d8c0 Merge main into perf/offthread-prefault 2026-10-11 18:10:02 -05:00
Chris Tate fa11424e5f Prepare v0.2.9 2026-10-11 17:25:14 -05:00
Malte Ubl 2162b0af65 Identity bitwise forms over proven int32 operands return the operand
`x | 0`, `x ^ 0`, `x & -1`, `x << 0`, `x >> 0` (and `x >>> 0` for a
uint32) over an operand the integer range analysis proves an int32 now
return the operand's number and its integer views instead of converting
to i32 and back: ranges exclude -0, so the result is the operand bit for
bit. The range analysis computes bitwise result ranges from the operand
ranges (exact ToInt32/ToUint32 views, shift-count ranges) instead of the
blanket int32 range, so `(n & 0xff) | 0`, `s.length >> 1` or `x >>> 1`
keep tight bounds for later integer consumers.
2026-10-11 14:24:59 -07:00
Malte Ubl 9899c4014b Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 13:39:52 -07:00
Malte Ubl bb274adc5d Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 13:27:39 -07:00
Malte Ubl d57b7c4b63 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 13:27:32 -07:00
Malte Ubl eae8e02481 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 13:27:25 -07:00
Malte Ubl 5ac6252916 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 13:27:18 -07:00
Malte Ubl 0148f6b741 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 13:27:11 -07:00
Chris Tate c9452c8c33 Merge pull request #867 from vercel-labs/perf/checker-round4
PR: Read nullable-union array elements inline and borrow them for preserving live ranges
2026-10-11 15:25:09 -05:00
Chris Tate d761cb530d Merge main into perf/static-immutability 2026-10-11 15:19:32 -05:00
Malte Ubl fa991511e1 Hint LLVM to inline the shared indexed-read helpers
Every safe a[i] read of one element type calls one lifted helper
(lowerSafeIndexRead): a dense bounds test, a state test and a load
behind cold runtime fallbacks, about twenty instructions. With many
callers LLVM kept the hot instances out of line, so each read paid a
call and converted an integer index to a double and back across it
(tsc-ts Checker.nodeLinks: two calls per paged lookup).

The helpers now carry inlinehint. tsc-ts self-check (callgrind,
single-threaded): -0.73 % instructions; binary +0.41 %.
2026-10-11 13:10:15 -07:00
Malte Ubl 994d9ea7f6 Dispatch literal integer switches without separate range tests
A literal integer switch over a number tested min <= x <= max with two
ordered compares before converting and round-tripping the discriminant.
The round trip alone already rejects every non-matching value once the
conversion is frozen: NaN and infinities compare unordered, a frozen
out-of-range conversion converts back to a different value, fractions
fail, and -0 selects case 0 like strict equality. Cases in [2^31, 2^32)
dispatch on i64 instead of fptoui to i32.

A discriminant already proven to be an exact integer (int32 slots,
x | 0, x >>> 0, counted-loop counters) now selects directly, narrowing
to i32 only when its proven range fits.

tsc-ts (callgrind, single-threaded): self-check about -0.8 % instructions,
mostly switch (node.kind) and token dispatch.
2026-10-11 13:10:11 -07:00
Malte Ubl 757c48c2f8 Anchor the canonical byte-loop check on the element-check blocks
f3cfa80a asserted that the canonical byte loop contains no `fptosi double`
at all, meaning no exact-index round trip. The fixture also coerces its
running sum with ToUint32, whose inline fast path legitimately uses
`fptosi double ... to i64`, so the test fails on main (aaccbb39) as well;
#865 merged before the shards that run it. Assert instead that no
`bytes.index` check block is emitted, which keeps the original intent
(no unproven-index path for a proven in-bounds induction index).
2026-10-11 13:10:06 -07:00
Chris Tate f43ef506db Update byte-loop emission test for exact index checks
(cherry picked from commit f3cfa80a8e)
2026-10-11 13:08:09 -07:00
Chris Tate 8b5cf78e19 Update constant-table emission test for exact index checks
(cherry picked from commit 9b80f3d4b4)
2026-10-11 13:08:08 -07:00
Malte Ubl 0f05bf368d Pre-fault only for large slices and heaps
The first version committed memory ahead for every program: the heap's
16 MiB top pad was faulted in as soon as the helper started, and every
slice past its first 256 KiB faulted the next chunk ahead. A small server
(runtime suite http-api) peaked at 20.7 MiB instead of 4.4 MiB.

- The helper now only watches the break until the heap has grown by
  32 MiB; only then does it set the top pad and fault growth in ahead.
  Smaller heaps keep glibc's defaults, including its dynamic mmap
  threshold, which any top-pad mallopt fixes.
- A slice faults ahead only once it has carved 1 MiB, so lightly used
  classes commit nothing extra (and a program that never fills one does
  not start the helper at all).
2026-10-11 13:07:04 -07:00
Malte Ubl 2039d88e91 Record the preflight/order baseline of the charCodeAt loop fixture 2026-10-11 13:05:27 -07:00
Chris Tate 187ca8b998 Check the index guard in the integer byte-loop test 2026-10-11 15:05:18 -05:00
Malte Ubl 86c4edab54 Read charCodeAt at proven integer indices without a double round trip
String lengths and found indices are now int32 facts for the integer
range analysis: runtime strings hold at most SCR_STR_MAX_CAP UTF-8 bytes,
and a UTF-16 length never exceeds the UTF-8 byte length. Loops bounded
by a string length therefore get integer counters, and charCodeAt at the
counter counts as an integer consumer for counted-loop versioning.

A charCodeAt whose index the backend holds as an exact integer calls
@sc_str_char_code_at_int: on a proven-ASCII string one unsigned compare
against the length rejects negative and out-of-range indices, and every
other case defers to scr_str_char_code_at with the exact double.
2026-10-11 13:00:46 -07:00
Malte Ubl 767c33ebdb Give glibc's large blocks transparent huge pages
Blocks above the small-object classes (array storage, hash tables, link pages, large strings) come from glibc, whose brk heap and per-block mappings stay on 4 KiB pages where THP is in madvise mode; glibc advises its own memory only under the glibc.malloc.hugetlb tunable, which a program cannot set for itself. Once the brk heap passes 32 MiB, every large allocation now advises the heap grown since the previous one (and the aligned interior of big blocks mapped outside it), and glibc's top pad is raised to 32 MiB so heap growth no longer leaves each 2 MiB range straddling the heap end when it is first touched; the mmap and trim thresholds are pinned to their adaptive maxima, which setting the pad would otherwise freeze at their defaults. Linux and glibc only.
2026-10-11 12:58:50 -07:00
Chris Tate 6eef5b7e5a Merge main into perf/gc-trace-null-skip-main 2026-10-11 14:54:07 -05:00
Malte Ubl 61f3bac6d4 Add the @scriptc/types README 2026-10-11 12:52:33 -07:00
Malte Ubl 7cdbfc26a9 Format local-array-reads and record the preflight baseline for corpus 4671 2026-10-11 12:49:26 -07:00
Malte Ubl 2278456bbd Pre-fault allocator memory on a helper thread (Linux)
First touch of fresh memory costs the allocating thread a page fault and
a page clear. Linux executables now hand that to a helper thread:

- Size-class slices raise their bump limits in steps (256 KiB on the
  small-page prefix, half a huge page after the switch), and each step
  asks the helper to fault in the next step ahead with
  MADV_POPULATE_WRITE, which commits zeroed pages without storing, so it
  cannot race with carving. A slice holds at most about one step of
  untouched committed memory; a slice's first chunk is not faulted ahead.
- glibc's heap gets a 16 MiB top pad and the helper faults in each brk
  extension as soon as it sees the break move (checked after every
  request, then polled every 1 ms while it keeps growing; an idle program
  does not wake it).

The fast paths are unchanged; pages the helper has not reached are still
faulted by the owner. The helper masks all signals. SCR_SA_PREFAULT=0
turns it off, SCR_SA_PREFAULT_HEAP=0 keeps glibc's heap defaults; kernels
without MADV_POPULATE_WRITE (< 5.14) fall back to the old policy, and
library archives keep it.

The slice-growth allocator check now carves past several huge-page steps
and checks every fresh block reads as zero (UBSan and TSan builds run
with the helper).
2026-10-11 12:38:24 -07:00
Malte Ubl 0825756407 Contract checks compile in the self-hosted compiler
Narrow IR nodes by kind instead of structural casts, and pass concrete arrays
instead of Iterable, so the self-hosting build of the compiler accepts the
contract checker and the may-throw explanations.
2026-10-11 12:38:23 -07:00
Malte Ubl a20f01a8b1 Merge remote-tracking branch 'origin/main' into feat/contract-types
# Conflicts:
#	packages/compiler/src/backend/may-throw.ts
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 12:25:31 -07:00
Malte Ubl d5c5262d01 Test may-throw explanations 2026-10-11 12:25:11 -07:00
Malte Ubl 947c9bcf3f Contract types: explain throwing paths, bind only declared functions, docs and baselines
- NoThrow diagnostics name compiler-resolved Node errors (class and message)
  and explain parameters the frontend compiles as T | undefined because a
  caller passes a possibly missing value (for example xs[i]!), naming that
  call.
- Synthetic dynamic-class helpers (%dyn.*) carry the location of the code
  that first needed them; they are no longer bound to annotations there.
- Int32 hints only parenthesize where needed; the NoThrow hint no longer
  suggests try/catch, which does not discharge the contract.
- Diagnostics snapshots and TypeScript preflight/order baselines for the new
  fixtures.
- Docs: /docs/contracts and a limitations entry.
2026-10-11 12:24:27 -07:00
Malte Ubl fc93d10871 Merge remote-tracking branch 'origin/perf/array-layout-main' into HEAD 2026-10-11 12:23:05 -07:00
Malte Ubl cc8392d26a Merge remote-tracking branch 'origin/perf/heap-layout-main' into HEAD 2026-10-11 12:22:56 -07:00
Malte Ubl c8430bbf0d Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 12:22:49 -07:00
Malte Ubl e84adc9727 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 12:22:40 -07:00
Malte Ubl 3a13f03544 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 12:22:31 -07:00
Malte Ubl fa6c6988b2 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/src/backend/llvm/emitter.ts
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 12:22:24 -07:00
Malte Ubl 20029454ef Read nullable-union array elements inline and borrow them for preserving live ranges
Optional array reads also match arrays whose element is itself the read T | undefined nullable-pointer union; owned copies retain and release through its NULL-tolerant entry points. A local array read borrows when its array operand is rooted at a stable parameter and its own live range preserves heap edges, and a call-argument read borrows when the call preserves them. An optional chain on such a read borrows the inline element instead of taking a second reference.
2026-10-11 12:20:28 -07:00
Chris Tate baeaf9c7e0 Merge pull request #854 from vercel-labs/perf/exception-flag-main
Exact per-thread pending word for worker executables, and a call-free macOS stack guard
2026-10-11 14:06:38 -05:00
Chris Tate b2bb6fa173 Merge main into perf/exception-checks-main 2026-10-11 13:38:06 -05:00
Malte Ubl 5d4509702f wip: contract types prototype 2026-10-11 11:17:17 -07:00
Chris Tate 0ab65a79b0 Merge pull request #836 from vercel-labs/perf/parmem
Support os.availableParallelism() in static programs
2026-10-11 12:55:51 -05:00
Malte Ubl 00f88680cd Merge remote-tracking branch 'origin/main' into HEAD 2026-10-11 10:17:26 -07:00
Malte Ubl 112add3dce Merge remote-tracking branch 'origin/perf/gc-trace-null-skip-main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 10:17:20 -07:00
Malte Ubl dab7719e28 Merge remote-tracking branch 'origin/perf/array-layout-main' into HEAD 2026-10-11 10:17:13 -07:00
Malte Ubl 796013f9d2 Merge remote-tracking branch 'origin/perf/heap-layout-main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 10:17:07 -07:00
Malte Ubl fca66ec655 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 10:16:59 -07:00
Malte Ubl d770871069 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 10:16:52 -07:00
Malte Ubl a34d018497 Merge remote-tracking branch 'origin/perf/exception-checks-main' into HEAD 2026-10-11 10:16:45 -07:00
Malte Ubl 445730ee38 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 10:16:37 -07:00
Malte Ubl c72131b2b9 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-10-11 10:15:33 -07:00
Malte Ubl 9931ea30de Merge remote-tracking branch 'origin/perf/static-immutability' into HEAD 2026-10-11 10:15:24 -07:00