Commit Graph
25 Commits
Author SHA1 Message Date
Malte Ubl 439964a693 Inline the small-object allocator into emitted constructors and releases
Emitted record and class constructors called scr_rt_calloc/scr_cyc_alloc
with a constant size, so the size class, the zeroing length and the
collector header setup were recomputed per object behind a call (plus an
empty scr_obj_alloc_note call and a variable-length memset). llvm/alloc.ts
now emits the fast paths inline: pop the class free list (clearing only
the bytes the helper does not store itself) or bump, with the old calls as
the cold slow path. Releases inline the weak-dispose hook check, the live
count, and the free-list push; blocks outside the reservation still go to
scr_rt_free.

The emitted IR does not know which runtime it links against (the sanitized
lane only differs at link time), so the runtime now defines scr_sa in
every build. Where the allocator is compiled out (ASan, SCR_RC_AUDIT,
wasm32, Windows, 32-bit, thread-instanced libraries) it stays all-zero and
the inline paths always take the slow path, which keeps the RC-audit
object notes. ScrSaState's layout is pinned with static asserts. Inline
emission is additionally gated off for 32-bit, WASI, Windows triples and
thread-instanced libraries.

Runtime callers with hot constant-size allocations (union boxes, arrays,
boxes, closures, dyn nodes) use new static inline twins
scr_cyc_alloc_inline/scr_cyc_free_inline; scr_cyc_live is exported.

The ABI guard now recognizes function-pointer extern data
(scr_weak_dispose_hook). New corpus program 4410 pins recycled-block
clearing, size classes, objects above the largest class, cycles and weak
observers.
2026-10-08 15:36:07 -05:00
Malte Ubl 064277f297 Prefetch upcoming reference elements in the comparator sort kernel
On the x86_64 lane 41% of records-sort samples sat on the first load of
each record header (the comparator argument retain): a merge step touches
one element it has not seen before per comparison. Prefetching the
element four positions ahead on the advancing side (and during run
detection) overlaps that miss with the current comparator call: ~4%
faster on Linux (156 vs 162 ms median), neutral on macOS.
2026-10-08 15:31:04 -05:00
Malte Ubl 5ad1b976fd Decide string comparisons inline before calling the runtime
records-sort compares interned literals (`key === "department"`, equal
departments) and names that differ in their first bytes, yet every
comparison was an out-of-line call (and memcmp through a stub).

- sc_str_eq: identity, then byte length, then scr_str_eq.
- sc_str_cmp / sc_str_cmp_u16: identity; then the first differing byte
  of one 8-byte word when both strings are that long, otherwise of the
  first bytes; the UTF-16 order is decided inline only for ASCII bytes.
  Everything else calls the runtime.

The helpers are internal module functions that LLVM inlines; they are
emitted whenever the runtime entry is declared.

The comparator sort kernel also holds one undefined-`this` window for the
whole sort (each emitted call restores the receiver stack it pushes) and
resolves the element retain function once.
2026-10-08 15:31:04 -05:00
Malte Ubl bf10875927 Sort comparator snapshots in the runtime without per-move reference counting
After the union fix, records-sort spent most of its time in the IR merge
sort's element moves: every src[i] read retained the swapped `src` array
and the element (scr_arr_get_ref), every dst[k] write released the old
slot, and `dst` grew from an empty array.

scr_arr_sort_values runs the IR helper's exact algorithm (same comparator
call order) over borrowed slot copies of the private, compacted snapshot:

- comparator calls use the callValue ABI (owned element arguments, an
  undefined `this` window, pending-exception check);
- the array keeps owning every element and is untouched while user code
  runs, so the cycle collector and a throwing comparator see the original
  snapshot; the sorted permutation is published only after success;
- f64 and reference-counted element types qualify (isSortValuesElement);
  unions with an undefined arm and checked/island values keep the IR.

The new internal arrIntrinsic `sortValues` is validated, may throw, and
is not reference-preserving.
2026-10-08 15:31:04 -05:00
Malte Ubl 70dfdbd61e Route strings, arrays and emitted records through the small-object allocator
Move the allocator into scr_alloc.c with inline fast paths in scr_runtime.h;
strings, array headers/slot buffers, and emitted acyclic records/classes
(scr_rt_calloc/scr_rt_free) now use it. Adds scr_alloc.test.{c,ts}.
2026-10-08 15:01:26 -05:00
Malte Ubl ecd9643eb0 Remove the large-array cliff (1.2M-element fill and update: 10.4 s to 0.03 s) (#743)
* Remove the large-array cliff past 2^20 elements

Arrays longer than 2^20 kept every element beyond the cutoff in a sorted
side store, and each indexed write to an existing entry deleted and
reinserted it, so fill + indexed read/write loops went quadratic
(n=1.2M: 10.4s on macOS arm64, 12.6s on Linux x64).

- Rewrite existing sparse entries in place.
- Let dense storage grow past the cutoff for contiguous extension of a
  populated prefix, bulk range writes, and sparse tails that reach 3/16
  density, migrating side-store entries so index < cap stays dense.
- Make the side store a deque with front slack so descending first-touch
  writes and prefix migration are amortized O(1).
- Bound range-derived slice/splice capacity hints for hole runs.

scr_arr_new now reserves the live count, so a 2^20+3 element Set
converts into dense storage instead of a sparse tail; the white-box
test_lib check that pinned the old policy now asserts dense storage.

* Share immortal boolean union boxes

Maybe-undefined reads of boolean[] elements (`if (composite[i])` in a
sieve) box the element in a boolean|undefined union and free it at once:
one calloc/free pair per read, about a quarter of numeric-kernels samples
at large scales. Unions are immutable and boolean arms own no payload, so
low tags now return headerless immortal instances, the same convention as
the compiler's static unit-arm constants.

* Record preflight/order baselines for corpus 3138
2026-10-08 15:01:21 -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 0b168d5c23 Borrow stable loop references and bound deep cleanup
- Borrow stable loop references and share guarded dense array reads.
- Bound recursive cleanup across generated objects and runtime containers.
- Preserve ownership through mutation, callbacks, sparse reads, and abrupt exits.
2026-10-07 14:31:14 -05:00
Chris Tate 4ec55cdf64 Accelerate default primitive array ordering (#721)
- Sort primitive arrays with stable native ordering and pure conversion keys.
- Preserve sparse semantics and argument effects while avoiding callback overhead.
- Verify Unicode ordering and ownership across native and WASI paths.
2026-10-07 08:15:08 -05:00
Chris Tate f22013f177 Improve array storage and bulk transformations
- Share dense array storage and move owned ranges without rebuilding containers.
- Batch array construction and insertion while preserving evaluation order and bounded temporary storage.
- Lower indexed mutations natively and preserve typed-array reversal across numeric element types.
2026-10-06 01:05:40 -05:00
Chris Tate addc2418ab Streamline native string construction
- Combine bounded string concatenations while preserving evaluation and ownership.
- Build array joins and raw strings directly in their final storage.
- Support literal null and undefined string conversions with preserved effects.
2026-10-05 07:32:26 -05:00
Chris Tate 0311082801 Reduce cycle traversal overhead
- Bound collector recursion and reuse scanned candidates during cycle cleanup.
- Skip redundant candidate checks and unused array storage.
- Preserve reference counts across deep, shared, and mixed-generation graphs.
2026-10-05 06:26:26 -05:00
Chris Tate 668eb78124 Optimize native array loops and preserve live tuple iteration
- Reduce allocation and reference-management overhead in native array loops.
- Enable vectorization while preserving numeric behavior.
- Preserve live tuple iteration and cover reference lifetimes and missing values.
2026-10-04 10:38:10 -05:00
Chris Tate e2d58f60d2 Expand static support for common Effect 4 patterns (#600)
* Expand static compilation for common Effect 4 patterns

* Run every Effect 4 fixture slice in CI

* Preserve existing corpus behavior across Effect 4 lowering

* Preserve Effect platform callbacks and native stream state

* Record new corpus baselines and retain child stdin refusals

* Retire the obsolete stateful regex diagnostic

* Preserve promise reference count layout for cycle collection
2026-10-01 17:36:29 -05:00
Chris Tate eb7be34d9a perf: reduce compiler work and reuse CI native builds
- Reduce frontend, runtime, and linking overhead for native builds.
- Add compact compiler artifacts and reproducible build timing benchmarks.
- Reuse native CI packages and consolidate self-hosting validation.
2026-09-30 11:08:13 -05:00
Chris Tate f52b3b5b73 Compile untyped Map and Set registries without a JavaScript engine
- Store checked values in native collections while preserving supported object identities and JavaScript key equality.
- Support collection iteration and reject reference conversions that would copy or adapt stored objects.
2026-09-28 01:12:29 -05:00
Chris Tate d2195d8b6e Optimize terminal startup and memory use (#464)
- Reduce allocation overhead in array comparisons and string operations.
- Reuse fiber stacks and release cached memory while idle.
- Read terminal dimensions directly from native streams.
2026-09-26 10:02:56 -05:00
Chris Tate 1f23c28bb0 Expand static Array flat, splice, and join lowering (#460)
* Expand static Array flattening, splice insertion, and join coercion

* Refresh Array diagnostic and TypeScript 7 baselines
2026-09-26 08:41:06 -05:00
Chris Tate 6242053fc9 perf: fuse numeric array reads into one runtime lookup (#399) 2026-09-24 00:03:04 -05:00
Chris Tate 7482bc773b feat(math): add static support for analytic functions (#395)
- Lower ten Math methods through C and LLVM, including variadic hypot.
- Match JavaScript numeric edge cases and add differential coverage.
2026-09-23 19:12:54 -05:00
Chris Tate 94fd361b54 Implement sparse native arrays and missing-value parity (#297)
Represent ordinary JavaScript arrays with hole, value, and explicit-undefined states; add sparse side storage and carry missing values through the frontend, native backends, runtime, and Node-facing consumers.
2026-09-12 22:39:07 -05:00
Chris Tate 6f7a1a08fe feat: support thread-instanced library state (#137)
* feat: support thread-instanced library state

- Add the abi.instance_per_thread profile field: the archive's TUs compile
  with -DSCR_THREAD_INSTANCES, and the SCR_TL qualifier in scr_runtime.h
  moves every runtime unit's mutable statics into thread-local storage;
  both backends emit the program TU's module globals, run-once guards, and
  lazily-compiled regex literal caches thread-local to match
- ONE linked archive then serves one independent instance per embedder
  thread through the unchanged entry family: a thread registers its sink
  and calls the init entry, and owns its own collector, result arena,
  poison flag, and program state — a trap poisons only the instance it
  fired in while sibling threads' instances keep answering
- Document the contract beside the profile spec: one instance per thread,
  selected implicitly by the calling thread; instance lifetime is the
  thread's lifetime; the never-entered-from-two-threads rule is unchanged;
  independent of and composable with abi.localize_runtime
- Immutable interned data (string literals, unit arms, template arrays,
  vtables) stays shared — Darwin ASan's image-registration common
  included, keeping the one-registration-per-image discipline; the
  exception cell's current pointer resolves a NULL sentinel in this mode
  because a thread-local address is not a constant initializer
- Add the four-thread acceptance probe (distinct per-thread workloads,
  concurrent instance-local inits, per-instance collects, a trap delivered
  to its own thread's sink exactly once), the composition probe pairing a
  thread-instanced localized archive with a second different-prefix
  localized archive, an explicit ASan rerun beside the suite-flavor
  sanitized builds, and profile-shape coverage; schedule the new lane
  contracts beside M1/M2
- Non-opted builds are byte-for-byte unchanged: the qualifier expands to
  nothing outside -DSCR_LIB -DSCR_THREAD_INSTANCES (verified object-level
  over every touched runtime TU in both default and library flavors)

* fix: keep sanitized runtime-localized archives linkable on ELF

ASan's instrumented globals ride ELF section groups; archives built from
shared runtime objects carry groups with REPEATED signatures, so a process
linking two runtime-localized sanitized archives kept one archive's group
and discarded the other's — whose now-local references then dangled at the
embedder's link. Resolving the groups into the combined relocatable member
(ld -r --force-group-allocation) keeps every archive's copies; the
localization step then demotes them per archive exactly like unsanitized
state. Plain builds carry no section groups, so the flag is inert there.

With the groups resolved, ELF surfaces the same deliberate exception
Mach-O already documented: the image-wide registration guard COMMON stays
shared so the final image registers its ASan globals exactly once — M1/M7
now pin that spelling on both platforms. The explicit ASan pairing (M8)
additionally points Linux LSan away from contractually thread-lifetime
instance state, exactly as the sanitized suite lanes do.

* fix: isolate inspect state per thread

* fix(runtime): share uptime anchor across threads
2026-08-12 09:49:43 -05:00
Chris Tate b3d3101550 Add static array unshift and reverse (#107)
- Lower unshift and reverse through the C and LLVM backends.
- Preserve spread, mutation, evaluation-order, and reference-counting semantics.
- Add runtime, differential, and surface-manifest coverage.
2026-08-09 14:25:53 -05:00
Chris Tate 59274de9d7 Trap funnel: scr_trap/scr_trap_fmt over every core-linkable trap site
- executable expansion in scr_console.c is byte-identical fputs/vfprintf + abort; core expansion in the new scr_core.c poisons, delivers to the registered sink exactly once, aborts pre-registration or if the sink returns
- 89 fputs/fprintf+abort pairs and 10 bare OOM aborts across the 20 core-linkable TUs now funnel; lazy atexit teardowns (json errdyn, regex bytecodes, symbol registry) reroute to the core reset registry via scr_atexit
- scr_core.c owns sink/poison/result-arena/reset-registry/marshalling helpers; scr_exception.c renders escaped exceptions as "Uncaught ..." into the funnel; scr_lib exposes its cleanup to the reset seam
2026-07-23 01:14:32 -05:00
Chris Tate 23d5918a53 init 2026-07-22 18:08:04 -05:00