The --layouts wrapper always forwarded to clang. A musl target links with
`zig cc`, and SCRIPTC_LINKER replaces the driver together with its `cc`
argument, so every layout build failed. The wrapper now forwards to
`zig cc` when SCRIPTC_TARGET names a musl triple.
The LLVM helper's runtime-unit now optimizes a unit itself (--opt-level),
with IR instrumentation (--profile-generate) or a profile (--profile-use),
through the same pipeline as program code. The Linux x64 pack's speed
units are compiled to unoptimized bitcode and optimized there with
packages/runtime-pack-common/profiles/linux-x64-gnu.profdata.gz, so one
LLVM writes and reads every profile. Release and library flavors are
unchanged. The pack manifest records how the profile matched the units.
Code the runtime profile finds cold is optimized for speed, not size: a
runtime function the training programs rarely call can be hot in another
program. scr_message.c is optimized without the profile.
scripts/refresh-runtime-profile.mjs trains the profile on the runtime
benchmark workloads and, optionally, a tsc-ts checkout, and keeps only the
records of functions the instrumented runtime units define
(merge-profiles --keep-functions). Tests check that the profile still
matches the runtime and names only runtime functions.
With --cpu-dispatch, an x86-64 executable carries two copies of each
program function: the baseline code and a copy compiled for x86-64-v3.
main reads CPUID and XGETBV once. Calls between the program's functions
go to the caller's copy. A function that is also named outside the
copies (a function value, a call from main, a runtime callback or a
coroutine body) keeps a dispatcher under its own name, which tests the
level and tail-calls one copy. Functions without such a use get no
dispatcher, so their copies have the callers the original had and LLVM
inlines them the same way.
The executable still runs on any x86-64 CPU. SCRIPTC_CPU_LEVEL caps the
level at run time. The option is off by default, applies to executables
without a --target-cpu level, and is part of the executable cache key.
bench:runtime takes --baseline-cpu-dispatch and --candidate-cpu-dispatch,
and the LLVM differential suite runs each program at levels 0 and 3 with
SCRIPTC_CPU_DISPATCH=1.
readline split redirected input byte by byte, twice per chunk (once to
find whether the chunk ends a line, once to split it). Before splitting
it copied each chunk out of its Buffer and into a scratch buffer, decoded
it into a new string, and copied the unfinished line and the whole chunk
into a joined buffer. Now:
- one scan per chunk: memchr for a CR once, then LF to LF with memchr
when the chunk has none, and the LF, CR and CRLF split otherwise;
- the unfinished line joins only the first line of the chunk;
- an ASCII chunk with no partial UTF-8 sequence held from the previous
one is split where it is, and a chunk that is not shared memory is
read from its own storage. Lines are still copied into their strings
before the first is delivered.
Buffer.from(string, 'latin1' | 'ascii') encoded character by character;
an ASCII prefix, all of an ASCII string, is now copied with memcpy.
Line events, their order and the CRLF delay are unchanged. Corpus 8344
covers lines joined across chunks, a line longer than a chunk, empty
lines, mixed line endings and UTF-8 split across chunks. The new
line-reader workload reads a 67 MB log through readline over a file
stream.
Release builds compile for the x86-64 baseline, so LLVM vectorizes with
SSE2 only. `--target-cpu=<baseline|x86-64-v2|x86-64-v3|x86-64-v4|native>`
compiles the program's code for a higher level: every emitted function
carries the level as its `target-cpu`, so LLVM can use AVX2, BMI2, FMA or
AVX-512. The executable then runs only on CPUs of that level.
- `native` resolves to the highest level the build machine supports, read
from /proc/cpuinfo on Linux and `sysctl machdep.cpu` on macOS.
- The level is part of the executable cache key; `baseline` and builds
without the option produce the same output and keys as before.
- The runtime pack keeps its baseline code.
- Targets other than x86-64 refuse a level (SC3002). The CLI's cached
bootstrap route leaves builds with the option to the full compiler.
- `bench:runtime` takes `--baseline-target-cpu` and `--candidate-target-cpu`.
workloads.json listed every workload in one array, and its README table
listed them in one hand order, so changes that each added a workload
appended at the same place and conflicted. Each workload is now a JSON
file in benchmarks/runtime/workloads/ named after it, read through
benchmarks/runtime/workloads.mjs by the bench runner, the zero-cost
gate, the strace script and the dynamic-tier test, ordered by category
and then name. The loader still reads workloads.json in a checkout from
before this change, so the gate can compare against one. The README
table follows the same order.
The --layouts padding object was linked directly before the program
object. The runtime is linked before the program object, so every layout
moved only the program's code and left each runtime function at the same
address. A change to the runtime's code size then moved its functions in
all layouts at once, and the verdicts could not tell that placement
change from a real one.
The padding object now goes before every other input on the link line,
so each layout shifts the runtime and the program code.
Speed is a third native optimization posture next to release (default,
-O2) and dev (-O0). It is optimized exactly like release at the C/LLVM
level and is the switch for optimizations that trade executable size and
build time for run-time speed, so the default release build stays inside
the binary-size budget.
- backend/optimization.ts: the NativeOptimization type, its -O class, and
cache-key helpers. Release keeps its historical key shapes (absent).
- CLI: build/run/cache warm accept speed; usage text names the trade.
Bootstrap's routed executable cache mirrors the posture.
- Early executable cache keys and native-feature validation include speed,
so release, dev, and speed entries never satisfy one another.
- Runtime packs: an optional executable `speed` flavor (-O2, only beside
release). Speed links it when present and the release objects otherwise;
library modes link their release flavor. The sanitizer lane follows the
same flavor key.
- Library profiles accept "speed".
- WASI release-only linker flags apply to speed too.
- bench-runtime: --baseline-optimization/--candidate-optimization and a
per-contender cold_build_ms.
Release executables are byte-identical to the stack tip for all 14
benchmark workloads (macOS arm64, sha256 of executables built from both
trees).
The suite only had single-process CPU workloads. Add the two largest
real-world categories:
- http-api (kind "server"): a node:http JSON API in the static subset
(routing, query parsing, seeded catalog, per-cart state, typed JSON
bodies, ETag/If-None-Match, auth and request-id headers), driven by
clients/http-load.mjs, a raw-socket keep-alive HTTP/1.1 load client
that is identical for every server. 64 connections, 2 client threads,
4,000 untimed warmup and 40,000 measured requests on loopback. The
per-connection request schedule is seeded and server state is
partitioned per connection, so the response digest is deterministic.
Correctness compares server stdout, server stderr (minus the PORT
line), exit status, and client stdout with Node byte-for-byte. The
timed sample is the measured-phase wall time; a second table reports
req/s, p50/p90/p99 latency, client CPU, and peak RSS.
- cli-config (process, launches: 20): a deploy-plan CLI that reads a
~110 KB JSON manifest, validates, interpolates env values, and orders
80 services into dependency waves. Each sample is the median of 20
launches, so startup dominates.
The runner stays backward compatible: process workloads behave as
before, the JSON report keeps its fields and adds kind, launches,
category, server, node_peak_rss_bytes, and geomean_vs_node_by_category,
and the summary prints per-category geomeans (cpu, startup, server).
Node is the reference the project aims to beat. A single cold oracle
launch overstated its time; it is now warmed and sampled in the same
interleaved loop as the compiled executables, and the report adds a
per-workload candidate/Node ratio and its geomean. --no-node skips it.
Fourteen application-shaped workloads (JSON, regex, parsing and class
dispatch, sorting, rendering, collection pipelines, async fan-out,
Map/Set graphs, allocation, numeric kernels, exceptions, CSV, and the
two multi-module build benchmark apps on large inputs).
bench:runtime builds each workload with one or two checkouts, refuses to
time any executable whose stdout, stderr, or exit status differs from
Node, interleaves baseline and candidate launches, and reports medians
with bootstrap 95% confidence intervals, binary size, and peak RSS.