Commit Graph
5381 Commits
Author SHA1 Message Date
Chris Tate acb2d23df5 Attribute slot causes to the owner only for types without their own slot 2026-10-26 07:59:53 -05:00
Malte Ubl 572e7e3eac Merge remote-tracking branch 'origin/lf/l4-expressions' into lf/l5-polish
# Conflicts:
#	packages/compiler/src/frontend/lowering/lowerer.ts
2026-10-26 05:37:00 -07:00
Malte UblandClaude Opus 5.5 ec9624997a Attribute the dynamic pieces of pushed-down types in SC6101 and the slot operation counts
A pushed-down slot's dynamic operations act on its failing components,
whose checker types are not degraded themselves. The lenient mapping
records those components with the declared type they belong to; SC6101
names them ('hook' is a dynamic piece of 'Plugin[]'), and the operation
counts go to the owning slot. Causes read a binding's declared type, not
its narrowed flow type. The coverage test uses its own fixture, so it does
not depend on which interface members main can map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:35:58 -07:00
Chris Tate 75e48ddec3 Merge lf/l3-push-down 2026-10-26 06:55:57 -05:00
Chris Tate 92e42f7c6f Merge lf/l2-declarations and main, and fix self-hosting 2026-10-26 06:24:09 -05:00
Chris Tate 2ad699c98a Compile classes that extend Array (#2059) 2026-10-26 03:57:16 -05:00
Chris Tate e289b03ddc Keep int32 fields in programs with adopting casts (#2324) 2026-10-26 03:27:40 -05:00
Chris Tate eec525dc6a Fix eight compiler and type bug reports (#2319) 2026-10-26 03:10:55 -05:00
Chris Tate 6c1ddbf390 Keep int32 fields in programs with adopting casts 2026-10-26 02:57:52 -05:00
Chris Tate acf19e2652 Merge main 2026-10-26 02:53:26 -05:00
Chris Tate 7a8ec3f3d2 Hold locals and module globals whose declared type does not compile as checked dynamic slots (#2292) 2026-10-26 02:49:06 -05:00
Chris Tate 7183a76422 Address review: missing strings in bindings and calls, evaluated recursive flags
- A string held in a binding or returned by a call can be a missing element
  read (undefined), so `head + PAD[j]` and `at(i) + PAD[j]` still add to NaN
  when both are missing. Only string literals, templates and sums with such
  an operand now count as always a string.
- `readdirSync(path, { recursive })` folds only the `true` and `false`
  keywords. Any other flag expression, including one typed as a literal,
  evaluates at run time for its effects and exceptions.
2026-10-26 02:42:04 -05:00
Chris Tate f52806988d Read typed-array header fields once at a speculative loop's entry (#2321) 2026-10-26 02:33:23 -05:00
Chris Tate 3512b288ad Order the native compiler's cold functions by how many builds run them (#2320) 2026-10-26 02:33:05 -05:00
Chris Tate 035d618517 Store a table entry for if/else chains that store literals to one element (#2317) 2026-10-26 02:21:51 -05:00
Chris Tate 8a37d54e65 Register classes and functions with dynamic slots when a reference flushes their type failures (#2293) 2026-10-26 02:14:26 -05:00
Chris Tate eaa8a44cdf Read typed-array header fields once at a speculative loop's entry
A speculative integer loop's fast copy exits to a copy that resumes the
failed iteration, and both read the header fields of the views they
access. GVN's load PRE moved the fast copy's read of a view's data
pointer ahead of the exit branch, to make the resume copy's read
redundant, and put a copy on the exit edge. The loop then kept its first
iteration in the loop, so the overflow checks of a value whose range is
known after one iteration stayed in every iteration, with a reload of the
data pointer.

A view held by a const local or a parameter that the loop does not rebind
is the same view in every iteration, and its length, data pointer and
shared storage never change. The loop's entry now reads them once, and the
element accesses of every copy use those values.
2026-10-26 02:02:46 -05:00
Chris Tate 71de76b813 Order the native compiler's cold functions by how many builds run them 2026-10-26 02:02:18 -05:00
Chris Tate e03afc25f3 Add range-proof regression cases for constant-choice tables 2026-10-26 01:51:53 -05:00
Chris Tate 055d1f9682 Fix eight compiler and type bug reports
Co-authored-by: dolphin278 <1130473+dolphin278@users.noreply.github.com>
Fixes #2139
Fixes #2131
Fixes #2132
Fixes #2133
Fixes #2140
Fixes #2141
Fixes #2159
2026-10-26 01:50:59 -05:00
Chris Tate 5d62d5a5f1 Read a constant choice's index as on every path when it is the compared local
The store a constant choice emits for its arms used the first arm's
index expression, with the ranges proven inside that arm. When the index
is the compared local, `j === 0` narrows it to [0, 0] there, so the one
store, which also runs on the final else's path, converted the index to
an int32 from that range. For `j` = 4294967297 that conversion has no
defined result, and the bounds check that follows could accept it: an
out-of-bounds write could reach a valid element.

The store now reads such an index through the first comparison's
operand: the same local, read before the chain branches, so its range
holds on every path. An index that is another local is not narrowed by
the comparisons and is unchanged.
2026-10-26 01:47:16 -05:00
Chris Tate 70eac263f7 Merge lf/l1-whole-slot 2026-10-26 01:43:44 -05:00
Chris Tate 771706cfbe Wait for the lookup connection's accept in 7515 before changing the server's mode (#2318) 2026-10-26 01:38:08 -05:00
Chris Tate 60bd974744 Wait for the lookup connection's accept in 7515 before changing the server's mode
7515-net-socket-backpressure's server picks each connection's handling from
`mode` when it accepts it. The lookup client's 70000 bytes fit in the
kernel's buffers, so its 'drain', and the step that sets `mode` to "pause",
can run before the server accepts that connection. On macOS the accept can
come in a later kqueue batch: the server then paused the lookup socket,
never read its EOF, and the open connection kept the program running after
server.close(). The program now waits for that accept before it changes
`mode`. Its output is unchanged.
2026-10-26 01:10:20 -05:00
Chris Tate a28afa0261 Find bridgePlainUnwindCalls' calls and mentions with indexOf (#2316) 2026-10-26 01:08:35 -05:00
Chris Tate 33be2a80ee Store a table entry for if/else chains that store literals to one element
An if/else-if chain that compares one number local with integer literals
and stores a number literal to the same typed-array element in every arm,
including a final else, such as

    if (x === 97) a[i] = 0;
    else if (x === 116) a[i] = 1;
    else if (x === 99) a[i] = 2;
    else a[i] = 3;

is now emitted as one store of an entry of a constant table indexed by
the local's value (constant-choices.ts). On input whose cases do not
repeat in a pattern, such as the bases of a DNA sequence, the branches
mispredict; LLVM keeps them, because cases this far apart are too sparse
for its own switch tables.

The chain needs at least three distinct values within 256 of each other.
The table covers the cases' span, or the local's proven integer range
when that range has at most 256 values, in which case no test is
emitted. Otherwise a value that is not an exact integer in the table's
span (NaN, a fraction, or a value outside it) stores the final else's
literal, chosen with a select. The first arm wins for a repeated
value, and -0 selects the arm for 0, as strict equality does. The store
is the arms' own (the same receiver and index), converted to the
element's type as each arm's would be. Debug builds keep the arms.

k-nucleotide's transform of the input sequence uses this form: its
median wall time drops from 2.71 s to 2.21 s.
2026-10-26 01:04:42 -05:00
Chris Tate 2a0d115629 Keep a candidate's generation when a limited cycle pass reaches it (#2315) 2026-10-26 00:55:26 -05:00
Chris Tate 2e142d6121 Find bridgePlainUnwindCalls' calls and mentions with indexOf
bridgePlainUnwindCalls rewrites the module text after emission on Linux.
It split the text with a lookahead regular expression, ran a regular
expression over every line to find mentions of raising functions, and
split every function that has calls into lines to test each one.

- The text splits before each `define` line with indexOf.
- Mentions of raising functions are found by scanning for `@` and reading
  the name that follows it.
- A function without a plain call of a raising function rewrites only the
  lines that hold `call `, marking them nounwind. A function with such a
  call takes the line-by-line path as before.

The emitted text is unchanged.
2026-10-26 00:40:44 -05:00
Chris Tate e2a22aa1bc Localize merged program shards in process on Linux (#2310) 2026-10-26 00:31:57 -05:00
Chris Tate 77f65a7264 Skip the element read for array callbacks that never use it (#2286) 2026-10-26 00:29:20 -05:00
Chris Tate 7e3d10fa13 Lay out the native compiler's functions by the code each kind of run executes (#2265) 2026-10-26 00:29:02 -05:00
Chris Tate 11aa86d571 Cache named child nodes and text on AST nodes (#2314) 2026-10-26 00:26:48 -05:00
Chris Tate feb4501013 Keep a candidate's generation when a limited cycle pass reaches it
A limited pass (scr_cyc_collect_set, which freeing a LifetimeRegion runs
over the objects the region owned) takes only the candidates in its set.
Its walk can still reach other candidates, and one that is referenced from
outside is blackened by scan and promoted to the next generation while it
keeps its slot in its old generation's buffer. scr_cyc_on_dead finds a
candidate's buffer by its generation, so when that object died it removed
another buffer's entry and left its own slot pointing at the freed block,
which the next pass then read.

Such a candidate is no longer promoted: it keeps the generation of the
buffer it is in until a pass takes it.
2026-10-26 00:24:05 -05:00
Chris Tate e7b60a5302 Read and write shared typed-array elements at a counted loop's counter with plain accesses (#2309) 2026-10-26 00:23:15 -05:00
Chris Tate 21bce9e4fa Merge main 2026-10-26 00:21:24 -05:00
Chris Tate b090950ffe Read undeclared literal keys as undefined and keep filled-in JavaScript vars dynamic (#2311) 2026-10-26 00:08:06 -05:00
Chris Tate 04c0e5c66e Cache named child nodes and text on AST nodes
The frontend reads the same children of the same nodes many times: on a
tsc-ts build, `expression` is read 29.6M times over 253K nodes, `name`
16.4M times over 176K and `text` 26.8M times over 456K. Each read went
through AstFile.namedChildAt (the file check, the child mask, the packed
child table) and AstFile.node (the node table), and `text` through the
wire's kind switch and string table.

- AstNode.childNodeAt keeps the child nodes it has read, by wire slot, in
  a per-node array sized to the node's highest named child.
- AstNode.text keeps the text it has read.
- AstFile's packed child table is a Uint32Array from the start (empty
  until the first named child is read), so namedChildAt no longer
  retains and releases an optional value on every call.

Cold tsc-ts build with the native compiler, 16 interleaved rounds: lower
31.68 -> 30.46 s, wall 78.81 -> 76.56 s (faster in every round); peak RSS
+105 MB, which the frontend-dispose heap trim keeps below main's peak.
2026-10-25 23:57:36 -05:00
Chris Tate d60698695f Read an ignored element before its callback runs
The element-independent callback paths read the slot after the callback:
- filter appended the slot as it was after the predicate;
- find and findLast read the element only once the predicate held.
A predicate that overwrote or removed the current slot therefore kept or
returned the new value, where JavaScript keeps the element it read before
calling the predicate. An unfused filter over proven-present elements also
stored the placeholder it passed to a predicate with an unused element
parameter.

- filter, find and findLast keep a snapshot of the slot, read before the
  callback runs (arraySlotSnapshot): the value, or the dynamic value of an
  element outside the element type. filter appends that snapshot as it
  was stored, and find returns it, with the checked conversion for such an
  element. Fused loops take the same snapshot before a stage whose
  callback ignores the element.
- Leaving an ignored element unread now applies only to arrays that can
  hold elements outside their element type (elementMayBeForeign) and are
  not proven present. Every other array reads the element as before, so
  its filter stores the element it read.
2026-10-25 23:56:15 -05:00
Chris Tate 4a39f8e1b7 Address review feedback 2026-10-25 23:55:18 -05:00
Chris Tate ef6f8838d0 Address review feedback 2026-10-25 23:53:54 -05:00
Chris Tate 6817a24581 Merge lf/l0-report and main 2026-10-25 23:49:52 -05:00
Chris Tate e30700f9e0 Guard every x86 intrinsic header in the toolchain probe, and declare mkdtemp on macOS (#2313) 2026-10-25 23:44:47 -05:00
Chris Tate a8080733df Build the bootstrap test's own seed without the function layout
The bootstrap test builds its own seed when CI shares none, and that build
still applied the committed function layout, so its LLVM listed functions in
a different order from the native compiler's own emission. Both seed builds
now share one environment, which leaves the layout out.
2026-10-25 23:42:51 -05:00
Chris Tate 41957ec668 Update tsc-ts to 0.0.10 (#2297) 2026-10-25 23:39:19 -05:00
Chris Tate 801444fc30 Keep optional chains out of the undeclared-key absence probe
A presence test of a literal key that a record shape does not declare
read the receiver as the record and answered undefined. In an optional
chain that conversion runs before the chain can short-circuit, so
`missing?.["a;b"] === undefined` threw a TypeError for a missing value
stored dynamically, where Node answers true. An optional read keeps the
optional chain's lowering, which reads a nullish receiver as undefined
and the undeclared key of a present one as undefined.
2026-10-25 23:37:53 -05:00
Chris Tate 13757c7da3 Add the canonical type normalizer, not yet wired into mapping (#2305) 2026-10-25 23:36:57 -05:00
Chris Tate f2c53ff2f8 Build the bootstrap seed without the function layout
The native bootstrap compares the seed's LLVM, which the Node-hosted
compiler emits, with the LLVM the native compiler emits for itself. The
seed's build applied the committed function layout and the native
compiler has none, so the two listed the same functions in different
orders. The layout only orders functions, so the seed is built without it.
2026-10-25 23:32:41 -05:00
Chris Tate fc88e5c474 Return the freed heap pages to the system after the frontend is disposed (#2306) 2026-10-25 23:24:57 -05:00
Chris Tate 9847375966 Run registered classes' static initializers in place and keep retried statements' diagnostics
- A deferred class with static field initializers is reached where its
  statement evaluates: its file's %init retries it there
  (retryDeferredAtDeclaration), so its initializers lower in place. A failed
  retry reports nothing, restores the types it degraded, and leaves the
  class deferred; no later flush registers it, so it reports at its
  reference instead of registering after its initializers' place. Before, a
  class a later reference registered never ran its static initializers.
- A generic class that registers on retry registers the units of its
  static methods. Before, a call to one was an undeclared function
  (SC9001).
- A statement that lowers again after degrading a type keeps the
  diagnostics of the function-local class bodies it lowered the first time:
  the retry reuses those bodies instead of lowering them again.
- retriableDiags and failedTypes are a Set and a Map owned by the lowerer
  instead of weak collections, and the degraded types of a mode are copied
  without an empty-array fallback, which the self-hosted compiler could not
  lower.
- The coverage test of a class rejected behind a receiver probe pins its
  refusal under --tier=static-only, as the diagnostics corpus does; under
  localized failure the class compiles.
2026-10-25 23:22:35 -05:00
Chris Tate 766a732f8e Initialize a forward-captured binding's box when its statement lowers again
A binding that a function declared earlier captures is pre-declared as a
TDZ box, and its declaration's assign initializes the box. The declaration
dropped the pre-declaration before lowering its initializer. When the
initializer failed with a degradable type (a cast whose type does not
compile) and the statement commit point lowered the statement again, the
retry no longer saw the box: it declared a new local and initialized that,
and a later call of the function threw ReferenceError.

The pre-declaration is now dropped after the initializer lowers, for
variable, class and generic-function declarations.
2026-10-25 23:21:20 -05:00