Commit Graph
11 Commits
Author SHA1 Message Date
Chris Tate 51c9e31533 Skip the receiver bracket for adapters of receiver-free callbacks
A signature adapter forwards the ambient receiver to its original, and
its closure carries the original's RECEIVER_FREE bit. Calls through the
closure's own pointer tested that bit, but the direct entry of a known
adapter target bracketed every call, because the adapter itself counts
as a receiver reader. The direct entry now tests the bit too.

The receiver readers analysis also admits more node kinds that run no
user code: optional chains, record presence and absent fields, dynamic
boxing, array for-of loops, typed array writes, typed array methods and
constructors over typed operands, the module binding check and region
intrinsics, performance.now, Atomics operations, string and number
library calls over primitives, and new Error(message, options) without
an options object.
2026-10-29 10:42:30 -05:00
Malte UblandClaude Opus 5.5 5a3d6d7903 Call function tables without boxing or reference counting
Values typed `F | undefined` for a function type F are now plain closure
pointers (nullable-pointer unions), so a keyed read of a
`Record<string, F>` or a Map.get no longer allocates a union box. Keyed
record reads, string-keyed Map.get and checked extractions lend the stored
closure as a receiver or callee when ReferenceWrites proves the call cannot
write the locations it was read from, and optional-chain receivers lend
under the same proof. Owned record overflow reads use the inline entry
lookup.

Indirect closure calls skip the ambient receiver bracket for closures
whose bodies never observe it (new ScrClosure.function_kind bit
SCR_FN_RECEIVER_FREE; signature adapters that only forward the receiver
copy it from their original), and a callValue that forwards `dyn.this`
needs no bracket at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 14:49:00 -07:00
Chris Tate feec4bf4df Call typed-array callbacks directly and visit on an integer counter
The helpers of the typed-array callback methods (forEach, map, filter,
some, every, find, findIndex, findLast, findLastIndex, reduce,
reduceRight) called their callback through the closure's code pointer on
every element, and one helper served every call with the same signature,
so the callback was never inlined. A literal callback now gets a helper
of its own, as for the array methods, and the constant-callback census
accepts the typed-array helpers, so the helper calls that function
directly.

The visit is now a loop on its own counter, `for (let i = 0; i < a.length;
i++)` or down from the last index, instead of a loop on a counter
declared before it, so the counter is an integer and element reads need no
index test. reduce and reduceRight without an initial value read their
seed at the first visited index and start one past it.

Adds reduce, some and map callbacks to the typed-array-kernels workload.
2026-10-22 04:44:34 -05:00
Malte UblandClaude Opus 5.5 89bca68cbe Compute with NaN-boxed jsval values in the dynamic tier
Stage S3 of the two-tier design: dynamic-tier functions compute with
jsval, a new IR type holding a 64-bit NaN-boxed word whose numbers,
booleans, undefined and null are immediates, so a number between two
dynamic operators is no longer a heap node.

- IR: the jsval type and js.* builtins (operators and the boundary
  conversions fromDyn/toDyn/fromF64/fromBool/fromStr/fromBigInt/toF64).
  The jsval pass rewrites dynamic-tier functions after tier assignment:
  operator chains, comparisons, number checks and truthiness of operator
  results run on jsvals; dyn locals become jsval locals when that saves
  boxing; dynamic functions called only from dynamic functions return a
  jsval. IR version 17 serializes each function's tier.
- Runtime: scr_js.h and scr_js_value.c behind the dynTier feature
  (invariant I3); static runtime units never include scr_js.h.
- Backend: number cases inline (immediate doubles and NUM cells), always
  inlined jsval retain/release; 32-bit targets keep boxed cells.
- Interprocedural analyses see jsval conversions as the dyn forms they
  replace, so no fact about a static function changes.

Static functions are untouched: typed programs and the self-hosted
compiler's static functions compile to byte-identical LLVM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:11:32 -07:00
Malte UblandClaude Opus 5.5 1d1e934b4c Copy callback helpers per known callback target
Direct calls whose callback arguments resolve to known functions call a
copy of the helper made for that tuple of targets, so the constant-callback
analysis devirtualizes and LLVM inlines the callback. Constant callbacks
also resolve through recursion that forwards the callback, through other
consumers' parameters, and through unwritten captures.

Copies are limited to bodies of at most 300 IR nodes and 4 copies per
function, which keeps the self-hosted compiler within 1% of its size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:14:49 -07:00
Malte Ubl 8d7f23ae76 Borrow generator parameters and devirtualize their constant callbacks
A generator frame owns its parameters until its final exit, across every
suspension, so an unchanged, unboxed reference parameter is a stable
owner that reads may borrow (CallLifetimes.generatorParameters). Calling
a callback parameter on every step no longer retains and releases it.

Generator declarations entered only through direct (spawn) calls are now
constant-callback consumers: when every call passes the same function,
the body enters it through the guarded direct path, which inlines it and
drops the dynamic-`this` bracket for callees that cannot observe it.
2026-10-16 06:59:42 -07:00
Chris Tate 7e6294ca0f Make scriptc compilation native-only 2026-10-12 12:25:30 -05:00
Malte Ubl 425386b877 Call array-method callbacks directly and pass plain elements
Array HOF helpers (%arr.map/filter/forEach/flatMap/find*/some/every/
reduce*) called every callback through its closure, bracketed by the
ambient JS receiver stack, with each element boxed as T | undefined.

- Intern a helper per literal callback and admit %arr.* helpers as
  constant-callback consumers: the callback becomes a guarded direct
  call that LLVM inlines into the loop.
- Replace the scalar-only receiver check with an exact module fixpoint
  (AmbientReceiverReaders) over direct calls, constructors and virtual
  dispatch; known callbacks that cannot reach a receiver read skip the
  push/pop.
- For inline arrows over plain element types, lower the callback a
  second time with the element type and split each visit on the array
  state: present values take the fast closure with no union box; holes
  and present undefined keep the original closure and undefined arm.
- Resolve factory-made callbacks (every return yields one function) and
  immutable captured callbacks at their closure creation sites.
- Read and write scalar capture boxes in place instead of calling
  scr_box_get_f64/scr_box_set_*.

Corpus 4140-4142 pin the split visit, receiver brackets on the direct
path, and factory/captured callbacks.

Linux x64 (sandbox, --layouts=4 --runs=16, baseline stack 06 tip):

| workload | baseline ms | candidate ms | change | 95% CI | verdict | size | node ms | candidate/node |
| --- | ---: | ---: | ---: | --- | --- | ---: | ---: | ---: |
| json-records | 192.5 | 193.8 | +0.7% | -2.1% .. +2.4% | neutral | +0.0% | 436.3 | 0.44x |
| regex-logs | 166.4 | 165.5 | -0.5% | -1.8% .. +0.9% | neutral | +0.0% | 243.9 | 0.68x |
| ast-interp | 264.9 | 243.0 | -8.3% | -9.5% .. -6.9% | faster | +0.0% | 232.2 | 1.05x |
| records-sort | 593.2 | 576.4 | -2.8% | -6.6% .. +0.3% | neutral | +3.0% | 210.3 | 2.74x |
| template-render | 69.3 | 68.6 | -1.0% | -3.2% .. +2.6% | neutral | +0.0% | 189.0 | 0.36x |
| functional-pipeline | 215.8 | 111.0 | -48.6% | -49.6% .. -46.3% | faster | +3.8% | 255.8 | 0.43x |
| async-pipeline | 194.8 | 196.8 | +1.0% | -1.1% .. +12.0% | neutral | +0.0% | 85.9 | 2.29x |
| word-graph | 271.8 | 270.1 | -0.6% | -7.0% .. +5.5% | neutral | +3.0% | 517.1 | 0.52x |
| alloc-trees | 314.4 | 316.6 | +0.7% | -0.4% .. +1.9% | neutral | +0.0% | 124.9 | 2.53x |
| numeric-kernels | 46.2 | 45.7 | -1.1% | -2.5% .. +1.5% | neutral | +0.0% | 128.2 | 0.36x |
| validate-errors | 179.7 | 174.5 | -2.9% | -5.4% .. +0.7% | neutral | +0.0% | 711.1 | 0.24x |
| csv-numbers | 343.0 | 338.9 | -1.2% | -7.3% .. +0.7% | neutral | +0.0% | 519.6 | 0.65x |
| log-summary | 166.1 | 168.3 | +1.3% | -4.4% .. +9.1% | neutral | +0.0% | 300.0 | 0.56x |
| inventory-report | 298.1 | 290.1 | -2.7% | -14.6% .. +5.4% | neutral | +1.3% | 421.4 | 0.69x |

Geomean -5.9%; candidate/node 0.715x. macOS arm64 (shared host):
geomean -4.7%, functional-pipeline -44.1%, ast-interp -11.0%,
inventory-report -8.0%; none slower (records-sort -0.5% on a calmer
rerun).
2026-10-08 15:38:45 -05:00
Chris Tate 1f10e83720 Promote canary to main (#738)
- Preserve callback identity, receiver binding, and class references across unions and collections.
- Track record field presence independently of undefined values and improve native runtime behavior.
- Expand differential coverage and streamline CI bootstrap and compiler rebuild checks.
2026-10-08 11:00:13 -05:00
Chris Tate 095b619330 Add native workers and shared memory
- Add native worker execution with isolated module state, structured messages, and lifecycle controls.
- Support shared buffers and integer atomics with coordinated runtime ownership and cancellation.
- Extend compiler, runtime-pack, differential, and compatibility coverage for native concurrency.
2026-10-07 19:07:02 -05:00
Chris Tate a8bc0f6abf Optimize scalar string access and numeric calls
- Avoid materializing scalar string slices and preserve index metadata.
- Specialize known callbacks and numeric call boundaries.
- Lower proved integer remainders without changing number semantics.
2026-10-07 15:39:05 -05:00