Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate 6e17225ffc Keep module-level lets unboxed in the dynamic tier and return receivers from dispatch helpers (#2471) 2026-10-28 09:15:26 -05:00
Chris Tate 9244612aa6 Skip the arena clear for record literals that write every field (#2470) 2026-10-28 09:07:33 -05:00
Chris Tate 25bb2a43ef Lower optional-chain promise methods, promise-or-unit Promise.resolve, and Array.from(typedArray, mapper) (#2465) 2026-10-28 09:05:56 -05:00
Chris Tate ec79f2aca0 Destructure narrowed and joined union sources, await promise-armed unions, support writeFileSync(fd) (#2466) 2026-10-28 08:57:38 -05:00
Chris Tate 385555f2fd Reuse lib diagnostics across options that cannot change them
The stored and prebuilt answers for the declaration files scriptc supplies
were keyed by every compiler option, and the prebuilt ones are recorded
without a tsconfig. A project whose tsconfig sets noUnusedLocals,
noUncheckedIndexedAccess or a similar option therefore missed them and
checked the supplied files in every cold build.

The key now leaves out the options that cannot change what checking the
supplied declaration files reports: the unused-identifier options (the
checker reports none in declaration files), the statement options, the
access-expression options and allowSyntheticDefaultImports. A test checks
the supplied files with each of them set and unset, and checks that they
have none of the constructs those options apply to.
2026-10-28 08:56:40 -05:00
Chris Tate f041da4b10 Drop the computed Array.isArray refusal from the stdlib fence snapshot
`Array.isArray([1])` folds after evaluating its argument, so the stdlib
fence battery no longer reports SC1090 for it. The snapshot drops that
diagnostic and the statement notes that it lowers.
2026-10-28 08:56:20 -05:00
Chris Tate 2c7b67c5aa Keep module-level lets unboxed in the dynamic tier and return receivers from dispatch helpers
- The jsval pass also promotes dyn module globals to jsval globals when
  only dynamic-tier functions read and write them and the boxing their
  writes save outweighs the boxing their reads add, the measure locals
  use. `checksum += f(x)` in a top-level loop no longer allocates a
  number cell per update.
- A class dispatch helper that calls a method returning `this` on every
  path calls a copy of the method that returns nothing and answers its
  own receiver, the capsule of the same instance at its exact class, so
  the call boxes nothing.
2026-10-28 08:35:25 -05:00
Chris Tate 4e18ec933b Record the preflight and order baselines of the new corpus programs
array-from-typed-array-mapper.ts, promise-catch-optional-chain.ts,
promise-resolve-promise-or-unit.ts and promise-void-catch-undefined.ts each
load only themselves and report no preflight diagnostics.
2026-10-28 08:28:40 -05:00
Chris Tate 7918ec43f7 Skip the arena clear for record literals that write every field
An arena-aware record constructor zeroed the whole object after the bump,
then stored the count and shape number, and the record literal that called
it then stored every field. A record literal covers every field of its
shape, so the zeroing only mattered for the dispose entries of an arena
that is not closed, which read the fields when the arena drops.

The arena-aware constructor of a record shape without dispose entries now
clears only the collector header, when the shape has one. Its object is
never read before the literal's stores: a release of an arena object only
lowers its count, and scr_arena_obj and the heap fallback still return
zeroed objects. Shapes with dispose entries and classes, whose constructors
need not store every field, keep the whole clear.
2026-10-28 08:20:06 -05:00
Chris Tate 1ffc738c58 Keep undefined out of the shape overlay's set of constructed functions (#2464) 2026-10-28 08:12:42 -05:00
Malte UblandClaude Opus 5.5 08ec7fd291 Merge ni/i10-js-island-requires into ni/i11-cross-heap-gc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:43:05 -07:00
Malte UblandClaude Opus 5.5 0fcc4d15a4 Merge ni/i9-recorded-deferral into ni/i10-js-island-requires
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:43:00 -07:00
Malte UblandClaude Opus 5.5 433dc179ef Merge ni/i8-exit into ni/i9-recorded-deferral
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:42:55 -07:00
Malte UblandClaude Opus 5.5 6ec5b3ff58 Merge ni/i7-routing into ni/i8-exit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:42:43 -07:00
Malte UblandClaude Opus 5.5 9edf1d3aa3 Merge ni/i6-subclasses into ni/i7-routing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:42:18 -07:00
Malte UblandClaude Opus 5.5 86056af745 Merge ni/i5-shared-modules into ni/i6-subclasses
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:41:14 -07:00
Malte UblandClaude Opus 5.5 4a673558b9 Journal the shared-module plan fields in the lowerer
The unit-journal test merged in from main requires every lowerer field
to be journaled or named as unjournaled, and the NI-6 fields were
neither: `sharedModulePlans` runs inside the first unit that lowers a
module, so journal the warning count like `diags`, and name the plan
memo and the per-statement TDZ flags it fills as unjournaled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:33 -07:00
Malte UblandClaude Opus 5.5 39326c55be runtime: ship child.off/signalCode in a gated unit
Adding the six new entry points (scr_child_off_spawn/_exit/_close/_error,
scr_child_has_signal_code, scr_child_signal_code) to scr_child.c changed
the executable of every program that links the unit, although none of
them calls the new functions and IR and LLVM stayed identical: the extra
functions grow the unit's `.eh_frame` contribution, and the linker's
frame bookkeeping for the retained sections changes with it (the first
visible difference is the phdr sizes at byte offset 208). Appending them
after the unit's own functions does not help — the unit's frame section
still differs, so all 89 corpus programs that link scr_child.o got a
different binary.

Move the entries into scr_child_lifecycle.c, a unit of their own
selected only by programs that use them (feature childLifecycle, the
scr_dyn_handle_props.c pattern: the matrix's optional source list, the
RUNTIME_FEATURE_RULES rule for child.offSpawn/offExit/offClose/offError/
signalCode, and the link-feature plumbing through the executable cache,
CcOptions and RUNTIME_UNITS). scr_child.c keeps its original content and
layout; the new unit reads its registries through scr_child_private.h,
which holds the per-platform ScrChild layout both units compile against.

A program that does not call the new entries links exactly the units it
did before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:17 -07:00
Malte UblandClaude Opus 5.5 4de46addd7 perf: look the contextual signature up once per lambda
The void-slot ABI decision above `declaredReturnType` and the adoption
block below it both ask the checker for the lambda's contextual
signature. Memoize it: one lookup per lambda, as before, so the new
early decision adds no checker work to the path every lambda takes.

No behavior change: both sites read the same cached array.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:17 -07:00
Malte UblandClaude Opus 5.5 4d7f64c461 path/stdio: sub-namespace bindings and stdout writableLength
`import { win32 } from "node:path"` binds the win32 sub-namespace itself,
so `win32.normalize(...)` must key the win32 table on every platform —
Node answers each namespace its own platform's semantics anywhere.
`builtinNamespaceModuleOf` now resolves a named `win32`/`posix` binding
of `path`, `path/posix` or `path/win32` to that sub-namespace module
instead of falling through to a dynamic receiver.

`process.stdout.writableLength` / `stderr.writableLength` are now
accepted and answer 0: the compiled binary writes fd 1/2 synchronously,
so nothing is queued when the property is read, and `write()` answers
true for the same reason. Backpressure probes stay correct. The ambient
`tty.WriteStream` declares the property.

Corpus 8414 pins the value and its type before and after writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:16 -07:00
Malte UblandClaude Opus 5.5 882c87d425 process/child: void slots, listener removal by identity, signalCode
Three shapes from pi's process/child helpers:

1. A cleanup arrow such as `() => process.off(signal, handler)` sits in
   @types/node's `Process`-returning slot. The lambda's ABI was decided
   from its inferred return type before the contextual void slot was
   consulted, so the arrow promised a `Process` value the lowering cannot
   produce and the registration fenced. `lambdaSignature` now takes the
   contextual void answer first; the adoption block further down already
   computes it, so every program that compiles today keeps its IR.
2. `child.off`/`removeListener` were unimplemented. They join the
   `child.on` family, are limited to the removable literals `spawn`,
   `exit`, `close` and `error`, and require the listener value that was
   registered — removal is by closure identity, scanning the registry
   from the end as Node does. New C runtime entry points
   `scr_child_off_spawn/_exit/_close/_error` release the entry and shift
   the tail.
3. `child.signalCode` reads `string | null` from the recorded exit
   signal, with `scr_child_has_signal_code`/`scr_child_signal_code`
   behind the same typed-union wrap as `spawnRes.signal`; the IR
   validator pins the `string | null` result.

The process listener registration paths (stdin events, IPC message and
disconnect, uncaughtException and uncaughtExceptionMonitor,
unhandledRejection, warning, signals, exit) now accept any discarded
expression, matching `child.on` — `resultIsDiscarded` rather than a bare
expression statement — so `handlers.push(() => process.off(…))` compiles.

Evidence: corpus 8412 (removal through `off` and `removeListener` for
all four events, `signalCode` while running, after a normal exit, after
a fatal signal and after a failed spawn, idempotent double removal) and
the `node-types` fixture plus project-config test that pins pi's exact
`() => process.off(...)` cleanup closure. Ambient declarations gain the
`signalCode` property and the four `off`/`removeListener` overloads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:15 -07:00
Malte UblandClaude Opus 5.5 c3232c265d fs: accept a const options binding in three-argument writeFileSync
The three-argument form required its options argument to be an inline
object literal, so a shared `const SCRATCH = { encoding: "utf-8",
mode: 0o600 } as const` binding — the shape pi's temp-file helpers use —
fell through to SC2020 even though every option was a literal. Resolve a
`const` initializer through `constInitializerOf`, strip casts and
parentheses, and admit a literal whose every option member is a
literal-valued property, which is exactly what the inline form accepted.

Corpus 8413 pins the creation mode and that an existing file keeps its
mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:14 -07:00
Malte UblandClaude Opus 5.5 86299300f6 Fold Array.isArray over a computed argument after evaluating it
`Array.isArray(v)` is a compile-time constant when `v` has a static type:
an `T[]` or a tuple is an array, every other kind is not. The fold only
accepted a bare variable, a record field or a field read, because only
those could be dropped without losing effects, so `Array.isArray(make())`
and `Array.isArray([1, 2])` reached an SC1090 refusal.

A computed argument now folds the same way after being evaluated for its
effects: the value goes into a sequence whose discarded statement is the
argument. Unions keep answering by their runtime tag, and `unknown`
values keep their real runtime test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:08 -07:00
Malte UblandClaude Opus 5.5 c7c214532e Lower species forms through a narrowing wrapper and a union flatMap
The species lowering for a callback method rebuilt its `%arr.<method>`
call by shape — the result had to be a call taking the receiver as its
first argument — so two forms the method lowering does produce fell to
SC1090 whenever the program declared a class that extends Array:

- a filter whose callback narrows: the filtered array comes back through
  the checked-narrowing wrapper, `call(%arr.narrow, [call(%arr.filter,
  …)])`, and the species path saw a call whose first argument is not the
  receiver;
- a flatMap whose callback returns a union with an array arm, which
  lowers to `%arr.flatMap.<n>` — a helper boundedArrayHofHelper rejected
  because arrHofInfo, which holds the pipeline-fusable helpers, has no
  entry for it.

The call is now found by walking the expression for the `%arr.` helper
call over the receiver, whatever wraps it, and boundedArrayHofHelper
accepts any `%arr.` helper (forEach excepted, it makes no array), so the
union flatMap earns a bounded variant. Hidden locals are still declared
in the old order, so a program that compiled before gets byte-identical
IR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:07 -07:00
Malte UblandClaude Opus 5.5 f09e02255d Guard the X509 dynamic-value fence against recursive types
refuseX509DynValues walks a converted value's type looking for an
X509Certificate record, and it memoized only records (by shape id) and
unions (by union id). A type that reaches itself through an array, set,
map or promise has no id to memoize by, so `type Rec = string | Rec[]`
in a program that also holds a certificate recursed until the compiler
ran out of stack — a crash, not a diagnostic.

The walk now keeps an identity-based visited set over those kinds: a type
met again while it is still being visited answers false, the least fixed
point of reachability, since nothing outside the cycle makes a
certificate reachable. Unions keep their memo, which now also covers a
union cycle that names one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:06 -07:00
Malte UblandClaude Opus 5.5 071be693d9 Support writeFileSync(fd, data)
Node's writeFileSync accepts an open file descriptor where it accepts a
path: it writes the data at the descriptor's current offset and leaves the
descriptor open, since closeSync owns its lifetime. The static fs surface
declared only the path forms, so `fs.writeFileSync(fd, text)` failed type
mapping — the compiler was missing a documented overload, not a
conversion.

Declare the (fd, data) and (fd, data, "utf8" | "utf-8") forms in the
node-fallback typings, route the call by the first argument's static f64
type to a new fs.writeFileSyncFd libfn, and implement it as
scr_fs_write_file_fd: a write loop over the whole string through the
descriptor-validating scr_fs_write_bytes, resuming short writes, with the
descriptor operation's errno shape on failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:38:00 -07:00
Malte UblandClaude Opus 5.5 99ebf7501d Await a union whose promise arm settles a union
`Promise<A | B> | undefined` awaits as Awaited of the operand, which the
maybe-promise lowering already computes: it awaits the narrowed promise
arm and coerces its settled union, and the non-promise arms take JS's one
microtask hop. The await lowering refused any union inner instead, so the
promise-or-unit shape now goes through that same path; a union whose
promise arm has no combined result keeps the SC1090 fence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:59 -07:00
Malte UblandClaude Opus 5.5 a0a6910e4e Destructure union sources by narrowing first and joining arm reads
An object pattern over a union whose arms type a field differently, or do
not declare it at all, was refused with SC1031, even though the pattern
element's own type is the checker's answer: the join of the arms' declared
types, with undefined for an arm that omits the key, or the narrowed arm
when a discriminant test or an `in` guard refined the source at the
pattern's site.

Keep the unionDisc fast path wherever every arm declares the field with
one type, so programs that compile today emit identical IR. Otherwise read
through the existing keyed join (unionKeyGet): each arm answers its
declared type and a record arm that does not declare the key answers
undefined, which is exactly the desugar's own property read. Only
destructuring passes the new missingFieldAsUndefined flag, so dotted reads
keep their current refusals.

Narrow first when the un-narrowed pattern does not fit: bind the
checker-narrowed view of the source to a hidden local — a trusting
arm extraction for one arm, a sub-union re-tag whose excluded arms trap,
and the stored layout view for an intersection refinement — and lower the
pattern over that view.

A rest element over a union source packs each arm's unconsumed fields into
a fresh record of the checker's arm-wise rest type, selected by the
runtime tag; CopyDataProperties makes a fresh object too, so nothing
aliases the source. Shapes that do not spell the rest type still fence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:58 -07:00
Malte UblandClaude Opus 5.5 b8bb5bfdb0 Record the new corpus programs' CI costs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:52 -07:00
Malte UblandClaude Opus 5.5 0496f0eb25 Lower Array.from(typedArray, mapper)
`Array.from(bytes, f)` had no lowering — only the mapper-less
`toArray` — so every mapper over a typed array fenced. It lifts a loop
that reads each element through the typed array itself, because Node
iterates live: a mapper that writes the source is visible to the
indexes it has not visited yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:51 -07:00
Malte UblandClaude Opus 5.5 a4a84f59da Resolve a promise-or-unit operand in Promise.resolve
`Promise.resolve(p)` over `Promise<void> | void` — an async callback's
declared result — was refused: the maybe-promise detection required
every non-promise arm to carry a value, while a nullish arm is exactly
what `Promise.resolve` fulfills as undefined. `promiseOrUnitUnionOf`
names that shape, and the resolution reuses the existing adopt-or-
fulfill path, so the promise arm passes through unchanged
(`Promise.resolve(p) === p`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:50 -07:00
Malte UblandClaude Opus 5.5 bf28595035 Lower promise methods called through an optional chain
`p?.catch(h)`, `p?.then(h)` and a guarded chain tail (`f()?.then(h)`)
were refused: the promise-method lowering rejected every
`questionDotToken` instead of asking whether the optional chain was
currently being lowered, so the guarded body never reached it. It uses
`chainBlocked` now, like every other lowering that rides the chain.

The call that ENDS a chain checker-types as the chain's undefined-armed
union (`Promise<T> | undefined`) while this lowering builds the promise
arm, which `finishOptionalChain` wraps back into the union. The
handler's result type takes that arm, so the handler is typed against
the promise it actually handles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:50 -07:00
Malte UblandClaude Opus 5.5 279d42e562 Lower a discarded undefined catch handler
`Promise<void>.catch(() => undefined)` tripped the IR validator's
bare-unitLit rule: the handler body lowered as a bare expression, and
`undefined` has no standalone IR value. The handler's value is
discarded, so lower the body as a statement — the same rule a
discarded `undefined;` statement already follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:49 -07:00
Malte UblandClaude Opus 5.5 efc22a2fd3 Box user module namespaces as dynamic values
A `file:` module namespace handed to typed code now boxes as Node's
namespace object instead of the "converting typed values to 'unknown'"
fence: `const u: unknown = ns`, `take(ns)`, a `Promise<typeof
import("./m")>` widened to `Promise<unknown>` (or `Promise<unknown> |
undefined`), and `await import("./m")` in an `unknown` slot all see one
non-extensible null-prototype object per module whose own properties are
the module's exports in Node's sorted order. They are enumerable
read-only accessors whose reads call the compiled namespace getter, so
they stay live, and the object carries Symbol.toStringTag "Module",
Node's strict-mode write TypeErrors, the not-extensible add error and
the ToPrimitive throw. `node:` builtins have no compiled export table
and keep their SC1101 fence.

The object is built by a new gated runtime unit,
packages/runtime/src/scr_module_namespace_dyn.c, selected by the
`moduleNamespaceDyn` runtime feature (`module.namespaceDyn` in the IR).
It adds no field to ScrDynHandleOps and no branch to a shared unit: the
core reaches it through the ordinary SCR_DYN_OBJ paths, so key listing,
for-in, `in`, JSON, identity, property reads and assignment all reuse
the existing dispatch. The compiler reuses the getter lifted for
module.namespaceValue (existing programs' IR is unchanged) and adds
moduleNamespacePromiseDynValue, one lifted async mapper per module for
the promise slots.

Zero cost: a program that never boxes a namespace links exactly what it
did. The zero-cost gate reports every program identical, with
byte-identical IR and LLVM; the runtime pack's only difference is the
new unit (0 changed runtime functions), and among the namespace corpus
programs only 8412 links it.

Evidence: tests/corpus/8412-module-namespace-untyped is a differential
program against Node in both the plain and sanitized lanes, and the
diagnostics snapshots and TypeScript 7 baselines stay unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:37:45 -07:00
Chris Tate 6f65b58432 Report a namespace's member fences where the program names it
The program's builtin lookup holds every declared global when a module
reads the global object, or a sloppy-mode function's this, by a computed
key. Building the Reflect and JSON namespaces for it reported the SC1090
fence of each member without a function value (Reflect.apply,
JSON.rawJSON, ...), so a program that never names those namespaces
reported them.

A namespace's builder now only creates the functions that throw those
fences when called, and builtinNamespaceValue reports the fences, once per
program, when a site that names the namespace reads it. The lookup reads
the namespaces without reporting; its members still throw their fences
when called.
2026-10-28 07:14:31 -05:00
Chris Tate e2b95cdee8 Run short runs of small innermost worker loops without a poll (#2463) 2026-10-28 06:52:19 -05:00
Chris Tate 8b27e9cd2f Name program modules by the entry's file name so profiles match in every checkout (#2462) 2026-10-28 06:43:48 -05:00
Chris Tate 35eda071b2 Give JavaScript fetch bodies the members of a web ReadableStream (#2459) 2026-10-28 06:41:21 -05:00
Chris Tate 3134642f19 Keep undefined out of the shape overlay's set of constructed functions
A union that is a Set element or a Map key is stored as a union box, and
a union type has one representation in a program. The overlay's
`Set<ts.Node | undefined>` made the native compiler box every
`ts.Node | undefined` value, including each optional child a syntax node
returns. The set now holds the defined nodes only.
2026-10-28 06:40:50 -05:00
Chris Tate 74a131bbcc Run short runs of small innermost worker loops without a poll 2026-10-28 06:14:20 -05:00
Chris Tate 0bf0f69dc3 Balance program partitions by size (#2461) 2026-10-28 06:10:55 -05:00
Chris Tate 0f47b9cbab Answer builtin globals read through a sloppy-mode function's this by key
A sloppy-mode function called with undefined or null as this reads the
global object. Element reads of that this, or of a variable it initializes,
now reach the program's lookup too: a literal key (`this["Map"]`) adds that
name, and a computed key (`this[name]`) installs the lookup for every
declared global, as `globalThis[name]` does. Before, a literal key read the
opaque identity token and a computed key threw SC2020.
2026-10-28 06:05:39 -05:00
Chris Tate 62b34e7e01 Annotate functions a stale profile misses from its block counts (#2460) 2026-10-28 06:00:31 -05:00
Chris Tate 23d95a99fd Name program modules by the entry's file name so profiles match in every checkout
Each partition's source file name was the program entry's absolute path,
and profiles name a module's local functions after it. Since programs keep
partition-local functions internal (#2449), a profile collected in one
directory missed those functions in builds from any other: a native
compiler profile collected on main named 27% of its records, holding 2.2%
of its counts, after the checkout's path. Modules now carry the entry's
file name. The partition cache key still includes the full path.
2026-10-28 05:44:38 -05:00
Chris Tate 98f5926119 Update bodyUsed when a body's web view starts a read, and link the view for untyped uses
After a fetch body's handle gained its web view, a read, cancel or pipe
started on the view left Response.bodyUsed false until the view's pull
reached the body, where Node's bodyUsed reads the stream's disturbed state
at once. bodyUsed now also answers the view's disturbed state, which the
web streams' bridge reports (ScrWebStreamBridge.disturbed).

A program that names none of the web stream classes linked them only for
member accesses the checker resolves to ReadableStream, so body.tee() or
for await over a body inside a function whose parameters have no type
threw a TypeError. The same member names on an untyped receiver, and
for await over an untyped value, now link the classes in a program that
links the fetch unit.

Adds corpus program fetch-body-web-stream-helpers.mjs, with its corpus
cost and preflight/order baseline, and bodyUsed cases to
fetch-body-web-stream-linked.mjs.
2026-10-28 05:29:42 -05:00
Chris Tate 007a144cb6 Balance program partitions by size 2026-10-28 05:12:02 -05:00
Chris Tate 3bf316de6b Annotate only mismatched functions from block counts
A block-count file annotated every function the profile left without
counts, including functions the profile has no record of. The recovered
count could then exceed the mismatched functions it annotated and hide the
stale-profile warning for mismatched functions left without counts. Only
functions the profile annotation tagged as mismatched are annotated now, so
the recovered count stays among the mismatched ones.
2026-10-28 05:10:57 -05:00
Chris Tate d9481023c3 Annotate functions a stale profile misses from its block counts
A profile annotates a function only while its control flow hashes as it did
when the profile was collected, so compiler and source changes leave the
changed functions without counts. A --profile-use build now reads a
block-count file beside the profile (app.blocks beside app.profdata). Right
after the annotation before inlining, the helper aligns each uncounted
function's blocks with its recorded blocks by label kind and successor
count. Paired blocks take their recorded counts and their edges the recorded
edge counts, by target or, between targets whose labels changed, by
position. LLVM's flow inference fills in the rest, avoiding the edges the
function's own llvm.expect weights make unlikely. The helper then sets the
entry count, branch weights and hot or cold attribute as the profile
annotation does.

SCRIPTC_RECORD_PROFILE_BLOCKS names the file a --profile-use build writes
for the functions its profile annotates; such a build reuses no cached
artifacts. The block file keys every cache, the build report counts the
functions it annotated, and the staleness warning counts only the functions
left without counts. A single-module build with a context-sensitive profile
no longer counts annotated, mismatched and missing functions a second time
after inlining. scripts/refresh-cli-profile.mjs records the file in the
second phase and build-native-cli.mts reads it beside the profile.
2026-10-28 04:50:01 -05:00
Chris Tate d6bb226f48 Pass numbers to directly called untyped functions as jsvals (#2458) 2026-10-28 03:59:31 -05:00
Chris Tate 0325b921aa Compile pre-module constructor functions as classes and namespace functions as direct functions (#2175) 2026-10-28 03:57:06 -05:00