Materializing the checked view of a JavaScript class instance copies its
property bag through scr_dyn_copy_property_descriptors, which built a key
string and a descriptor object per property and defined each through
scr_dyn_define_property (about 2k instructions per property). Plain objects
on both sides now copy each entry directly with the attributes, value or
accessor pair and own-key order the definition gives; properties the target
already has, non-extensible targets and other receivers keep the general
path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A local number[] candidate whose stores are exact integers but not all
proven int32s (a distance array updated with dist[v] = dist[u]! + 1), and
that is only ever allocated by new Array(n).fill(v), now holds SCR_ELEM_I32
slots when n is at least 4096. A shorter fill creates doubles, which fit a
typical L1 data cache. The first store of a value that is not an int32
widens the array to doubles in place (scr_arr_widen_i32).
An innermost loop that accesses such arrays tests their kinds once on
entry (backend/llvm/int32-kind-loops.ts). Its fast copy reads and writes
them as int32 arrays; a store that would widen one leaves for a copy that
re-executes it with kind tests and finishes the loop on doubles. When the
arrays already hold doubles, a copy accesses them as double arrays. Other
accesses test the kind each time.
Only these arrays take kind tests: every other array, and every program
without one, emits the same code as before.
A local number[] that only compiled element operations reach, and whose
every stored element the int32 slot analysis proves an int32 (never -0),
is now stored as SCR_ELEM_I32: 4-byte int32 slots, with state bytes once
it holds a hole or undefined, as for every array. Element reads give
consumers an exact int32, so indexing with an element skips the double
round trip, and the slots take half the memory.
The runtime's dense slot accessors convert between the int32 slot and the
double, so its double-valued entry points serve int32 arrays unchanged;
growth, copies, fill and length changes use the 4-byte width.
ir/int32-arrays.ts proposes the candidates and inlines their element-reader
helper calls; the int32 slot analysis keeps the proven ones, and
selectInt32Arrays rewrites the module for exactly those, so a program
without an int32 array emits the same LLVM IR as before.
When the last reference to an array dropped, the runtime ran the generic
teardown: scr_rc_destroy's depth bookkeeping, an indirect call to the array
destroy, and a loop over the element slots. Arrays of numbers, booleans and
compact ProgramLifetime references release nothing, and neither do arrays of
other ProgramLifetime instances, whose release does nothing.
- scr_arr_release frees an acyclic array without an ext directly when its
elements are numbers, booleans or compact references, or its descriptor
has SCR_ARR_OPS_RELEASE_NOTHING. Every other array keeps the full
teardown.
- The backend sets SCR_ARR_OPS_RELEASE_NOTHING in the descriptor's flags
word on 64-bit targets for ProgramLifetime element types that are not
compact, plain or as the pointer arm of a nullable union, in programs
without lifetime regions. Registration copies it into a table indexed by
the descriptor's registry id before the id is published. Descriptors
without the flag read 0 and keep the full teardown.
Corpus 7401 drops arrays of ProgramLifetime instances, numbers and booleans
through every array operation, with holes and far indices, next to arrays of
ordinary objects, and matches Node.
The cycle collector widens the growth fraction that schedules full passes
up to 16 times the heap left by the last pass while passes find little.
Lowering a large program leaves hundreds of megabytes of cyclic garbage,
and whether a scheduled pass reclaims it before the build's peak depends
on where the growth rule fires: small changes to the compiler moved the
peak of a tsc-ts build between 1.66 and 2.05 GB.
The native compiler now sets the growth cap to 2 at startup, as
SCR_CYCLE_GROWTH_CAP=2 does, so a full pass runs once the heap has grown
to three times its size after the last one. scr_cyc_set_growth_cap sets
the cap for a program from its own code; the environment variable still
takes precedence, and child processes are not affected.
test_dh.c includes scr_dh.c and still called sd_generate_safe_prime
without its new top_two argument, so dh.test.ts failed to compile. It
now passes false, as the DiffieHellman paths do, and a new case checks
generatePrime's search: the sizes and add forms with one possible
result, top-two-bit safe-prime starts, and checks above OpenSSL's round
count.
An array starts without per-slot state bytes. While it has none, every
index below its length is a present value in dense storage (len <= cap,
no sparse entries) and its hole-free prefix bound `filled` equals its
length, so its slots take 8 bytes per element (1 for booleans) instead
of 9 (2). The first hole or present undefined below the length, a length
set past the elements, or a sparse entry gives it states
(scr_arr_add_states), which it keeps; SCR_ARR_STATES in `spare` records
it, and the element ops id moves up one bit.
Runtime readers take scr_arr_dense_state or scr_arr_scan_states, and
writers keep the elements contiguous from 0 or give the array states
before raising its length. A length set on an empty array with inline
slots reserves its slots and states in one allocation.
Emitted reads keep their shape: they load a state only at or past
`filled`, which an array without states never reaches. A store past
`filled` tests the flag and, without states, appends at the length; push
and pop write a state byte only when the array has states. map, fused
map and slice pipelines that keep holes, and concat's element copy
reserve the result's storage, fill it, and set its length afterwards,
so a result without holes is filled by appends and gets no states. The
typed-view cache refresh moves the flag with the storage it swaps, and
tagged-template arrays carry no state bytes.
An array constructed with a known element count of up to 16 (array
literals, copies, slices and the arrays the runtime builds) keeps its
slots in its own block, as empty arrays built for appends already do, so
it costs one allocation and one free.
scr_arr_make_ops is inlined into its two constructors, so the size class
of scr_arr_new_ops_small folds to a table index. It writes the element
ops id with the other header fields and records ops past the registry
in the array's ext after the storage is set up: the ext pointer precedes
the dense allocation, so inline slots must move out before the ext is
created.
A class carries the collector header when any class in its hierarchy can
be on a cycle, so instances of classes whose own fields hold nothing
cycle-capable were buffered as candidates and walked by every level. Such
an object has no traced children and is never on a cycle.
Their descriptors now name scr_cyc_no_children, and they are allocated
with buf set to SCR_CYC_ACYCLIC. That value has the buffered bit set, so
a release that leaves the object alive makes no call; scr_cyc_is_buffered
is false for it, and an emitted death or publication mark takes the object
out of a candidate buffer only when buf is below -1. A pass that walks
such an object as a child restores the value when it survives.
crypto.createDiffieHellman, crypto.getDiffieHellman and
crypto.createDiffieHellmanGroup return native DiffieHellman and
DiffieHellmanGroup handles backed by mbedTLS bignums. generateKeys,
computeSecret, getPrime, getGenerator, getPublicKey, getPrivateKey,
setPublicKey, setPrivateKey and verifyError follow Node 24.22:
- the modp1, modp2, modp5 and modp14 through modp18 groups, with the
private key widths of Node's OpenSSL 3 path (200 to 400 bits for
modp5 and up, [2, p - 2] for modp1 and modp2);
- explicit primes and generators in every argument form and encoding,
with keys of bits(p) - 2 bits, or below min(2^N, q) when the
parameters equal an OpenSSL named group (RFC 3526, RFC 7919,
RFC 5114);
- generated safe primes with OpenSSL's congruences for the generator;
- verifyError's DH_check bits, computed when it is first read;
- Node's argument, generator, modulus, key range, state and encoding
errors, including the OpenSSL errors' library and reason properties.
Safe-prime generation sieves candidates with the odd primes below a
bound that grows with the prime's length and tests them with
Miller-Rabin rounds over a variable-time Montgomery exponentiation that
only reads public values; key and secret exponentiations stay on
mbedTLS's constant-time path. Primes longer than 8192 bits, property
reads other than verifyError, property writes, Object.prototype methods,
and calls whose output encoding is typed any or matches no @types/node
overload are refused.
The runtime unit scr_dh.c links, with the mbedTLS archive, only into
programs that use DiffieHellman. wasm32-wasi builds refuse it. The ECDH
unit's argument and encoding helpers move to scr_crypto_args.h, which
both units include.
An array's element ops are a registry id in `spare` instead of a pointer
field: descriptors get their id on their first array (emitted descriptors
are writable for it), and a program with more element types than the id
holds keeps the rest in the array's out-of-line storage.
The out-of-line storage (sparse indices, noncanonical numeric properties,
match metadata) is no longer a header field. An array that has it keeps
its pointer in the 8 bytes before its dense allocation and sets
SCR_ARR_HAS_EXT; such an array always has a dense allocation of its own
(inline slots move out, an empty array gets the prefix alone), and every
allocation, reallocation and free of the dense storage accounts for the
prefix.
Loops over elements look the ops up once, and the state lookup below the
dense capacity reads no out-of-line storage.
fs.promises.rm(path, options) accepts the { recursive, force, maxRetries,
retryDelay } literal options that rmSync accepts. It runs the same removal
and rejects with its errors.
Removing a directory without recursive now throws Node's ERR_FS_EISDIR
SystemError from rmSync and fs.promises.rm. A retryDelay outside int32 >= 0
or a maxRetries outside uint32 throws ERR_OUT_OF_RANGE. rmSync checks these
options before the path, and fs.promises.rm checks the path first, as Node
does.
Fixes#1615
Co-authored-by: jlucaso1 <55464917+jlucaso1@users.noreply.github.com>
The frontend's syntax trees and program state refer to each other, so
reference counting frees almost none of them when a build disposes the
frontend. Whether a cycle pass reclaimed them before the build ended
depended on where the collector's schedule happened to be. Passes during
the frontend found its data live and widened the growth fractions, so the
next pass waited until the heap had grown well past its size at disposal,
and the dead frontend and the emitter's garbage stayed until the build
ended.
The native compiler now runs scr_collect_cycles_phase right after it
disposes the frontend, through a host function. The runtime entry point
sweeps, then restarts the growth fractions and the death rule's backoff
from the heap the sweep leaves, as after a productive pass. Under Node
the hook is unset.
A comparator that chains key differences with `||`, such as
`(b.score ?? 0) - (a.score ?? 0) || a.id - b.id`, ran through the
comparator sort: TimSort called the closure once per comparison. Each
term orders one number field, ascending or descending, and a term that is
0 or NaN falls through to the next, so the chain orders elements by their
keys with the first deciding. With no key NaN or -0 that order is
consistent, and a stable sort by the keys gives the comparator sort's
permutation.
The recognizer now accepts such chains of up to eight terms over number
fields of records and class instances, and a key may read an optional
number field with a number default (`x.k ?? 0`), also as a single key. The
runtime reads and checks every key of every element before it moves a
slot, then runs stable keyed passes from the last key to the first. A NaN
or -0 key, or a chain with any other term, keeps the comparator sort.
The generic store keeps a native Error's code slot in step with writes
through its checked view, as it does for name, message, stack and cause,
so the store cache leaves `code` to it. The header invariant accepts
scr_js_*.test.c units, which link the dynamic tier.
ScrArr stores len and cap as 32-bit fields and no longer stores the
state pointer. The dense states share the values' allocation and run
backward from its end: the state of slot i is at
data + cap * (slot width + 1) - 1 - i. Removing slots from the front
(shift, splice(0, n)) advances data and lowers cap, which leaves every
remaining state at its address; the head room size, needed only to free
or reallocate the block, is kept in the vacated slot before data. On
64-bit targets the header goes from 64 to 48 bytes, and from 36 to 32
bytes on wasm32. Array lengths were already limited to 2^32 - 1.
The emitted code loads len and cap as i32 and zero-extends them, and
computes state addresses from data and cap. Runtime loops compute the
end of the states once per call. Local array reads load data once for
both the state and value paths. A local or strict element read whose
index is a loop's induction variable reads slots above the hole-free
prefix through the runtime: an inline state check there makes LLVM keep
-1 - i as a second induction variable, which costs a register in the
loop.
Bulk operations do not read back states they just wrote: an append to
an array whose hole-free prefix reaches its length moves the prefix bound
to the new length, a copy from below the source's prefix writes value
states without reading the source's, and releasing a string array skips
the state checks below the prefix. Copies of up to eight slots and of
short state runs use overlapping word moves instead of a call. Emitted
element access computes the state address only on the state-checked
path. Without these, programs that build many small arrays with split or
slice ran 2-4% slower than with the separate state pointer.
Integer loops over an array's length compare the counter with the i32
length field in 32 bits; the counter never exceeds 2^32 - 1 there. The
array-length stability proof accepts Map and Set reads (get, has, size)
and checked union extractions (`x!`), which run no user code, so loops
that look up maps keep the dense fast copy, which tests the hole-free
prefix once before the loop.
Small constructions with the small allocator request 6 slots, so their
blocks stay 112 bytes, now with 7 inline slots (6 after a collector
header). With 96-byte blocks of 5 slots, programs that free and reuse
many small arrays ran 15-20% slower.
RUNTIME_ABI_VERSION goes from 22 to 23.
SHA-384, SHA-512, SHA-512/224 and SHA-512/256 (createHash, createHmac,
crypto.hash, pbkdf2, hkdf) process their blocks with the fastest kernel
the CPU supports:
- x86-64 with AVX2 and BMI2: the message schedule is computed two words
at a time in vector registers, interleaved with the scalar rounds,
which rotate with RORX.
- x86-64 with AVX-512VL: the same, with vector rotates and three-input
XOR in the schedule.
- arm64 with the SHA-512 extension: SHA512H, SHA512H2, SHA512SU0 and
SHA512SU1.
- Otherwise the portable blocks, now unrolled over a 16-word schedule
ring.
The CPU and OS checks run once and are cached. The update and final
paths pass all their whole blocks to the kernel in one call.
test_sha_kernels.c checks the FIPS 180-4 SHA-384 and SHA-512 vectors
through every kernel the CPU supports and compares each kernel with the
portable blocks over every length from 0 to 1100 bytes and 1 MiB, whole
and in 1-, 7-, 64- and 100-byte updates. Corpus 5779 compares SHA-384,
SHA-512 and HMAC-SHA512 digests over many lengths and update sizes with
Node; tests/harness/corpus-costs/ records its plain and sanitized costs.
The backlog threshold of a level backs off by doublings as backlog passes
find little to free, but only passes that had candidates count. A program
whose mature passes have no candidates while its heap grows keeps the base
threshold, a quarter of the live heap, while those same passes widen the
level's growth fraction. When it then reads the structure it built, the
mature buffer fills with live objects and the first backlog pass walks a
quarter of the live heap to find nothing. In tsc-ts's program build on
Sentry, that pass ran inside the loader walk and took 9 to 18 ms.
Until a pass at a level has had candidates, the backlog threshold now uses
the level's growth fraction doublings. The first pass with candidates
judges the level, and the threshold follows its own yield from then on.
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>
Conflicts with #1286's merged form and the death rule (74702ac4b):
- scr_cycle.c: keep the death rule's per-thread fields and drop the
running buffered total this branch removed (the slow path sums the
buffers); scheduling decisions route bypassed appended candidates and
then note the live count's fall.
- test_cycle.c: keep both new checks (inline buffering, death brings a
full pass).
- The self-capture corpus program moves from 5990 to 5985 (5990 is now
5990-worker-uncloneable.mjs), with its order baseline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main now has #1286 and later collector changes. Resolved in scr_cycle.c:
the scheduled collection point collects the nursery, then notes the
live-count fall (death rule). Semantic fixes for the new main code:
a full pass's markGray no longer overwrites a young object's `buf` (its
nursery index) with the component number, and a nursery collection
refreshes the death rule's live reading at its end so the young garbage
it frees does not count as deaths outside passes. Formats the runtime
pack matrix test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every release that left a headered object alive and unbuffered called
scr_cyc_on_release, which looked up the type's bypass statistics, pushed
the object, kept a running total and compared it with two triggers. The
nursery case now has a fast path that appends the object to the nursery
buffer itself, while the runtime-maintained window says the append can
neither make a collection due nor grow the buffer, and the live count is
below the allocation trigger:
- scr_rc.h exports the candidate buffers (scr_cyc_buffers: the three
buffers, the window and the allocation trigger) where the collector
state is ordinary globals, and scr_cyc_append. The runtime's own
release paths (scr_cyc_released) append inline, and scr_cyc_on_release
tries the append first.
- Optimized emitted code buffers through one sc_cyc_append helper per
module, called with preserve_mostcc, so release fast paths keep the
size of the plain call they had (inlining the append into every
release fast path grew the self-hosted compiler's text by 4.6%) and
their callers keep their registers. Its runtime call goes through a
second preserve_mostcc function, which keeps the register saves off the
append path.
- scr_cycle.c keeps the window: recomputed where buffers, capacity or the
trigger change in bulk, lowered by one on older buffering and nursery
deaths, 0 during passes. The running total is gone; the slow path sums
the buffers.
- An appended candidate whose type bypasses the nursery would have been
buffered past it. scr_cyc_route_bypassed moves such candidates before
any scheduling decision or pass reads the buffers, consuming the bypass
counts as scr_cyc_on_release does (skipped while no type bypasses).
test_cycle checks the window (the release that reaches the trigger still
collects) and the routing of inline-buffered candidates of a bypassed
type; its mature-bypass probe routes before reading generations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SCRIPTC_YOUNG_NURSERY=1 at compile time makes an executable allocate
traced, program-owned class instances and records young and link the
collector unit built with SCR_YIN (a runtime pack variant of
scr_cycle.c; libraries never get it). The emitted constructor lists the
object in a per-thread nursery and marks its header: generation
SCR_CYC_YOUNG and its nursery index under the buffered bit, so every
existing release path treats it as a buffered candidate (no call while it
stays alive; scr_cyc_on_dead takes it out of the nursery at its death).
A nursery collection (whenever 4096 young objects are live, at collection
points, before full sweeps and limited passes) counts each young object's
references from other young objects, marks from the objects whose count is
below their reference count (referenced from outside the nursery: stacks,
globals, older objects, runtime structures, FFI, fibers, coroutine
frames), frees what stays unmarked, and gives up the dead set's outgoing
edges after the teardowns. The counts are recomputed at every collection
instead of maintained by a store barrier, so soundness rests only on the
invariants trial deletion already needs. Survivors join trial deletion's
nursery and outside roots become candidates; trial deletion walks young
objects it reaches as nursery-level objects. Per-type admission statistics
send types that do not die in young cycles back to trial deletion for a
while. Publication takes young objects out of the nursery. The RC audit
build checks nursery membership and counts at every collection.
With the flag off, the compiler emits and links exactly what it did
before (249 programs and the self-hosted compiler compared
byte-identical). Measured with the flag on: cg-rings -38%, cg-pairs -27%,
the runtime suite geomean -0.3% (published-graphs +3.4%), the self-hosted
compiler neutral; shapes whose cycles go through arrays or closures
(phase 2) pay without gaining.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Full passes are scheduled by heap growth, and the fraction they wait for
widens while growth-triggered passes find nothing, up to 16 times the heap
after the last full pass. That keeps a large live heap that keeps growing
(a compiler checking a large program) from being walked again and again. But
growth alone cannot tell such a heap from one that keeps garbage: a program
that drops a large structure and builds another, as a compiler rebuilding in
watch mode does, holds the dead structures' cyclic parts until the heap has
grown to 17 times its size. Ten rebuilds of benchmarks/regions/compiler.ts
without lifetime regions reached 1.3 GB that way.
The acyclic parts of such a structure go by reference counting, and that
shows: the live count falls between passes. The collector now sums those
falls since the last full pass. Once they reach 1/32 of the heap that pass
left and have stopped (a structure torn down by reference counting falls
over several checks, and a pass in the middle would find its rest still
referenced), a full pass is due as soon as the heap has grown the base
quarter past it, whatever the growth fraction. One that finds fewer older
objects than had died doubles the fall the next one needs, up to 256 times,
and a productive one restores it.
A full pass that the growth rule did not choose (this one, a backlog pass, a
scheduled-age pass) now raises the growth baselines of the levels it walked
but does not lower them. After such a pass reclaims a dropped structure, the
heap regrowing to where it was is the program rebuilding what it had. From
the low point, growth passes at both levels would walk the rebuilt live heap
and find nothing. On the ten rebuilds that was a third of the collector's
added time. A heap that only grows never falls, so its schedule is
unchanged. The checks run where the release path and the event loop already
decide which pass to run.
shift() moved every remaining element down one slot, making queue drains
quadratic (100k elements: 0.85 s; 1M: no result in 30 s). Dense arrays of
8-byte slots with their own allocation now remove front elements by
advancing data/present and lowering cap; the vacated slots stay in the
allocation as head room. A flag in `spare` marks it and its size is
implied by the layout (present == data + cap + head), so every reader,
including inline element access in emitted code, is unchanged. Growth
moves the elements back to the start of the allocation (in place only
when that leaves half the moved count free, else geometrically), a
drained array starts over at the front, unshift reuses head room, and an
array unshift already had to move once reserves head room for half its
length. scr_arr_outline drops head room before compiled storage swaps.
Booleans, inline slots, sparse arrays and static arrays keep the old
behavior.
Scaling probes: shift/BFS/splice(0,1)/deque drains and unshift runs go
from exponent ~2.0 to ~1.0 (shift 1e5 847 ms -> 1.6 ms); runtime bench
geomean 1.0045 (neutral).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slice read only by lending consumers (string equality and ordering,
length/charCodeAt/indexOf/includes/startsWith/endsWith, Number/unary
plus/parseInt/parseFloat, typed string-keyed Map/Set get/has/set/add/
delete keys, string switch discriminants, and string parameters their
callee only lends) is written by scr_str_slice_scratch into a per-site
stack buffer as an immortal ASCII ScrStr marked as scratch (cap < len),
instead of a heap allocation. Stable locals initialized by such a slice
qualify when every read lends. Map/Set insertion stores a heap copy of a
scratch key, and probes cache the key hash in the scratch header, so the
counting idiom allocates only for new keys. Longer, non-ASCII, and
interned results keep the owned slice; coroutines and debug builds are
unchanged. Slice bounds are normalized without a libm trunc call.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fs.cpSync, fs.cp and fs/promises cp compile with every option:
dereference, errorOnExist, filter, force, mode, preserveTimestamps,
recursive and verbatimSymlinks. They follow Node's two implementations.
cpSync follows cp-sync.js over the fs binding: copy errors are plain
Errors with a code, std::error_code failures throw an ErrnoException,
and links inside a copied directory point at their canonical targets.
fs.cp and fs/promises cp follow cp.js, whose copy errors are
SystemErrors with code, info, errno, syscall and path.
Options and paths validate in Node's order with Node's errors. filter
runs for the top-level paths and each entry in Node's order; an async
filter must return a promise that is already settled, and a pending one
throws SC2020. The callback and promise forms complete the copy before
the callback runs or the promise settles. Windows builds throw SC2020.
The copy lives in the new scr_fs_cp.c. Directory walks keep their frames
on the heap, so deep trees also copy on a fiber's stack. File contents
copy with sendfile on Linux, and COPYFILE_FICLONE uses FICLONE
where it exists. ScrError gains the SystemError context and the
ErrnoException key order, with matching fields in the compiler's Error
layout. The stored fs.cp callback uses the same implementation.
Corpus 5567-5571 cover the sync, callback and promise forms, errors,
symlinks and 1500-level trees.
On x86-64 CPUs with the SHA extensions (and SSSE3 and SSE4.1), SHA-1 and
SHA-256 process their blocks with SHA1RNDS4/SHA256RNDS2 and the
message-schedule instructions, several blocks per call. The CPU check
runs once and is cached; other CPUs, other architectures and wasm32 keep
the portable blocks, and both give the same digests. createHash,
crypto.hash, createHmac, pbkdf2, hkdf and every other caller of the two
digests use the new path.
sha-kernels.test.ts runs both block implementations over known vectors
and every length from 0 to 1100 bytes and 1 MiB, whole and in 1-, 7-,
64- and 100-byte updates. Corpus 5763 compares the digests, HMAC, PBKDF2
and Hash.copy() with Node across block-boundary lengths and update
sizes.