Skip to content

[JSC] AOT: what a field is expected to hold, long functions, calls into text made at run time - #906

Merged
Jarred-Sumner merged 512 commits into
mainfrom
claude/compile-aot
Oct 26, 2026
Merged

Jarred-Sumner merged 512 commits into
mainfrom
claude/compile-aot

Conversation

@Jarred-Sumner

@Jarred-Sumner Jarred-Sumner commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Follows #757 and #903. Everything here is under ENABLE(AOT) or aot/, except the B3, Air and interpreter changes listed below.

Behavior

  • With --trim, a built-in function that does not list all its members counts as used once it is read.
  • Objects filled by key: the table that remembers first additions has sets of eight places and goes by the name's own hash, so what it remembers does not depend on where strings are allocated.
  • An append of several parts is rolled back when one of them throws and the handler goes on with the builder.
  • A walk of frames that crosses an entry asks whether the frame in front of it belongs to compiled code.

Speed

  • A field is expected to hold what the program stores in it. Fields filled with one value share the names asked of them. A field that the allocating code fills is read with one test.
  • A method of a helper object that defines members under written names gets a copy per call.
  • A test that comes out the same in every round of a loop moves in front of the loop once nothing else in the loop asks for a test.
  • A read by name at a site reads an own inline value at the place the site names, on x86-64 and ARM64.
  • Compiled code enters a function that has bytecode without the trampolines. op_ret has a form without the count of returns.
  • A string that a handler reads stays in its builder. A string that grows around a loop gets a builder only if it stays in that builder all the way around.

Build time

  • No generic copy of a function where it would take over 16 parameters per value of the function.
  • A location with more reads than the limit is solved in parts, with the same result.
  • A very long literal of computed numbers is appended to an empty array of its own form.
  • Liveness: past a number of visited blocks, the sets at the heads of blocks share the parts they have in common.
  • Copies of a helper object's method are made three calls deep.

Shared with the JIT

  • B3: path constants are looked up in a tree over the decided comparisons past a limit, with the same result; redundant phi cycles are swept only where something changed; the next interfering value of a block is computed once.
  • Air: liveness in slices names the blocks that hold an index of the slice; a move of a spill slot to itself is dropped.
  • The interpreter's prologue zeroes the frame 32 bytes a turn on x86-64.
  • CallFrame::callerFrameUnlessCompiled() is the step that VM::topJSCallFrame() and the debugger's walkers take.

Testing

Sixteen new IR tests under aot/tests, new cases in testair and testb3, new native tests.

…gisters

A function that only returns object literals with the same properties returns the
values in registers when every caller only reads them, and the object is not made.
For a literal with a typed layout that skipped what its stores do: a value of another
type was accepted, and an integer stayed an integer where the field holds a double.

It also made the build depend on timing. While the callee returns in registers a read
has the type of the value. Once another caller needs the object, the same read has the
type of the field, which may be narrower, so what the reader published depended on
whether it was analyzed before or after that.

The callee now passes each value through the conversion that scalar replacement uses,
before the return, and records the converted type, which is contained in the type of
the field. The check is at the return, so such a literal is made if its stores and the
return are not covered by the same exception handler: a catch block around the literal
would not see the refusal. With auditAOTTypedFields such literals are made.
A Switch in a procedure that must not contain absolute addresses was always
lowered to a binary search. Dense switches now use a table of offsets that
lives in the code and is addressed relative to the program counter (adr on
ARM64, lea with rip on x86-64).

Loads that are declared immutable are also shared across blocks by common
subexpression elimination, within a bound on the number of blocks searched.
… fast paths

Every area of the compiler was read side by side with what DFG, FTL, the
baseline JIT and the bytecode generator do for the same operation. Each
difference came with a program; the ones that behaved differently from the
interpreter are fixed here, each with a regression test that failed before.

Memory safety and crashes:
- A function escapes through instanceof, through accessors reached from its
  properties and through prototype.constructor. Such a function kept unboxed,
  unchecked parameters, or no code at all.
- Stubs check the stack before they call an accessor.
- Array views of loops that are not split are reloaded after the slow path of
  an element read, which can run program code.
- The arguments of a direct eval call count toward the scratch area, and the
  scratch accessor asserts its index.
- The guard of an inlined call tests for a function unless functions are the
  only cells the variable can hold.
- A write barrier follows every cached transition, whatever is stored. A
  barrier is shared by a run of stores only if nothing in between can collect.
- A value re-enters the fast copy of a loop only if a test proves its type.

Types:
- Scopes of functions that have call and construct code are not summarized.
- A RegExp literal argument is trusted only if the call is its only user.
- Ranges of typed array elements and lengths follow the inferred type.
- op_add is monotone.

Semantics:
- Property runs end at a read of a variable that a setter could change.
- Stores in a try block are not absorbed into create_this.
- A closure is sunk into a fallback call only if that call cannot run twice.
- Spread copies only arrays without a sparse map.
- object == symbol converts the object.
- endsWith takes the slow path for strings whose length the stub saturates.
- The instanceof cache requires a read-only Symbol.hasInstance.
- Dictionaries are not copied from remembered offsets. Direct puts skip the
  megamorphic store cache. Guarded stores respect typed fields, box Int32 for
  Int32 arrays, and typed stores respect frozen objects.
- The body of a function that nothing calls is the right trap on x86-64.

Optimizations:
- Truthiness by type for objects, Int32 or nullish, number or undefined and
  strings. Relational comparison with an Int32 inline. Strict equality by kind.
- One stack check per chain of direct calls with small frames.
- Host and internal functions in the callee cache; host functions known at
  build time are called directly.
- Promise and async helpers, Map and Set operations, size, number to string,
  templates, inherited setters, private field definitions and brands, and
  spread of ordinary arrays are handled in stubs.
- Result types of built-in methods, nullish receivers, narrowing after
  check_tdz and on falsy edges, Int32-preserving Math functions, create_this.
- No barrier for a new environment, barrier only for a cell, constant string
  keys become property names, paths that lead to a throw are cold.
- Object literals merged by a phi are replaced by their values.
- Remarks say why an allocation escapes.
duplicateTails was skipped for every position-independent procedure. A value
that is computed on several paths and then tested, as the inline forms of
truthiness, comparison and equality are, went through a Phi and was tested a
second time. The phase now runs for them too and leaves alone tails that
contain a Patchpoint or a CCall, which carry state of their site.

The TypeScript compiler checking a large project executes 1.8% fewer
instructions, 1.1% fewer with its types; its image grows by 0.35%.
The Narrow of the counter in the pre-header of a split loop kept the range of
the first entry, 0..0 for a loop that starts at 0, because only the phis of
the pre-header were recomputed once the re-entry edge existed. The re-entry
check passed and the next block left the fast copy again. As the first
iteration is slow whenever caches are cold, such a loop ran all its iterations
in the generic copy on the first call of its function, and after any slow
iteration later.
Checkpoint of the fixes for the remaining findings of the comparison with DFG
and FTL, and for what a second round over the previous batch found. Each fix
has a regression test that fails before it.

Memory safety and crashes:
- A typed constructor with a foreign new.target wrote outside the object; the
  operations allocate the layout the site expects.
- Calls of a Proxy or of something not callable, and direct eval, at the top
  level of a module.
- The call of the ToBoolean stub is control dependent, so that it is not
  hoisted above a TDZ check.
- A property cache for a module namespace object is filled only for the
  namespace object itself.
- A Presence condition is permanent only if the offset cannot move.
- Caches validated by the megamorphic epoch are cleared when it wraps.
- guardResolveScope counts dissolved scopes on the way to an importing module.
- A guard hoisted out of a loop of an inlined function belongs to that frame.
- A parameter hidden by a function declaration, and a string literal with NUL
  characters, no longer stop the compiler.
- x18 is pinned on ARM64. The image stamp depends on the features of the build
  target, not on what the CPU reports.

Semantics:
- The receiver check of a lowered built-in is repeated at the call unless
  nothing in between has effects. The list of overridden methods, its option
  and the unchecked calls that rested on it are gone.
- Array callbacks treat a hole as absent only for an original array. map and
  filter with an unused result skip species only for an original array.
- The length of an array without an indexing header.
- Substrings and literals whose characters are 16-bit at run time.
- Stores to module variables invalidate reads of namespace objects.
- defineProperty on a typed field throws.
- Objects whose static properties are not reified are not copied as empty.
- switch on strings with very long labels.
- Names of module code and function objects of compiled frames in stack traces.
- create_this stays ahead of the type checks that follow it. op_type_tag in
  every tier.

Optimizations and diagnostics:
- WeakMap and WeakSet lookups, Array includes and indexOf, toLowerCase and
  toUpperCase in stubs.
- A result of a built-in method that is only tested stays a boolean.
- Constant keys through to_property_key. Result types for receivers that are
  allocations of the same function.
- Remarks say why a function escapes and which built-ins classes extend.
- Barrier tests run with generational collection.
- type-coverage.py knows which operations have a fast path in a stub.
In code that runs a few times, has no loops and lives across collections, several
fast paths quietly stopped being taken and the slow path became the usual one.

Caches:
- An attempt now counts a change of a slot, not an arrival in the operation. Transient
  reasons (an empty free list, a structure that died) cost nothing, permanent ones give
  up at once, and giving up lasts until the next full collection.
- Planned construction took the long path for ever after a collection without a live
  instance. It now has a short path for a valid cache.
- Sampling of which function gets its own data was periodic, so a function could starve
  for ever in a loop with an even number of operations. It is aperiodic now.
- A store in a function that started without data never got its own slot, because the
  megamorphic probe hit first. PutById follows GetById's policy.
- A miss of a callee cache in the shared data counts for the calling function (ARM64).
- Construction entries of the megamorphic cache survive collections while their
  structures are alive.
- A property run no longer remembers "no target" because a prototype was a dictionary.

Allocation and arrays:
- An empty array took its storage from another size class than the C++ path, which
  therefore never refilled the free list that the helper needs.
- `new Array(n)` and `Array(n)` use a helper.
- Element stores to Int32 and Double arrays, the end of an array iteration, and steps of
  Set, Map, string and array iterators stay in stubs.
- The first growth of out-of-line storage is done in the stub.

Collector:
- An eden collection visits only what was added to the tables of an instance.
- The first object of a long shape becomes a dictionary instead of leaving a chain of
  structures that only it needs.
- Write barrier stubs fence and look at the cell state again before they call C++.
- With verifyGC, typed fields are checked against their types.

Calls:
- Pointer to a function's data in 2 instructions, shorter stack check thunks and
  to_this, no scratch words for calls whose arguments stay in registers.
- Functions that are only called directly and hold no captures have no function object.
- A function compiled for direct calls only has a public entry that throws.
- Jobs enter a compiled function directly.

Compiler:
- Branches on known values and on types are folded, and dead blocks removed.
- Stores to frame registers that are overwritten in their block are removed, so that a
  callee in a temporary register no longer escapes. Function numbers have 20 bits.
- Scopes of a function's call and construct code share one identity.
- Concatenation of operands that are not proven to be strings, comparisons with long
  literals, and slice on an unknown receiver stay out of C++.

Fixes:
- Nested spread calls through iterators trapped.
- The result of map and filter was typed as an array for a receiver with its own
  constructor.
- The build trapped on a closure inlined where it is made that reads a capture of its
  maker.
- Analysis and compilation disagreed on a receiver proof after inlining.
- A type check that follows a planned store ends the plan.
- Own properties named like a typed field are refused on prototypes of typed objects.
- Positions of stores in a property run.

useAOTOperationCounters counts the calls of each operation that reach C++, by the state
of the slot they were given. aotOperationCount() in the shell reads them, so that tests
can assert that a loop stays in compiled code.
The image replaces the code of functions that nothing creates or calls by a breakpoint.
What a function creates was noted by the code blocks of the created executables, but the
executables that construct code creates are copies without code blocks of their own, so
nothing was noted for them. That did not matter while the call code created the same
functions. Since branches on new.target are folded, it may not.

Resolve the executable to the function that is compiled first.
…layouts and call targets

Soundness:
- A call of a closed method verifies its callee instead of trusting the declared type of the receiver.
- Structure::setKnownShape no longer overwrites a typed layout ID, which lost stores.
- An object that was not made by the constructor of a class is not converted into an instance of it.
- A store or a delete that a typed field refuses throws in sloppy code too. Object.assign and spread no longer drop the value.
- The PutById stub refuses a plain object for a field whose mask names a kind.
- An export of a compiled module cannot be overridden through its namespace object.
- Air emits the trap of an Oops that does not follow a Patch in position-independent code.
- Returned object literals are planned after the calls they hold, which trapped the compiler.
- toInt32ForBitOp converts a double behind a branch, so that x86-64 makes no call for int32 inputs.

Performance:
- Keyed accesses whose base is known to be a typed array are lowered inline outside of split loops.
- The PutById stub accepts the functions and other objects that the mask of a typed field allows, instead of missing every time.
- TypedLayoutTable::findField searches a contiguous array of hash prefixes.
- The first instance that a constructor makes shares the Structure of the later ones.
- The check of a typed layout is skipped where the value is known to have the layout if it is a cell.
- An object literal that a function returns is built by the callers that keep it.
- The megamorphic cache survives full collections.

A function that escapes can have a second body for its known callers. This is switched off until exact call sites are checked against the summary of that body.

For experiments and measurements only: files of facts for layouts and for call targets, a dump of the facts of the analysis, counts of operations by site, a census of allocated bytes by origin and by stack, and a census of code made at run time.
…the first call

- On ARM64 every trailing brk was trimmed from a function. A trap in the last block vanished, and a check that failed there ran into the next function. Now only the breakpoint that the compiler appends itself is dropped, and one more only if the last Air block ends in a Patch followed by an Oops.
- With type coverage counters no function starts on shared data, so the callees of a cached call are noted from its first execution. Before, the first activations of nearly every function were not seen, and a site could look as if it had one callee.
- Validation can check the inferred types of phis, which are appended to their block without going through setResult. This is behind validateAOTTypesOfPhis until the tests have run with it.
- Keyed accesses to known typed arrays keep the stub in blocks that are generic or rarely executed.
- The census of allocated bytes is rewritten after every megabyte charged and lists live bytes.
- aot-loop-guards.js is split: the sections that overwrite intrinsics state what happens with mutable and with immutable intrinsics.
…fields inline

Soundness:
- A literal whose type has a layout is checked against its field types when it is made. The path for a literal that also has a property that is no field returned before that check, and the stores that fill it let undefined through, as a class field definition needs. A typed read then took undefined for a number, or for an object, which crashed.

Performance:
- A store through a type whose layout has field IDs is lowered to a test of the field ID, the checks of the field and a store. Before, such a store in a function that is not compact called the runtime every time, because the inline cache sent every slot with a field type to the slow case.
- A typed literal that has other names is filled by one call instead of one call for each property.
- A run of stores on an object that has more than 128 properties, or that the first-object rule would convert, is stored one by one, so that the object becomes a dictionary. Before, every run cloned the whole property table, and all the tables stayed alive.

Diagnostics:
- A refused store names the class.
- With type coverage counters, the fast copy of a split loop counts its exits by the guard that failed.
- operationAOTGetById is counted by site, as operationAOTPutById is.
- The facts dump lists modules, exports, requests, namespace imports, functions declared at the top of a module, and the cell type of op_is_cell_with_type.
- aotFunctionWithWrongTypesForTesting makes the verifier of inferred types fail in one function, so that a test can see it fire.
…s, add a Div stub

- Loop splitting marked a call in a loop as guarded because the parser can splice the callee in. The parser always declined for a module function: it runs before the phis are filled, so the scope register in a loop has no identity yet and the callee only counts as likely. The call kept a guard that emits nothing, and the inliner refuses every call that has a guard. Now the guard is not attached when the callee is not spliced in, and the inliner takes the call.
- The plan that stores the properties of a new object together with its allocation ended at the first type check behind a store. With checked parameters a constructor then paid two cached stores and two write barriers on the object it had just allocated. A check now ends the plan only if it checks this, has another exception handler, or the plan already holds an assignment to a property that it has not defined, which a setter could observe. Definitions, and assignments to what the plan defined, cannot be observed when the check throws.
- Division of operands of unknown type called the runtime. It now has a stub with the double path of Add, Sub and Mul.
A pattern built at run time can repeat the same large character class thousands of times. Each
occurrence had its own CharacterClass: four vectors and a Latin-1 table, which BytecodePattern
keeps for the life of the RegExp.
From the 17th class of a pattern on, a class whose matches, ranges and flags equal those of an
earlier one reuses it. Terms already share pointers to built-in classes, a class is never written
after charClass(), and inversion lives in the term. Patterns with fewer classes pay one comparison.
Classes with strings are left alone.
The processor predicts a RET from the addresses that BL and BLR left behind. Three stubs on ARM64 broke that pairing, so their own
return was mispredicted and so was every return to a frame that was already live.

- A call of a function that takes its arguments as a list put the address of the adapter in x30 with ADR and jumped to the callee.
  It now jumps to a BLR placed right ahead of the adapter, as x86-64 always did with its CALL. This is all of Call, Construct,
  Call0..8, Construct0..8 and CallCached0..8.
- CallVarargs did the same ahead of CallList. It now passes the address of CallList to that BLR.
- A cold operation of a function without a frame returned with BR. It now returns with RET through the same register.

What hangs on the return address being the adapter's label, and how each was checked:
- The value is the same: the label is still taken right behind the instruction that sets x30, and every comparison loads it from the
  same label (read: the six uses of returnFromCallWithList are all in AOTStubs.cpp; nothing outside names the stub).
- The test for a tail call out of a callee that returns to the adapter, in the call stubs and in callListInTailPosition: unchanged
  code. 300000 tail calls in a row through each (cached call, nine parameters, spread) stay within the stack.
- Stack walking and unwinding tell an address in the stubs by range (classifyAddress), not by the start of a stub, so the label
  moving by one instruction changes nothing. The offsets that are compared exactly, returnsIntoAdapters, come from labels.
  Frames are compared by name for every call form, and a throw out of the callee is caught in the caller for every call form.
- locationForReturnAddress is only asked about addresses in functions. The return address into the caller is the one its BL left.
- The jump into the middle of CallVarargs from a call in tail position lands on the first of the two new instructions.

A tail call out of a callee that returns to the adapter still leaves one stale entry, calls in tail position with a list are still
entered with BL and never return, and a throw still leaves with BR. These are rare or cannot be paired.

Tests: aot-calls-of-functions-that-take-lists.js (rest parameters, arguments, nine parameters and a function that takes registers; as a
known call, a cached call, a construct, a tail call, a spread, apply; values, throws, frames, recursion to a stack overflow) passes
before and after. check-paired-returns.py reads the machine code of the stubs and fails before.
…rs, allocate small literals from registers

Soundness:
- A declared array type no longer narrows a value. TypeScript gives `t[i]` the element's type whatever `i` is, so a
  program without a cast could reach the inline array paths with undefined and crash. The tag was lowered to an alias
  with no test. Where inference proves an array nothing changes; elsewhere length, push and element accesses take the
  ordinary path, which tests and has a generic arm.

Types:
- The result of a layout tag replaces its operand in registers that an exception handler reads as well. Such a register
  used to keep the bare argument, so every read tested the layout again and callees got an untyped argument. A handler
  reads through the frame, which is typed by what was stored, so its view stays wide.
- Array.isArray, Number.isInteger, isSafeInteger, isFinite and isNaN are tests of their argument for type inference: a
  branch decided by the type is folded, the dead arm is neither analysed nor compiled, and both arms are narrowed.
- The function behind a frozen path has an exact type, which variables, parameters and imports carry. A call through
  such an alias is lowered like a call by path, behind a comparison with the intrinsic unless the type is exact.
- The fast path of a guarded built-in and its cold call merge in the node's own representation instead of boxing.
- hasOwnProperty.call(object, key) on the object and key of an enclosing for-in is answered from the enumerator.

Code:
- Inline fast paths meant for loops are emitted in a function that calls itself only if it has at most 512 bytes of
  bytecode. A likely callee no longer counts as a self call, and inlining a recursive body no longer marks the caller.
- The write barrier's test is inline; the stub is called from a cold block. The threshold is loaded afresh each time.
- Object literals of up to 6 values and array literals of up to 4 are allocated by stubs that take the values in
  argument registers, without the scratch area and the copy loop.
- Two strings are joined in the stub for addition.
- The slow path of a type check is laid out of line. Stubs preserve registers in pairs on ARM64.
- The registers that a stub changes come from one table, which the lowering uses and the map prints.
  check-stub-clobbers.py reads the machine code of the stubs and fails if one writes a register outside its set.
- Every refusal to inline leaves a remark with the callee's name.
…m, inline callees of up to 32 bytes

Built-ins:
- Under immutable intrinsics, what an existing built-in name resolves to cannot be changed. Beside writing, redefining
  or deleting an existing property, which was refused already, a built-in object can no longer be given an own property
  under a name that it inherits from a built-in prototype, and its prototype cannot be changed. New names may be added:
  the objects stay extensible. Objects and classes of the program shadow built-in names as before.
- So the read of call, apply or bind on a function whose type is an intrinsic folds to the member of
  Function.prototype, the comparison behind it folds, and the generic call leaves the graph:
  Math.floor.call(null, x) is Math.floor(x).
- The array and non-cell edges of Array.isArray end in a jump, so that B3 threads them for an alias as well.

Calls:
- The limit for inlining a callee outside loops is 32 bytes of bytecode instead of 18. A sweep over a large compiler
  written in TypeScript gave -1.5% instructions for +1.5% of code at 32; 24 gave -0.4% for +0.4%, 40 gave -1.9% for
  +4.1%, 60 gave -3.0% for +8.5%, and raising the limit inside loops alone gave nothing.
- A block that is reachable only through a catch entry is rarely executed.
- A parameter whose type excludes undefined is passed by every caller, so it is read from an argument list without
  the test of the count.
- The VM and the global object are loaded where they are used, not at the entry of every function that may need them.
run-tests.py joined the options of all `//@ runDefault(...)` lines of a header into one list, so a test with several run
lines ran once, with later options overriding earlier ones, which could switch off what the test is about. It also read
only the first six lines of a header. Now each run line is a run of its own and the whole header is read. The runs of
one test follow each other, since they may write the same files; different tests run beside each other (--jobs).

aot-element-stores-by-array-shape.js failed in three runs with this, as it had before, unseen. Its expectation was wrong: a literal of
four slots with a hole is born with a vector of three, so its last store grows it, once a literal. It now says so, and asks that
literals of three slots make no call at all.

check-stub-clobbers.py --print counted visits of a call instruction instead of call instructions. The check itself never
used that count.
…push inline, stop batching stores that add nothing

Soundness:
- The generic call behind a lowered built-in was never a tail call. A method of a plain object that is named like a
  built-in and calls itself or another one in tail position overflowed the stack: 41 of 58 generated forms, all that
  are lowered. The fallback is a tail call again where the call was one. Nothing is lowered less.

Code:
- A constant cell of the program is read from a slot of the function's data: a load and a test where code may be bulky,
  a stub of three instructions on the usual path elsewhere. It was a hash lookup of 16 instructions at every use. The
  hash stays the owner and the root. In a large program with 383,045 uses in 64,703 functions, 300,377 distinct pairs
  of function and constant, this is 1.2% more code.
- Array.prototype.push of one value is inline behind the check of the receiver that the site makes anyway, except in
  tail position. 39 instructions through the thunk before, about 13 to 25 now. 0.6% more code in the same program.
- A run of stores to one object that once added nothing goes by single stores from then on. The run is meant for births;
  in that program 97.6% of its arrivals in the runtime stored the properties one by one, each with a lookup. Runs that
  add properties still make one structure. Arrivals: -87%.
- String.prototype.indexOf and includes with a needle of one character scan up to 32 Latin-1 characters inline.
- The read of a method on a value whose type is a primitive folds to the member of the built-in prototype.
- The remainder of two numbers that are integers is computed inline.
- Object.hasOwn probes the VM's cache of own properties, as the native function does: 148 instructions a call to 94.
- Built-in functions are marked immutable without reifying their lazy name and length: 888 bytes of live heap at launch
  instead of 42,538, and 0.6 M instructions fewer.

Diagnostics:
- A heap snapshot has an edge for each scope slot without a name and each captured value. Before, whatever hung on them
  had no path from a root. In a snapshot for GC debugging a compiled function is labelled with its number in the image.
- More named counts behind useAOTOperationCounters: fills of the megamorphic cache by kind, slots cleared by
  watchpoints, why an object cannot take a typed layout, how a typed read that reaches the runtime ends, how a copy of
  data properties ends, compilations of regular expressions.
…e of what reaches the runtime

- An operation that could cache its outcome filled the megamorphic cache whether or not the pair was there. Filling
  moves the occupant of the primary entry to the secondary table first, so filling a pair that is the occupant put a
  second copy of it there and threw another pair out. The JIT tiers have the same code but fill only after a miss.
  Now the cache is asked first (MegamorphicCache::findStore beside findLoad). With 900 pairs in use, filling 300
  present pairs again made the other stores miss 1.8 to 3.8 times as often; now 0.98 to 1.04 times.
- Behind useAOTOperationCounters: whether a fill found its pair, real fills as new pairs or pairs seen before, with
  the pairs of dead structures forgotten at each collection, so that "seen before" means thrown out; distinct pairs
  by kind; whether a store that arrives with an abandoned slot has its pair in the cache; typed reads that reach the
  runtime by site, with the layout wanted and the one found; regular expressions that are compiled without code in
  the image, by who compiled them and by the start of their pattern.
…t was not monotone

- The read of call, apply or bind was typed as exactly the member of Function.prototype whenever the base was a
  built-in function or anything that is no object. The helper it asked was made for the callee of a call, where a
  primitive throws; a read from a primitive does not. So with `g` a number or Math.floor, `g.call ? a : b` was
  folded to `a`. The rule now applies only if all that is left of the base's type, without null and undefined, is a
  function.
- A built-in value was taken to be truthy. Built-in strings are such values too, and Function.prototype.name is the
  empty string: `Function.prototype.name ? a : b` was folded to `a`. The rule now asks for an object. Checking
  inferred types could not see this: the type was right, the branch was not.
- The same read gave every type for a base that is only null or undefined and one function for a base that may also
  be a built-in function or a primitive, so the type of a larger base was smaller. Types are joined, so nothing was
  wrong at run time, but what a reader of a variable saw depended on whether another thread had inferred the writer
  yet: a fold was made or not by chance, in about 2% of the compilations of one test program. A base that is only
  null or undefined now gives no type: the read throws.
Copies of data properties into objects with a typed layout. The fast path of a
spread or a rest refused such a target, so a literal with a spread whose type
has a layout was filled by the generic function. The plan of a copy takes the
transitions that ordinary stores take, so it had the right structure and
offsets already. It now also records the field each value lands in. A value is
checked and represented as a store would. Every check comes before the first
write, and a rejected value leaves the copy to the generic function, which
throws as before. A clone born in another layout than its source's uses the
same plan before it goes property by property.

A spread of a boolean, a number, a symbol or a bigint copies nothing.
`{ a, ...(condition && object) }` spreads false when the condition fails, and
the fast path left that to the generic function. A string has indices and
stays as it was.

Direct stores are served from the megamorphic cache. The PutById stub left for
C++ on a direct store before it probed the cache, so the definitions in
`{ ...e, a: x, b: y }` and class fields arrived in C++ each time once their
site had seen too many structures: 23,700 of 24,000 in a test with 40
structures. An entry for a transition holds for a definition whatever the
prototypes have. An entry for a replacement holds if no property of the
structure is non-enumerable or non-configurable. A direct store that replaces
now fills the cache, and an entry whose transition grows the out-of-line
storage is applied at the start of the operation.

Validated mode checks the branches that types folded. A wrong fold with a
right type was invisible to it. The fold drops the arm as in production and,
validated only, keeps the condition, which is evaluated where the branch was
and reports a disagreement. --aotFunctionWithWrongBranchesForTesting plants a
wrong fold so that tests can show the check fires.

The shell reads remarks and type coverage without stat. On macOS
stat("/dev/fd/<n>") fails with EBADF in up to a quarter of calls while several
processes do it at once. aotRemarks() then returned null and tests skipped
their assertions. A failure now throws.

More named counts for copies, clones and conversions.
A Structure without a typed layout can record, for each of its first inline
slots, a number that stands for the name of the plain data property there. The
GetById stub already trusted that record on its polymorphic path, but a
Structure got its numbers lazily, through C++, one Structure at a time.

The numbers of the names a program gives its objects are now part of the
image, and a new Structure records the number of the property it adds when it
is made. A transition that adds a property copies the record. Every other
transition starts from an empty one, which reads as "not known" and is a miss,
never a wrong hit. The record covers 24 slots instead of 16.

A pass over the program notes the shapes its constructors and literals give
their objects. For a read `x.name` it takes the shapes that hold all the names
read from or written to the same value. If they agree on the slot of the name,
the read first compares the number recorded for that slot and loads on a
match. A mismatch takes the GetById stub as before, so a wrong guess costs time
only. Compact code calls a stub per slot. Other code has the comparison inline,
and consecutive reads of one value share the address of its Structure, which is
kept only when the read was lowered on the main path of its node.

Validated builds compare each such read with the generic one. With operation
counters on, hits and misses are counted by reason.

useAOTGuessedPlaces and useAOTInlineGuessedPlacesEverywhere exist for
measuring.

Tests that asked whether data stubs are in use by looking for a call of GetById
accept the new stubs too.

Also: a literal whose type has a typed layout is born with a structure known
at build time when some of its names are no fields of the layout. Such a
literal was made by three calls that looked every property up by name. Names
that are no fields go behind the reserved slots in the order of the literal.
A structure with out-of-line storage is never put into the allocation cache of
the site.
Fixes.

A name that only code left out of the image gives to an object was in the
image's list of property names with the number of an identifier that is not
encoded. A string equal to it at run time, such as a key from JSON.parse, hit a
release assertion. A name is now listed only if a function in the image has it
among its identifiers. The numbers of the names do not move, and the numbers
given at run time begin behind the last one given to any name, listed or not.
The records of typed fields use the same filter.

With a type table loaded, the Structure of a literal without a typed layout
lost the numbers of its names: setting the layout number 0 cleared them.

A typed object is born before it is validated. When the validation failed, the
rejected value was still in a typed slot while the error was allocated, where
the heap verifier could see it. The message is made first, the slots are
cleared, then the error is thrown.

A keyed store with a symbol as its key always fell through the front end into
C++: 540 instructions against 80 for a string.

Stores. A store `x.name = v` that replaces a property first compares the number
recorded for a guessed slot, as reads do. A Structure that may be a prototype,
or has a typed layout with typed fields, records no numbers, and the
replacement watchpoint set of a slot with a number is born invalidated. Stores
to an object born in the same function and definitions are not guessed.
Validated builds assert all of that at each hit.

Guesses. Every name the program reads or writes is listed, ordered by the number
of sites. Computed keys and spreads in literals and derived classes are
understood. The state of the pass hangs on the compilation, not on a static.

Cheaper checks. The address a structure ID is added to is kept per slot, which
takes one instruction off every check. Reads of several values share what is
known about their Structures across nodes that cannot change one. The read by
name at the place a site remembers is two instructions shorter.

Guards over whole functions, off by default. A function with enough guessed
places is compiled with a second, generic copy of its code, as loops are when
they are split. A guessed access becomes a guard that leaves for the generic
copy at the same bytecode offset when the name is not in its slot, and never
comes back, so the access has no call and no effect on the fast path.
useAOTGuardsOverWholeFunctions turns it on,
useAOTGuardsOverWholeFunctionsInsteadOfLoopSplitting lets such a function keep
its loops whole, and two more options set the least number of guards and the
largest size. Constructors, generators, async functions, functions with a
handler, functions that return their values in registers or whose allocations
were elided are refused, and no guard stands where the creation of an
environment can still be reached.

A search of the recorded numbers by vector comparison, off by default, for
measuring.

Counts. With operation counters on when the image is built, every executed read
and store by name is counted by its outcome and by how its site will serve it.
Arrivals of keyed and named accesses in C++ are counted by receiver and key.
Validated mode remarks which folded branches it verifies.
Fix. Since stubs check recorded names, a split loop never stayed in its fast
copy. The guards of the fast copy compare the Structure of the base with the
slot of the site's cache. Only a miss of GetById or PutById fills that slot, and
in the generic copy the stub that checks a name hit without touching it. Every
iteration left the fast copy at its first guessed access. A loop that walks
pointers took 12 ns a node instead of 5.4. In the generic copy of a split loop
no place is guessed now, so the plain stub fills the slot as it did before.

Families, off by default (useAOTFamilies, with useAOTGuardsOverWholeFunctions).
The objects born with all the names of a shape, by a literal or by a planned
construction, carry the number of that family in their Structure. Transitions
that add a property hand the number on. Those that delete, change attributes,
make a dictionary, prevent extension, change the prototype, make the object a
prototype, give it indexed storage or convert it to a typed layout drop the
number and set a byte for the family. A pass over the fast copy decides what
each guard has to check: the name, the family's number once per value, the byte
behind an effect, or nothing. Validated builds check the cell, the number and
the name in full behind every guard. Builds with assertions check at every
change of Structure that a member only leaves behind a hook. The option is not
to be turned on in typed code before a test sees what the pass makes of type
tags, narrowed typed fields and typed literals.

Guards over whole functions, still off by default. A guarded read uses the
value remembered from an earlier guarded read. A function keeps a loop without
calls whole only if a guard stands in it. The entry of such a function costs
nothing more than before. New options allow functions with handlers, ask for a
least density of guards, and compile the fast copy with inline fast paths.

Guesses. Births in the same module count first. The names of Object.prototype
are not listed, and `return` of the iterator protocol is not guessed. A value
read from a variable takes the names accessed on every read of that variable.
Inline checks of stores share the Structure with reads.

The search of recorded numbers by vector comparison is removed again: it cost 17
instructions wherever it found nothing. An option that did nothing is replaced
by useAOTStructureAddressesReloadedAfterCalls. Remarks have a key in a third
column.
The names an image lists were found by a filter, a binary search and a map in
the VM. The first sight of a name cost about 560 instructions, and a program
sees some ten thousand before it is ready. The list is now an open-addressed
table by the string's own hash, at most half full. A candidate is confirmed by
comparing it with the program's identifier of that index. A listed name needs no
entry in the VM's map, which keeps only the names numbered at run time. The
first sight costs about 60 instructions.

A module's code block that is linked from an image has no constants, and the
offset of its environment's symbol table is never set. One use of that offset
was guarded already. The other computed an index that is never used, which
builds with assertions reject.

When a conversion to a typed layout is refused, the Structure stops recording
names. The object does not move, so what its family promises still holds.
Validated builds expected the name in the slot and trapped. They now accept a
Structure that records nothing where the guard's place has a family.

Builds with assertions asked whether the byte of a family was set when a member
left. The byte stays set, so only the first member to leave was checked. A hook
now notes the family it is called for and the check takes that note, so every
departure has to come straight behind its own hook.
Both need useAOTGuardsOverWholeFunctions, which is off by default, and both are
as old as the option. Its generic copy is the only final graph with blocks that
no graph of the whole-program analysis has. Behind a guard every live register
is a phi of the two copies there.

A plain call f() passes the resolved scope as `this`, and the parser replaced it
by undefined while it parsed the block. In the generic copy the register was
still a pending phi, so the scope stayed. The analysis had seen the graph without
the second copy, recorded that `this` is undefined, and the callee had dropped
its conversion. A strict callee returned a scope object to the program. The
replacement now runs behind the simplification of phis, for all graphs alike, and
sees through phis whose leaves all qualify. Plain calls behind joins and in loops
pass undefined too now.

A constant object that is only read by name is never allocated, and its reads are
folded where the base is the read of the variable. A read behind a guard exists
twice, and behind a later guard the two meet in a phi. The fold did not match and
an ordinary read ran on the placeholder. A function that folds such a read gets no
second copy.

The check of a graph against the facts of the analysis ran in validated builds
only and printed. It now runs on every graph with guards in every build. A
function whose graph contradicts a fact is compiled again without guards, and that
graph is checked too. A contradiction there stops the build. The generic copy does
not narrow by its own tests, so its static types are wider in a few functions.

failEveryNthAOTGuardForTesting makes every n-th check of a guard leave though it
would pass, for the guards of split loops as well. An exit that is only valid
after a real failure, where a call must throw, is left alone.

useAOTPropertyNameIDs, for measuring: with it off a place without a family is no
place, nothing is guessed outside guards and the image lists no names. Validated
builds then ask the property table by name behind every check.

No guess by the births of a module where a shape born only elsewhere holds the
same names with the accessed one in another slot. Reads of one variable with
nothing between them count as one value for the checks of guards. A family given
by a birth has at least one name. Misses of guessed places are counted by site.

Structure::createWithProperties seeds every slot and sets the largest offset
before it adds a name, so that the table and the offset agree at every step.
Builds with assertions could not run code with typed layouts before.
A read or store could compare the number recorded for a name in a guessed slot,
through a stub, inline, or as a guard. By themselves the checks through stubs
changed nothing: one check replaced another of the same length. With families only
one guarded access in twenty still compared a name. On a large application the
guards without them execute the same instructions within half a percent, the code
grows by 1.0% instead of 2.0%, and a program reaches its first prompt with 0.7%
fewer instructions.

Gone: the list of names in the image and its table by hash, the recording of names
when a Structure gets a property, the 48 stubs ReadNameInSlot and WriteNameInSlot
with their 816 forms by register, the inline forms and their sharing of Structures,
guessed places outside guards and with them the special case for split loops, the
guard that checks a name, and seven options.

The pass that guesses places stays, since the names decide in the compiler. It runs
with useAOTFamilies, and a guessed place is a family and a slot. Families, their
bytes and hooks, the pass over the checks of guards and the guards themselves are as
they were. The read by name at the place a site remembers stays, and with it the
numbers that are given to names when a cache is filled.

Graph::remarkOnGuessedPlaces says what was guessed when remarks are collected, since
the lowering no longer asks. Validated builds ask the property table by name behind
every check of a guard.
…async functions

A validated build checks every call, store and return of the final graph against the
facts of the whole-program analysis. On a large application it printed 14 distinct
contradictions with default options. In each that was understood the analysis is
right and the final graph's static type is wider. The final compile changes the
graph in front of its own inference in ways the analysis never does. As long as
harmless lines are printed, a real one is not seen. 13 of the 14 are gone.

readUnchangedRegistersFromFrame replaced a use of a phi behind a resumption by a
fresh read of the frame's slot. Narrowing attaches to the node that is tested, so a
branch tested one read and a call passed another, and what the test had proved was
lost. The pass still decides at its old place, where a block's bytecode range tells
its definitions. It rewrites behind the inference now, and the read takes the type
of the value it replaces.

A save that was moved to the definition of its register outlives the suspension it
was made for, if inlining and folding remove that. The check skips such a store by a
flag. The store stays, since the identity of a scope restored from the frame is read
from the stores to its slot.

A contradiction behind a value whose type is empty is in code that cannot be
reached, for example behind a check of a type that always fails. It is ignored, said
by a remark, and counted in the log of a verbose build, so that silence cannot be
mistaken for a check that did not run.
…inlined body

An inlined callee does `kept = e ?? fallback`, and the argument is a read of a `let`
variable in the caller, so its type holds Empty. On the edge where it is neither
undefined nor null the type was not narrowed, since with Empty in it the result is
not all cells and narrowing was held not worth a node. The whole-program analysis
types the parameter without Empty and does narrow. The store in the final graph was
wider than the fact, and a validated build said so.

Where a branch of an inlined body tests a value from outside that body, whether
narrowing is worth it may also be decided on the type without Empty. The narrowed
type is as before and keeps Empty, so nothing new is claimed. The condition is never
true in a graph of the analysis, which does not inline, so no fact changes.

This was the last contradiction that a validated build of a large application
printed with default options.
…is without guards

A function whose graph with guards contradicts a fact of the whole-program analysis is
compiled again without guards. That second graph was checked too, and a contradiction
there was a release assertion. But a graph can be wider than a fact for reasons that
have nothing to do with guards, where the final compile narrows less than the
analysis. Two such shapes are known, and one is what a transpiler makes of `??` for an
older target. A valid program with that shape in a function with guards killed the
compiler. With default options the same program compiles, and nothing looks.

The second graph is now compiled as a build without guards compiles it. A validated
build prints the contradiction as before. That the restart did not help is said by a
remark and counted in the log of a verbose build.
- A type that is true of a value and a type that is only expected of it are different C++ types: ProvenType and ExpectedType. A proven type says whether it holds in every version of a function or behind the checks in front of the value.
- DeclaredType keeps its type and its flags private. A position is established or expected, and only a proof that holds in every version establishes one. The Trust enum is gone.
- One analysis, PredictedValues, says which values stand behind which checks. The passes ask it in place of walks of their own.
- The verifier refuses a type that is assumed to hold behind checks where no check stands in front of the value.
- Modules take the loader on demand, so modules, their imports and import() run in the order of the language.
- A planned capacity does not convert the storage of an array whose readers go by that storage.
- Class proofs are asked of no initial value, and what stays in the list of an allocation gets the type of its field.
- An initializer stays in the body where the body takes its value: the return of a block without operations, an argument of a handler, the subject of a cleanup block.
- A host function describes only what none of its overloads takes, and describes a receiver that it refuses as it describes an argument.
- Handler rows, the head of the positions, the steps of the table of null checks and four columns of Representation are generated from the schema.
- The list of operations that take a key comes from the schema.
- A member that is no closure is passed over where closures that are named late are listed.
- Host functions that the library modules call through a saved reference are followed to their callees.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…nd removed operations to their reasons

- Flow writes the type of a position in front of each store of a value that stands behind a check. assumeTypesOfProvenPositions, typeWithoutChecks and letEverythingThrough are gone, with the helpers only they used.
- Three more rules of the verifier: a stored type rests on no check that may fail, the kind of an operand rests on none either, and an operation that may throw or run code goes only where its pass names a reason.
- What the front end says of an operand of a library operation is marked as its claim and stands as an assumed type in every version.
- A join of texts throws by rule unless the types of its parts fit in one string, so a join that nobody reads stays. concat, the HTML methods, Object.prototype.toString and JSON.stringify say the same.
- The list of objects that JSON.stringify is writing is a value of the function that writes, and a handler that goes on writing puts back the list it had.
- A string made of 8-bit and 16-bit strings has the width and the form that the engine gives it.
- `this` of a sloppy function that asks for its own instances may be the global object once something lets it pass the test.
- A store under an inherited accessor or a read-only property counts as a read where a class below defines a field of that name later.
- An object that becomes a prototype counts as written, so Object.create stays where nobody uses its result.
- Array.isArray stays a call for a value that may be a proxy.
- A suspended function holds the names that closures share where its text reads their scope, and the members that were built of an object that never is.
- Every member of a fused chain of array methods has its frame. Frames list stringify and json, constructors that only forward, and functions of library modules under the names they were given.
- A function object notes the first look for its `name`.
- A number that may carry a payload is taken as it is where only the number counts: an index, a type test, a switch, a push.
- The run-time test for a closure as it was created knows async functions.
- A function without a body in any form keeps no name at run time.
- Built-in constructors found through the unknown object are handed out as the unknown object, in every order of flow's rules.
- A module record costs less at the start of a program with many modules.
- A block of Air reserves what it holds.
- More of what was kept in step by hand comes from the schema.
- PredictedValues walks less, and two walks that served one assertion each are gone.
- The asserts on the storage of an object ask about its own structure and the chain above it.
- Flow says of an iteration whether an array of a decided kind arrives, so a list that is handed in and walked is converted once.
- A word that a suspension keeps and that nothing has stored to yet is cleared in front of the first suspension.
- The steps of the module loader check for an exception between them, and a load makes one walk over the links.
- A built-in that may be trimmed matches no overload, so no pass calls it directly.
- A construction shows where its expression starts in call sites and in printed errors.
- The square root of a constant is computed at run time where the result is no number.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 2 findings from earlier reviews are still open above.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

… copies, listings and classes below built-ins

- assumeOperandKinds, typeOfKind, insertAssumedTypes and OperandOfOtherKind are gone. `this` of a field initializer is a claim of the front end.
- A class whose every heritage is a built-in or null ends the chain of bases, so what a plain function makes for such a class is an ordinary object.
- A closure of a function that records its callee is a cell of the heap.
- An element that a fresh result carries where two lists meet has the type of its own list.
- The test for a function of a built-in asks for the native code, also of a function with rare data.
- A lookup beside the unknown object follows the sources of copies as it follows prototypes. createRequire is always followed.
- A host method that was found on its receiver describes only a receiver that has the method or its holder on its chain.
- A listing whose operand may be an unknown object counts what flow sees arrive there.
- Proven and expected arms of the same member share one type test where the receiver's layouts are only expected.
- A store whose test for the layout's prototype fails goes by name on the spot. A receiver that the function made itself keeps its slot store.
- A layout that is made seldom and is a little too large for a cell sends out only the rows that have no room.
- A serializer of a type that may hold itself is watched where its buffer grows, one level for 64 K characters. JSON.stringify of data whose text is bounded by its type goes where nobody reads it.
- A join counts the separators of empty places and appends them together. toLocaleString calls the same loop.
- A frame knows the bytes above its frame record.
- A function that may get a displayName or a new name notes each look for its `name`, and a frame that has its function object asks that object.
- Text that is known at build time and evaluated has the places of the engine's own text, and its frames name the evaluator's URL.
- A construction, a name that is reported behind itself and the argument of import() say where they are written.
- SimplifyCasts takes another turn where a cast with two operands went.
- More orders of modules as `bun run` has them: an entry point below the runtime's own module, the names of a CommonJS entry point, files that need each other.
- A refused function of the module loader is named in its message.
- The IR printer prints where a construction starts.
- Walks over a body are made only where their answer is read.
- A spread of a value that has no iterator method throws where it stands.
- A function that reaches the helper that renames it by a long road records its callee: what arrives at a retired node is handed on.
- A bound function or a proxy that an operand names as new.target is handed on as new.target.
- The remembered positions of call sites are a constant when the process starts.
- The plan of array capacities asks for the kind of a use in front of its user.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 2 findings from earlier reviews are still open above.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…orded in a table, and modules link per importer

- createRequire is one of the table's objects that let in unseen code: one definition for the solver and the heap rules.
- The walk to the sources of copies leaves `Bun` out unless a named `Bun` is marked.
- The prototype objects of library classes have a set of their own for the two rules that need it.
- A change that program code makes through a value nobody knows counts on each prototype object of the language that has escaped.
- The elements of a list whose claim names no storage get the list's contents as a claim. SinkAllocations answers IsInBounds by the range of the position's type.
- Field definitions below a constructor that calls a method count as seen only where flow says that somebody can see them.
- The callees that frames name lie in a table placed by the caller's stack pointer. The typed body and its fallback are no further forms of a function.
- A closure takes its full form only where a statement is seen to give it a displayName or a property under an unknown key. A short closure takes `name` at the first look for it.
- The set of names that were looked for is sparse.
- A bound function of a target that records its callee holds the target's closure and calls it by value.
- A method under an own property that only some instances have is called directly where the slot is empty.
- An own-property question under the name of a member asks whether the member is hidden. A question whether a name is there predicts no layout.
- A field that holds undefined outside the cell reads as undefined behind the slot test.
- A rare-sided test of a value against its annotation restarts in the generic version, and what the restart would throw away is made behind the test.
- A warm loop gets a copy of its likely paths only where they are small and a number that feeds itself gets another representation.
- The words of a suspended frame that no path has stored are left out by the frame's plan.
- A module that binds a name that a library module lacks fails by itself. import() of a CommonJS file during a require() takes the file's names again. A load stays in flight until it has settled.
- An accessor of a literal under an index is defined through the object's own method table.
- The HTML methods of strings add up the lengths of their parts first.
- ExpandIteration gives its helper the origin of the call that it replaces.
- Bodies are typed without their checks in place and put back. The verifier drops only checks that can turn a value away, and compares dominators with the naive solution once a graph.
- The generator of the table lets every own value be called before the return.
- Class methods, getters and static methods under a computed key that flow proves to be one key are members of the class.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 2 findings from earlier reviews are still open above.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…through copies of a module, and long functions that suspend build in near-linear time

- An error that nobody catches and that leaves a callback of a timer, tick, microtask or listener is printed with the frames of its throw. A throw of a new error has two call sites at one return address.
- A loader of a built-in module is found by its name on an object that was filled under the module's keys, and on a value that may be the module or anything.
- The object that a property descriptor describes is a hidden slot that an escape does not follow. Accessors under a name that the table does not list are found as setters are. A built-in that reads by a key from an operand has an operand role of its own.
- A function is named ahead of time from a computed key only where it is named at one operation in the whole program and has no copy.
- A literal that is born with copies holds what its sources surely hold. An untyped literal of spreads alone is an object of its own layout.
- A require() of an ES module stops its walk at loaded modules and takes the loader only where its own unloaded part meets a load in flight. A module record says in one bit whether it is the face of a file. Targets that are settled for links are skipped. Settled loads are dropped when a load starts.
- A graph goes module by module through the loader while another graph is being loaded, not while an earlier one waits for its promise.
- `x % 1`, `x % -1` and `x ** 0.5` call the library function for a result that is no number where the sign of its answer differs from the processor's.
- Air: the use and def lists say where a Patch with over 64 arguments mentions a Tmp, Code keeps the forms of large patches, and the slots that live across suspensions are found with a bit matrix.
- The error of an entrypoint that nothing enters names no function.
- DeferInitializers keeps at the start of the body what a cast that stays there takes.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…hat nobody keeps are planned outside hot code, and flow keys reads by the receivers that hold the name

- A graph goes module by module through the loader while an earlier load is loaded and its root has not run to its end. A plan of the levels of a load leaves out a module that has nothing to link or run and the ES face of a CommonJS file. The files of a level run without records, marks below a walked root are made when a require() asks, and the link walk of the root is kept.
- Outside hot code the first spread into an empty literal, and the rest of a pattern, is planned again. It stays a call only where the plan has tests and somebody keeps the object.
- Flow: a read by name on a set of over eight receivers is recorded under the receivers that can hold the name, with one watch entry for a name and a statement.
- Flow: a write under a key that nobody knows, through a value that nobody knows, counts on the prototype objects of the language that were carried out as values.
- Flow: the type of a slot in front of a store holds behind checks where the stored value was cut by a check.
- A static definition whose computed key can only be "prototype" keeps its operand.
- An error that nobody catches and that passed a cleanup which throws the value on (`finally`, `using`, the cleanups of iteration), or that native code throws on, is printed with the frames of its throw. Two runtime operations, AddressOfThrow and CatchValueAndException.
- The stack of a SuppressedError lists the function that holds the `using` declaration.
- No async frames are listed above the body of an async generator that has gone on behind an `await` on top of the frame of its consumer. A getter that the rest of an object pattern runs has its caller at the pattern. The last place behind a spread in a list is where the operand starts.
- The frames on the stack say which recorded calls have not returned, and the table of recorded callees keeps those alone at a collection.
- A closure that only the store into an unread field sees goes with the field.
- The result of a whole remainder by zero is made at run time.
- The spread of an object literal in interpreted text asks whether its target is a JSFinalObject.
- SinkAllocations holds the members of unbuilt objects in one walk.
- RemoveRedundantHandlers skips a handler that only throws its value on where the handler is a cleanup, not where it is a `catch`.
- An array that a built-in makes for numbers has storage for doubles. Only a store that is marked as in bounds needs a number that cannot be NaN.
- Where the arm behind an empty slot holds the method of the class, the test of the slot is no prediction.
- Flow: a function object that still has its own `length` and `name` is handed to no accessor further up its chain.
- Flow: the own values of a built-in object are those of the object and of the sources of its copies, not of the objects above it.
- A `displayName` that is written through a value that nobody knows counts for every closure that has escaped.
- With the phase log on, the end of the rounds of flow lists the bytes of each table.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 4 findings from earlier reviews are still open above.

Still open from earlier reviews (4):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…ut what it holds under any key, and flow asks only the receivers whose chain can answer a name

- One unwinder of compiled frames walks frame records and the call site cache itself. Whether a function ends what a cleanup throws on is read at the handler of the block that throws.
- The three catch operations publish their frame record, as every operation that can collect does.
- A read under a key that nobody knows, of an object that the program filled, also answers the unknown object where a function that runs text or loads a module was left out of the answer. createRequire stays in the answer. A proxy bears the mark of a member that was left out where its target is a public module, and a read by name finds the member where its receiver holds the mark.
- A loader that is called with a constant package name which nothing in the image answers to counts as a load of a library module.
- No file runs ahead of a load that ends early. The name that fails is the last request of its record, with its error in the loader's table of resolution failures. Where the load of a graph is rejected, the jobs that were held for it go back in front of the steps of the rejection.
- The members of a class that are made at their first use are made with termination deferred. So are freeze, seal and isFrozen of an instance with elements, and the special property names of a closure.
- Only the body of an async generator that the stub entered goes on in place behind an `await`.
- A function whose body is left out of the executable takes what the compiler itself puts into a slot under the type of the slot.
- Flow: a read by name on over eight receivers, with no placeholder beside them, asks the receivers whose chain holds something that may answer the name. `new` on a bound function enters its target with that target as new.target.
- PredictedValues answers "it may be" for a value that was put into the body after the analysis was made, and ApplyFlowResults makes the analysis anew behind its own claims.
- A call gets the effects of unknown code only if its normal edge may run other code. A plan that reads a slot as its declared type counts as covered only by a test that asks for the declared type. What a guard proves about a list of keys is kept round a loop.
- An entry parameter takes the environment in place of a closure where every caller fills it with the closure of one environment field and it is only taken apart or handed on.
- GuardTaintedPositions and applyBodyUpdate() walk a body fewer times.
- Yarr: a group that is repeated a fixed number of times and makes no choice leaves its loop behind the first turn that takes no input.
- ReservedRangeHeap keeps the access of a freed run between two accessible neighbors once the process has 16,384 borders.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 2 findings from earlier reviews are still open above.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…are their executables between realms, and flow hands a callee only the receivers that find it by themselves

- Modules: a graph is planned, marked and walked for links once. An import() of an evaluated module is answered without the loader where nothing below the module awaits, or where its record names a cycle root that is evaluated without an error.
- A statement that deletes or redefines a property of a namespace object counts as a look at the export of that name, so the export stays described to the run time. A definition is put down as a redefinition on the namespaces among its receivers.
- Without the JIT, one weakly held table keeps the executables of host functions. Named ones enter it from the second realm of a VM on.
- Flow: a callee nobody follows that a set of receivers finds gets only those that find it by themselves. Calls of built-ins by native code use their own operands. Each bound function enters its own target with its own receiver and arguments.
- Flow holds fewer bytes at the end of its rounds, and two walks over all nodes behind the rounds are gone.
- A call that reads its callee from its own receiver under a computed key may call createRequire where the receiver is a public built-in module. A member of Bun that lets in unseen code is found by its name on what was filled under the keys of a Bun that the program loaded under a constant name.
- A call by value records its callee once a display name has been written at run time, and not before.
- `delete f.name` takes the name that Object.defineProperty gave a compiled function out of the names that frames show.
- The version of a derived constructor that is handed its `this` goes back to the ordinary constructor by a tail call. `return require(..)` stands in tail position.
- A read of a member of a constant that is printed as its value carries its written place.
- A function with `catch (e) { throw e; }` is summarized for inlining with its handler.
- A closure over one value is that value.
- The slot plan counts a field that copies fill as filled, keeps it in the cell where the cell is no larger for it, and leaves out the keys that every copy into a layout leaves out.
- A literal that is born in pieces holds what its pieces define.
- A list of numbers that arrives in storage for any value is copied into storage for numbers as far as the proof goes.
- ExpandIteration cuts the entry of a spread at the place of its open.
- AllocateInFrames asks one predicate on both of its ways whether the closure table holds a closure that is only called.
- What typing cuts out of a body names its reason.
- The pass runner leaves out a schedule whose passes are all at rest and counts the contents of a body from its change log. The run that finds names in the program tables leaves out what names no identifier. Two readers behind Select share their analyses.
- putDirectCustomAccessor() asks the structure whether the name is there.
…uilt-in function makes starts on the lean walk, and the plan of a load has a tighter loop

- A collection no longer goes over the frames on the stack. A recorded callee stays while its call site may still be on the stack, as before.
- One frame of a host function on top of compiled code is written on the spot, and the lean walk starts at its return address.
- Modules: the plan of a load visits a module once, and the walked way makes no link walk and no rebuilt key for a module.
- The link from what a TypeTest asks to its proof parameter holds for the closure of one function, or undefined.
- A closure in a field of an environment that nothing reads also goes where the environment names what the closure captured, in a field that is filled behind the closure in the same block, if every operation in between completes.
- Kept Retains in unknown memory covers a fresh Returns where effect summaries are compared.
- The high half of a 64-bit pair is cast before it makes a FunctionID.
…called, a store under an unknown key reaches the closed holders of replaceable members, and a recorded callee is dropped without reading a frame

- copyDataPropertiesInFrame() is NOT_TAIL_CALLED: it is handed the frame record of its caller.
- Flow: a retired node goes on taking the closed constructors and namespaces that hold a member that start-up may lock. Where such a holder is only listed beside the unknown object, a store writes the unknown value to it.
- A collection reads no frame. A recorded callee is visited until its call has returned: the VM is not entered, or an earlier collection marked the entry and the VM has been entered from outside since.
- Flow: what is copied onto a new object is asked of the source objects, not of their layout, and so is which keys they keep.
- The front end of a copy of own properties is a prelude of the call's head. Where the structures differ it looks up the mapped copy and makes the moves itself.
- Where an iterable is expected to be only undefined or null, the proven type decides whether the array arm is tested.
- Modules: a kept link walk survives a link of ES modules below its root.
- SimplifyCasts takes the set of opcodes from the runner and makes fewer walks over all values.
- The code that an operand of a built-in runs is described once for a type and a behavior.
- Bound functions that are called at one place enter their targets as one group, as before.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 2 findings from earlier reviews are still open above.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…, `instanceof` asks the target of a bound function beside values nobody knows, and copies of own properties take fewer instructions

- A PropDefine whose NewObject is out of sight creates the field wherever a construction of the layout leaves it out and the allocation in sight does not list it.
- Flow: where a set of callees also holds the unknown object, what each known receiver finds alone is united into the callees.
- Flow: retired nodes go on taking every holder that is no prototype object and has a member that start-up may lock. A definition writes the unknown value to a holder that is only listed.
- Flow: the argument nodes of the call group that is made for the unknown object take what is handed over.
- Flow: each bound function enters its own target with its own receiver and arguments, the unknown callee enters no escaped bound function a second time, and each receiver enters the accessors that it finds itself.
- Flow: a literal with members of its own gets fields for the names that every listed source of its spread is born with.
- Flow: a read that names a loader on a value that may be anything also looks at the public modules of which the listed objects are copies.
- Copies of own properties: the functor of a move is inlined, a copy takes no frame also where the target holds fields that it does not write, and the prelude also stands in heads that preserve registers.
- Stores that may add a field are numbered so that two of them meet in one entry of the remembered key order steps only over a layout that comes to both, and only those that still meet compare the turn. A store takes no more code in a larger program.
- In a validating build, a test that is repeated for validation counts as a use of its proof where the reads of a list are copied for the other storage.
- A read or `in` of an element that an array keeps in its sparse map stays in the stub.
- A RegExp literal whose copies meet in a block parameter is shared where nobody inspects it.
- A lone filter() or map() that InlineCalls accepts as a version in a later round is offered to the traversal.
- The stack of an exception that a host function makes above compiled functions is taken by the short walk, and the text of a stack reserves its room first and is written through StackFrame::appendTo().
- FuseConcatenations asks no effects of a concatenation.
- Checking every pass: SimplifyControlFlow names its reason where it erases an operation in front of a throw that nobody sees, and an answer that is asked for after an edit the analyses were not told of is reported in words.
- A generic copy behind a guess from names is Unlikely, not Rare, in checked functions too.
- The effects of a lookup by key are cut to the row of the operation where the row runs no unknown code.
- A compound assignment under a key carries a second place for callers.
- Modules: a kept link walk is replayed past what another link has linked, and a module that is loaded takes fewer steps.
- Build time: findConversionsEndingProofs() returns at once on an empty list, what a layout inherits under a key is looked up once a class, and the natural loops are asked for only where a short loop may stand.
- B3: fixSSA asks an index of frontier edges that takes linear space, and FoldPathConstants skips a run of entries of one comparison in one step.
- Tests: a native test that throws below a copy of properties. Tools/Scripts/aot/find-frame-records-handed-to-jumps.py lists the functions that hand the address of their frame record to a jump.
- aot/analysis/AOTFunctionAnalyses.cpp includes <wtf/text/MakeString.h>.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 2 findings from earlier reviews are still open above.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…e on the way to a throw is compact, and a require() into a graph that is being loaded takes one module a level

- A closure of a function that records its callee only once renamed may lie in a frame: checkCellForm() refuses only RecordsCallee without RecordsCalleeOnceRenamed. For a call by value of such a closure the run time records the function as written under a mark in place of the function object, and the name of that frame leaves displayNameInFrames() out.
- A block from which every way on ends in a throw is Cold and its code shape is Compact: what writes the message of an error and throws it is expanded by no pass, however often its function is entered.
- Modules: the levels of a load are planned under a synchronous load too, so a require() that comes into a graph which the loader goes through takes one module a level, as an import() does. The jobs that an imported CommonJS file has queued when it throws wait for the loader as those of a file that returns do.
- A PropDefine that runs no other code, under a name found on neither base chain of the layout of an object allocated in the function, ends neither NoFieldOutsideSlot nor NotFrozen of that object. A literal that writes a name beside the fields of its layout stores the fields behind it without tests.
- DeferClosuresInFields leaves a closure unmade also at a store that only expects the layout of its object, where no code that is not cold reads the field by slot or by name.
- PromoteEnvironmentsToVariables: where code runs once, a closure that nothing uses makes no variable of what it captures. A value stays an operand of the NewClosure, an environment that only such closures capture stays an object, and both go with the closure.
- Where a namespace object is only read by name, flow lists the exports that a look into the object names or the host reads (ModuleFlow has `exportsReadByName`). Only those have a row in the export table and are filled at the start, and DeferInitializers may leave the others empty.
- InlineCalls: a function that does little more than call another moves to the arm that alone calls it, where the program has written one call, the body has no loop and the caller's function is not cold.
- PlaceTypeTests: in cold code no test stands in front of an access to an element or of an operator, whatever the class of the function.
- A look for the `name` of a slim closure whose function notes such looks sets one bit in the header of the cell. The stub answers from the tables of names as for every other closure, and becomeReified() takes the name where the bit is set.
- Copies of own properties: operationAOTCopyDataProperties is the operation with a frame and stands in the table itself. The prelude of both kinds of heads looks at the place in copiesWithoutFrame and goes to that operation where the place is empty, else to the moves or to the new operationAOTCopyDataPropertiesByFieldMap, which tail-calls the other where it does not serve the copy.
- Copies of own properties: the map of two layouts serves a source field that holds a raw value from birth (describeMappedField()).
- Date.prototype.toISOString writes its text with the writer that the inline form and JSON use (makeISODateTimeString() in DateTimeWriter.h).
- The shared stub for `-x` of a value of unknown type flips the highest bit of the box, as inline code and operationAOTValueNegate do, so a NaN takes the other sign there too.
- ScratchRealm::call() answers a text however the engine holds it and says whether it is stored as a literal would be. The fold alone asks that, so a pattern whose text is put together at build time stays in the image.
- Words left on the stack: an adapter that native code enters fills the words of the entering frame that vmEntryToJavaScript leaves unwritten (the callee-save buffer of its VMEntryRecord, what rounds it up, and on x86-64 the word between an even number of arguments and the record). Beside compiled code asyncGeneratorBodyCall() first clears the stack below its caller (AOT::clearStackBelowCaller()).
- Flow: Object.getOwnPropertyDescriptors has a model (own keys, one Object.getOwnPropertyDescriptor a key, defined on a new object), so what comes out of a descriptor is the function object that went in, and an object that is handed to the call does not escape by that alone. The numbers of the compiler's own functions behind the model are one more.
- Flow: a callee that native code calls on several receivers looked at together gets the receivers that find it by themselves, and so do the built-ins that are applied there (class ReceiversByWhatTheyFind).
- Flow: while an overload of a built-in is applied, its receiver operand is cut to the receivers that the overload accepts; describeRefusedOperands() sees the whole receiver.
- Flow: where the table lists, under a name that a hub calls, a function that stores into its receiver, alters it or reads its species, every known receiver that has arrived at the hub goes on being handed to such functions as it finds.
- Flow: a read under a computed key answers the members that load nothing by themselves also where the operations of its own function say which module it reads (an import's binding, RequireModule, ModuleNamespace).
- Flow: Proxy.revocable answers an object of its own that holds the proxy, and what `new Proxy` puts into its result is written on that object.
- Flow: a store under an unknown key through an unknown receiver writes a primitive to the listed holders as it is; the unknown value is written once the operand holds an object.
- Flow: a row of the table for a receiver other than the holder may say that native code constructs an operand (b.otherwiseConstructs() in the generator): Array.of and seven static methods of Promise construct `this`, promiseResolve and promiseReject their first argument. A built-in function that raises an event when called and that an object of the program answers under @@species joins the group that native code constructs. Four .ir files.
- AddGenericVersion: in the copy of a body without its checks that stays inside the function, each operand that takes only what flow holds and is typed outside its required type gets flow's word about the position: an AssumeType, with a ValidateType in front of it in a validating build.
- A function of a module marked IsBundlerHelper (bun:wrap) is no function of a run-time module: it has an own `prototype`, its kind is Strict, and the text of toString(), the form of its closure and its statics follow.
- Stack frames: the lines `name@url:line:column` that the engine writes itself take, for compiled frames, the name that getCalculatedDisplayName() gives and the URL of the source provider, and a frame that stands for a host function is `[native code]`. The record Function has `hasWrittenAwait`: an async function that takes its name from a variable or a property has no name in frames where its text holds an await.
- Stack frames: whether a saved frame is the body of a module is read from the entry of the call-site cache for its resume address, and isSuspendedAsyncFunction() looks at the saved frame once. A name that is stored without a look for it is shown until somebody first looks for `name` (showNameUntilLookedFor()).
- A function whose entry always ends in a throw has no other way, so no block of it is marked: it is compiled by how often it is entered and is copied into the caller that catches. Its .ir file says so.
- ResolveCalls: where the parent of a super() is a constructor function, the object is allocated in place only where new.target is always the class that holds the call or a class below it (isAlwaysClassAtOrBelow()). For a class beside the holder the super call stays as it is and createThis() decides, as flow says.
- Flow: every field of a class that is the new.target of another function is missing at creation, the inherited ones too: the constructors between do not run.
- Tests: .ir files for blocks on the way to a throw (frequencies, own keys, code shapes), closures at stores that expect the layout, a closure that nothing uses, closures that are recorded as callees once renamed, a forwarding function that moves to its arm, no test of an element in the cold code of any function, a definition beside the fields of an object made in the function, exports beside a namespace object, and the helper module of the bundler. The dumps of guard availability list six facts an allocation, and two files of PlaceTypeTests say under which heat they speak.
- Tests: flow under all four schedules: the callback of a method is entered only for the receivers that the method accepts, converted objects are handed to what each of them finds (eight new files, four under a new name), and a pattern that has escaped goes on being split by what its species names. The native tests of the copy stubs say which of the two operations a refusal reaches, and that of the unary operations counts the sign of a NaN.
- Tests: .ir files for a super() that constructs for a class beside the holder, and for a class that is constructed for a class two steps below.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 2 findings from earlier reviews are still open above.

Still open from earlier reviews (2):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…takes fewer instructions in its stub, and the stub of a copy makes the moves that alter the form of a value

- Heat is estimated twice in an ordinary program (ranks, then entries by classes and events). What seen code does not reach is cold, what comes with events is hot only where it is entered over a hundred times an event, what a handler of the application calls nearly every time keeps its rank, and of the library only what a loop calls is hot.
- A call by value: a callee whose type is an object enters the stub behind the test for a cell (new entry CallValueCellWithoutThis), a slim closure that is handed too few arguments is told by the top byte of its word in an image with one instance, CallCellWithCount fills no register where the count says that every one is passed, and loadClosureWordOfThisRealm() leaves the test of the realm out for slim closures of an image with one instance. On ARM64 the byte map of PollBeforeEntering.FunctionObject is tested in the stub.
- Copies of own properties: the heads that preserve registers walk the moves of a copy too, the stub makes the moves that alter the form (an int32 into a double slot, an int32 boxed, a narrow reference widened), and a copy by the map that overwrites fields the target lists takes no frame.
- Copies of own properties: a copy that is kept for other objects at the place of this one is passed over in the stub: both heads call operationAOTCopyDataPropertiesByFieldMap only where the kept copy is of the source's Structure. The native tests say for every refusal which operation is called.
- The stub ClosureOfTable makes the function object of a top-level function itself where it is a slim closure. The table of recorded callees is walked once a phase of a collection, where the stack is scanned.
- A look for the `name` of a function object whose function notes such looks is noted in the bit that every cell has for itself (TypeInfoPerCellBit), in both forms of closure, so a closure over many variables and a generator function answer in the stub too.
- The own `name` of a host function that is named as a bound one comes into being when it is looked for (NativeExecutable::isNamedAsBound()), so such a function keeps the structure of host functions. Function.prototype.bind on a slim closure that has properties puts name and length off where both are as created.
- A store by key that the object's own table answers remembers its place, as a read does; where a Structure begins to watch replacements the places for stores are cleared (didStartWatchingReplacements()).
- A piece of a text that serves as a key for the third time becomes its name, and the base of a piece is loaded by one load and one shift.
- shouldMakeStructureForAdditionByKey(): a pair of Structure and name that is found again moves to the highest free place of its set and stays, so it is still found when a full collection has dropped the Structure.
- An await of a promise that is already fulfilled, in a function that has to suspend, queues its job in the stub, as an await of a primitive does.
- Evaluated text reads a field in the forms Int64 and OptionalInt64 in the handler of the interpreter.
- A native throw whose nearest handler in the same run of compiled frames is a `catch` makes its Exception without a stack of its own, as under `bun run`.
- HoistLoopInvariantReads: a PropGet that is proven to run no code leaves a loop in which nothing writes what it reads, a read of a variable that is stored once also leaves the block behind a test that holds on entry, and a loop entered from a branch gets a block in front of it. A name that flow lists among the added keys of the closures of a constructor function is read by name.
- SeparateUnlikelyLoopPaths lets an unlikely path leave a hot, short loop also where a test of the likely path reads state from ahead of the loop that nothing in the loop writes (leavesTestWithOneAnswer()).
- A new operation FindsDeclaredMember(object, key) compiles to one guard of members over the member along the whole chain. Which operations are accesses by a written name is read from the schema (findNamedAccesses()).
- GuardFacts: for a user that names the allocation itself, a store of what may be undefined or null counts against the declared type of a slot only where the allocation dominates the store.
- ExpandIteration writes the step through a string as the resolved CallBuiltin of codePointAt, and writes no string arm where a test of the operand for a string has not held. A value whose range of integers is no longer inside the range it had counts as widened in replaceUpcastsOfWidenedValues().
- ResolveAwaitedCalls leaves an await of `generator.next()` as it is where the layout of the iterator result is NeverAllocated.
- On x86-64 `float16View[i] = x` takes a number that holds no NaN out of bytes, as every tier does there.
- operationAOTDeleteByName and DeleteByNameSloppy hold the name while they work.
- The image asks for the frame of an access with an implicit null check at the access itself (collectOffsetsAskedForFrame()), so a function whose only such access stands last in its frame has its own frame number.
- Modules: a built-in module that no ES graph has imported yet is a leaf of the level on which it is requested, import() of a built-in module is settled in the jobs of a load, and the plan of a load asks for an ES module without a record first.
- Modules: RecordRequire may run unknown code. What stands as module.children gets a required file at once and is asked at every require() that returns (ModuleHost::showChildren()).
- Modules: the plan of a load keeps what it found level by level (PlanOfLoad): a require() that goes through the loader takes up the ways below the level that the loader has asked for, and a request for what another module of the same level has asked for is heard of one step later, as under `bun run`.
- Modules: an ES module that the loader had in its hands gives up its record, its registry entry and its promises once no load is in flight, where it ran to its end with its cycle; its status then says Evaluated, as that of a module that ran below the root of another.
- Flow: a copy under the layout of its own source holds what the literals of that layout list: the rounds of followCopiesAtBirth() begin with every name of the layout and take away what a birth does not bring.
- Flow: for a constructed class whose written chain of bases ends at an expression, the class objects that flow finds above it say who allocates (forEachAllocatingClassOf()). A returned value whose type holds no object only behind a check is followed to its sources where flow asks whether a constructor answers only what it constructs.
- Flow: constructSpecies() also asks the node itself for the objects of the program that hold something under @@species.
- Flow: at `o.m()` a primitive or an object nobody knows goes, as a known object does, to the functions that its own lookup answers.
- Flow: the mark of what a read of Bun under an unknown key answers also goes with the values of a copy of Bun, with Object.values and Object.entries of it and beside a descriptor of one of them, and a read under the name of a member whose answer holds the mark reads that member. A proxy takes the mark from what has escaped.
- Flow: a read under a computed key answers createRequire also where its receiver comes out of a constant that is set to what a module operation answers, and where that answer is an awaited import().
- Flow: a copy of properties whose target may be the unknown object lists its known sources, and a displayName written on one of them counts as written through that object.
- Flow: a mask that lists no object at all has no family, and on the taken edge of `x === y` the mask of a number is also written where y is a number by the widest type of its own operation.
- Flow: the class that holds the constants says which reads take one (ConstantsHoldingModules::isReadBy()).
- Flow: plain data that lies inside what a built-in makes (the value of the promise of json(), the elements of a parsed list) is an object and an array of its own, for which the two result objects of JSON.parse stand.
- Stack frames: a native function of the library that compiled code enters by its direct entry is listed above its caller: the call site names the function, and the table writes a name beside functionObjectCode. The function of the host stands between what require() runs and the code that called require() (EvaluateModule has `isCallOfRequire`; two new entries of the host's table).
- Stack frames: every function of a helper module has its text (hasFunctionText()), and where a function object lies in a frame nameSetAtRunTime() is not asked.
- Stack frames: `new f()` of a native function that is no constructor enters the native constructor of the function, which refuses in a frame of its own (throwNotAConstructorErrorInFrameOf()).
- Validating builds: a function is split behind its entry checks and the reads behind the test for Double storage are copied as in every other build.
- Validating builds: fillCellsOfEngine() waits for a member that the engine makes at its first read, and where flow has no results applyFlowResults() says that the keys of every holder may be listed.
- Checking every pass: each check of the analyses says in one record what the analyses hold and what the other way gives (isSame(), isSameList(), holds(); EffectsCache::reportOutdatedAnswer()). In an engine built with its own checks on, an operation whose row has no `collects` stops where a cell is allocated.
- B3: the search of Air's validation for a Tmp that is used before it is defined solves liveness in slices and keeps a set for the entry blocks alone.
- A function whose calls by value record their callee only once a function object was renamed is called directly behind one test: a new operation of the family Environment, beside RecordCallee, reads the word that the first such rename writes, and the call by value stands behind it. The body is copied where the place is hot. Three .ir files.
- Accessors of object literals: one plan lays getters and setters in any order into slots, one GetterSetter a name, also for a lone half; selectAccessorGroup() cuts a list that another member interrupts into several groups, and flow makes a name that only gets a setter an accessor field.
- The table of built-ins: a constructor lists all its members, as a function does (closeFunctions()).
- ResolveAccesses: for a proven receiver the read of a method counts as nobody's object also where what was read is asked what kind of value it is and where no call names it any more (isNobodysObject()); a read that nobody uses at all is no such read. One .ir file.
- Tests: .ir files for the two estimates of heat (33 restated, two new), the split behind entry checks, spreads behind a test for a string, a class below a mixin, loops that a test with one answer leaves, reads that leave loops, FindsDeclaredMember, what a construction answers, receivers nobody knows under all four schedules, and rounds of typing that run out. Native tests for the call stubs, the moves of copies from all three heads, an addition by key that came again, frames at lone accesses, and three for the records of the analyses.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 1 finding from earlier reviews is still open above.

Still open from earlier reviews (1):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

…, Reflect.construct() and private names on objects of built-ins stay in compiled code, and a spread into a literal that nobody keeps makes no object

- A field that is added to (`this.n += x`, `n++`) and is neither declared int32 nor used as an index holds the integers from -2^52 to 2^52 - 1 in eight bytes, only where every cell that holds it keeps its size class. On x86-64 the sum is made in the low word of the slot with a branch on the carry (a new form in B3LowerToAir.cpp), and the bounds lie beside the instance.
- Reflect.construct(F, list, T) that is written so, with a list that is always an array and a callee and a new.target that are proven closures of constructors, is a forwarded call as f.apply() is (planForwardedConstruct()).
- A private name that is defined on an object which a built-in made: the existing transition is tried first, and the call of operationAOTPrivateDefine has a prelude that compares the Structure, stores, and grows the storage by what the token of the class remembers.
- A static field that is read through a class below the one that declares it is remembered, `super.name` goes through getAndCache(), and the stub TryGetSuperById also answers Field, Variable and InheritedField.
- `{ ...o, b }` that nobody keeps is not made: a spread whose source has the literal's own layout gets the arms by key order where the escape analysis says that nobody keeps the literal. In ExpandOwnKeys an order that leaves out an enumerable field that is always filled gets no arm.
- B3: a load is not folded into its user across a call that may collect (Procedure::heapThatLoadsStayInFrontOf, which is empty for every other user of B3).
- `JSON.parse(text) as T`: a source is closed where a throw passes the frame that opened it, and in the generated parser a member that comes again keeps its place among the keys and takes the later value.
- The direct call behind the test of function names: the body of the callee takes the place of the call only where the copy is no larger than the call by the inliner's own count, whatever the heat.
- A callee with entry checks that may be renamed where nobody sees is entered by value alone.
- A function that is made once gets no RecordsCallee for being async, or for another form of it that is only called directly: it is called directly, and its frames are named by what the run time writes down at each rename of its one function object.
- A generic entry point that nothing enters is Rare and carries no name: its error names the function that a stack would list on top, so all such entries share two stubs.
- In a function of the library no test is planned for an arm that holds under a quarter of the layouts that are predicted for the receiver (planExpectedMembers()).
- TypePropagation: where two edges meet, a block parameter that has been typed sixteen times on the way down keeps the integer range that it has (keepRangeOfNarrowedParameter()).
- new Worker() where the address space is limited: it also asks the allocator for the pages that a thread takes as it starts, the cell range is asked for in the page sizes that a VM takes, a new cell segment leaves room free, and arenas are asked for only where one fits.
- The table of built-ins: a constructor that is itself what a module exports stays open.
- Where no host is bound, the methods of generators for either kind are also installed for an image that holds no compiled next().
- InlineCalls: also in hot code there is no copy of a shared body where the generic entry enters the body by a tail call; a hot entry that has to come back keeps its copy.
- InlineCalls: that rule for hot entries holds only where the entry is nothing but the tail call; an entry that reads a capture, holds a claim of flow or tests anything keeps its copy.
- The lookups that the schema's generator writes (PayloadWord, OperandPosition) read one byte table, shared by all callers, where 24 entries or more declare them. One native test.
- The rare arm of warm and hot code calls the stub ClosureOfTable, which takes the number of the function as an immediate, and the arm of a slim function says that it converts no cell, so B3 keeps what it has read of the caller's own objects.
- Modules: ImportModule takes an operand `options`: the second argument of every import() of a constant specifier is handed over as a value, and Modules::import() reads it ahead of asking whether the module is of the image.
- Modules: in flow's order of modules an import by a module that the loader loads completes ahead of the body only where its target lies on no cycle.
- Flow: a bare name that the program stores under (`globalThis.List = Array` or `List = Array` stands somewhere) reads what `globalThis.List` reads; which names are stored under is read off the text ahead of the solve.
- Flow: a function that lets in unseen code and is stored under a name into an object that has escaped is also written under that name on the unknown object, and a read that starts from the unknown object takes such functions from there.
- Flow: a row of the kind DataProperty takes every receiver.
- Flow: at `o.m()` whose callees hold a member nobody knows and closures for which the call already tells its receivers apart, an object of the program that has not escaped is handed to that member only if its own lookup of `m` answers a value nobody knows or a function that has escaped.
- Stack frames: a require() of a name made at run time that finds an ES module of the image lists the host's loader of ES modules, and the function of the host stands between a library module that loads for the first time and whoever required it (one new role of the host, createInternalModuleById).
- Stack frames: the frames of a run of compiled code go between the frames on top and those below only where the run shows a frame.
- Build time: what is live at the heads of blocks in guardNarrowReferences is solved by strongly connected components, successors first, and a head goes with its last reader. Who allocates for a class whose written chain of bases ends at an expression is asked once a chain, and the classes that the prototype of an allocation may select are listed once for all allocations with the same prototype objects.
- Tests: .ir files for the forwarded construction, a literal that nobody keeps, an order without a field that is always filled, entries that nothing enters, one of many expected layouts, ranges that shrink by a step a round (two under new names), a bare name that the program stores under, importers that the loader loads, field definitions below a constructor that calls a method under the three delayed schedules, and the callee behind the test of function names. Native tests for updates of integers in the slot, loads that stay in front, sources of JSON, and four for references over loops.
- Tests: an .ir file for a data property of the table that a receiver outside its declared type reads.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 1 finding from earlier reviews is still open above.

Still open from earlier reviews (1):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

@Jarred-Sumner
Jarred-Sumner merged commit 982277c into main Oct 26, 2026
76 checks passed
Jarred-Sumner added a commit that referenced this pull request Oct 27, 2026
…sition-independent decisions, a check of pointers across collections (#912)

Follow-up to #906, from the same branch. Everything is under `Source/JavaScriptCore/aot`, which only a build with `ENABLE_AOT` compiles, except what the last section lists.

### Generated code

- A read of a member that nothing above the receiver's layout has gets a site cache and compares the slot in line. Where one getter is seen at the site, its body is called directly.
- A function object that the caller made itself and has handed to nobody is called directly, without a test. A use between its creation and the call counts as a hand-over unless it merely looks at the object. A function object that becomes a prototype does not count as only called.
- Reads, `in` and stores through a `Proxy` read the `get`, `has` and `set` traps off the handler and call them from compiled code, also under a small index. A proxy without a `get` trap reads a data property of its target in the stub, and one without a `has` trap asks its target there. A program in which no proxy can be made holds no stubs for traps.
- `Reflect.get(object, key, receiver)` and `super[key]` answer a data property in a stub.
- `new C()` of a derived class that is reached by value makes one cell. `new C()` of a class expression that is evaluated many times, such as the class of a factory or a mixin, is answered in a stub for every evaluation. Constructing `Promise` and classes below it takes fewer steps.
- `===` and `==` on strings read a piece cut by `slice()` where it lies.
- `Reflect.ownKeys()` and `Object.getOwnPropertySymbols()` of objects with fields under symbols are answered from the table of key lists.
- A spread into an empty literal is filled field by field only where that lets the object go away. A spread that is kept copies its slots at once where the source is expected to have the literal's layout. An object that goes away may hold undefined or null in a field whose slot has room for them.
- The copy of a loop for arrays in storage for any value is made only where the contents are proven to be numbers.
- A list literal takes its storage from a record on the operation, and its elements are held to that storage by a required type, as the operands of every other operation are.
- Where the arms of one store meet again, one barrier at the head of that block stands for the barriers of the arms.
- A check for a type that holds any object makes no second version of a function.
- Custom accessors of built-in classes (`symbol.description`) are cached for primitive receivers, and an element that is added to a compiled object by index is defined without a descriptor.
- A string builder alone in the pool counts as the first, and one that is emptied with a large buffer makes the next buffer at the next write.
- In an async generator an await is merged into the yield in front of it only where what is proven of the values that are sent in can stand behind the switch on the mode.
- A parser by type gives up at a member whose field is an accessor, as at every field that takes no value.
- What native code puts on a class object by `putDirect()` reaches the static fields that compiled code keeps in module variables, and stands behind them in the order of keys.

### Modules

- An `import()` settles in the turn in which it settles under `bun run`: the image says which modules are loaded with the first entry point, the number of jobs of a load is exact across levels, and a further entry point is evaluated by its levels.
- A first `import()` of a synchronous ES module of the program is a promise and one job, without a record or an entry of the loader, until somebody asks for another module.
- CommonJS files and ES modules that need each other run in the order of `bun run`: a file that the loader evaluated itself takes no second step, whoever asks for the entry point while it loads hears of the load one step ahead of the program, and the loader waits for a CommonJS file that stands in front of a module on the way of a load.
- A CommonJS file that requires an ES module which imports it back, and then throws where nobody gets the error, is answered at every later `require()` with the namespace object of that import.
- Every pending `import()` reaches the loader also where a termination is asked for meanwhile, such as a `vm` timeout. The termination is thrown at the check behind the hand-over.
- `export { c } from` a module with two or more `export *` over modules without a table of names is looked up at run time.
- Deferred namespace objects list `then` and throw again what the root of their cycle threw, as `JSModuleNamespaceObject` does.
- A realm that the program creates (`ShadowRealm`) runs the host's library modules from their text.

### Guesses and facts

- What heat, an annotation or an open set says is an `Expected<T>`. It turns into neither its value nor a bool by itself, and `predicted()` marks each place that acts on it. What is true in every run is a `ProvenValue<T>`, which only `Proven` makes. A pass that takes a guess for a fact does not compile.
- Heat, the contents of collections, the callees of a call (`CalleeSet`, `CalleeList`, `CalleeCandidates`) and the layouts of a type, a test or an access (`LayoutSet`) answer in these two types.
- `--aot-heat=shuffled` draws the heat of every block, edge and function from a hash: a program means the same under any heat.

### Analysis

- Nothing is decided by where text stands in a file: blanks and comments move no generated code, and the key of a created symbol holds the number of its call in the file.
- A value that may be unspecified, such as a module variable that is read ahead of a loop, is looked at only where the program looks at it. No pass moves a test or a concatenation ahead of that place.
- A module that an `import()` loads may run while another module runs: inside a `require()` of an ES module, which runs the jobs of the loader, and inside a built-in that the table marks as running the waiting jobs. The order that flow draws between modules says so.
- A loader that is bound, called through `call` or `apply`, or handed to `Reflect.apply` is followed whatever else the callee may be. A bound function that is handed out beside library code which constructs an unknown constructor enters its target.
- The first suspension of a generator is not copied.
- The heat estimate follows reads and stores that may run accessors, also accessors laid under keys nobody knows, and counts the host as a caller only where running code may enter a function.
- Flow gives one answer in every order of its rules for four more of them.
- A name that reads as a number is kept with the elements everywhere in flow.
- A place that no program can name has a type of its own in flow, and a read under a name that a program can see says in its spelling that it gives only what was written.
- What is claimed of a field of a layout (who writes it, how often, what it holds) is computed from the writers that a ledger lists.
- The namespace object of a CommonJS file holds every own name of what the file exports.
- A name that a spread or a rest pattern brings into a layout counts as given a value there.
- Which closures a property definition compares is read from what the solver's own lookup finds on the descriptor, and one function reads what `ToPropertyDescriptor` takes from a literal.
- An element of a list literal, and a listed field that is made from a checked value, take their types from what is proven of them in every version of a function.
- What a function hands on to its generic version is read from that version's body.
- What a base class allocates for a class that extends what nobody predicted, such as the result of a call, is an instance of that class.
- An environment is not released behind a last use that never completes.

### Stack frames

- Sites that run the steps of a built-in in line name the built-in, so it is listed between a trap, a getter or a conversion and the caller.
- A function whose closures are named out of sight records its callee from the first such naming on. Compiled code gains nothing.
- Native frames under function objects of compiled code are private.
- A stack trace walks each run of compiled code once, without a visit a frame: a frame takes 200 instructions where it is shown.

### Build time

- Use lists are brought up to date from a function's log of edits.
- `InlineCalls` takes a body in at most 32 levels deep.
- Long rows of classes, stacked mixins and long chains of prototypes take fewer steps to build: lookups up a row keep their answers while the caches of the program are open, a new link of a chain wakes its readers last, and each of many heirs is asked once for what lies above it.
- 1,000 classes that each extend what a function of their own makes of one base class build in 10.7 G instructions, and the build grows in line with their number.
- Very many functions that are called at one place are put in order only where they are few enough to be told apart, and a summary that allows everything ends the walk over them.

### Checking

- Under `--aot-check=validate-references`, and in `testaot`, the finished Air of every function is read: a pointer into what a cell owns is not held across a call that may collect unless the cell is. The check also sees a pointer whose own load took over a trap, and asks at each place whether a pointer may still give cells.
- A testing word overwrites registers and spilled words whose values are not read again in front of every such call.
- A build that verifies the program verifies its records at the head of the read stage, and has four more rules: an operation whose operands all have values has one, no operation takes the slot of an accessor for the slot of a value, code is copied only to where each stand-in says as much as what it stands for, and what is proven of a value that is handed back beside a mode stands behind the mode.
- The check of native calls and the check of operations count an entry into compiled code as code that ran, also an entry by a job.
- In validating builds the last arm of a dispatch checks its type, a short form of a closure test is held against the whole test, and the quick walk of the stack is held against a visit of each frame.
- In builds with assertions an operation that is declared not to collect runs under `AssertNoGC`.
- The names of stub registers are held distinct on both ISAs in every build, and on arm64 `testaot` reads the code of every stub for a constant or a copy that is written again before it is read.
- The IR tests say the same under Bun's table of built-ins and under the stand-in host's.

### Later commits

- IR: `Entered : fact` and `AssumeType word, fact`: the read of a module variable takes the fact it relies on as an operand; a load that goes ahead of its fact has the type `T | unspecified`, and the verifier refuses every look at such a word.
- Flow: what is claimed about variables, environment fields, fields of layouts, classes and closures is a function of one ledger of writers, readers and reachers. A class of the table lists the own names of its instances or says nothing. Enumerators, native walks and copies read own properties through one function.
- Flow: an operation whose operands all have values and whose result has none ends every build with OperationWithoutValue. `--aot-flow=widened` solves twice, once with more given to drawn nodes, and compares.
- Types: LayoutSet answers all() or seen(); weights, heat, expected types and guessed layouts are Expected<>; TypeFlags::HasMembersWithoutLayout is gone.
- Loops: SeparateUnlikelyLoopPaths and HoistLoopInvariantReads move a read only where it ran on every way to the loop's entry; FunctionAnalyses counts repairs of SSA form that are pending.
- Speed: typed arrays made by from() and of() have their class, DataView.byteLength is read in place, for-of over a Buffer walks the storage, a function that returns a class is copied for each named class, index keys are stored by a stub, first reads by name are answered by the stubs' walk, a narrow slot moves so that its neighbor can be wide, the levels of a load are planned level by level.
- Run time: a Worker's start asks for room by the size of a block; the operations at the head of a handler are declared as collecting; under useZombieMode a sweep leaves pages alone that it has given back.
- Frames: `Array(n)` and `new Array(n)` are listed as the call is written; a symbol that a library module creates is a name only in library text.
- Flow: the source of a copy is read through the objects above it. Accessors of library prototype objects that the program holds escape. No listing and no retired node takes a host prototype object; the receiver of a write hands on those that come to it.
- Flow: what is claimed about fields of layouts, classes below classes and built-in locations begins with the ledger's one relation; who makes an instance is asked of the class it was defined with. A module leads to "any time" only where its own body holds an operation that may run other code.
- IR: a capture read takes the fact of the test it relies on; CallDirect says whether its callee is predicted; an `Entered` that the first turn is sure to reach goes ahead of the loop.
- Passes: what moves behind a test that restarts in the generic version begins behind the caught values of a handler. Code generation refuses a claim about storage or position that does not go back to the guard it names.
- Representations: a double read out of bytes becomes the one NaN at the read where something is computed from it.
- Keys: a symbol that a library module hands out is a key; Object.freeze() prepares its holders, JSObject::freeze(VM&) runs no code. One function makes the key of a module, whether the type of its import is written or made at run time.
- Heap: the VMs that share a range keep one last reserve for all of them.
- Speed: a class that a function returns is read as if it were written out; a class with a private field below another class makes its fields at construction; `index in object`, stores through a proxy and the text of an index are answered by stubs; the constants of a put-off initializer stand at their uses.
- Size: the compiler's Vector grows through one function.
- Testing: the report of `--aot-flow=widened` lives in testaot; the check of claims to the back end asks the layout plan what is padding.
- Flow: a read under a key counts as a read of elements only where every possible key is a number or a name that reads as one. A field that an initializer defines without a value is accounted for by the objects that reach the initializer's `this`, whether the field is declared or added late.
- Flow: the order analysis gives a frame to a closure that the function which makes it calls at one place, so what is stored inside is ordered against the code around it.
- Type tests: where only `int` of any size is known, an operator tests for int32. A value that has passed two tests in front of a loop keeps both where a later run adopts it as the proof of one.
- IR: a fact counts as the test does where closures are asked for their users.
- Frames: a method read shortly in front of its call on the same line has no site of its own; its column lies in the call's entry.
- Heap: each VM of a shared range also keeps three chunks from its first blocks on.
- B3: under a policy that only the AOT client sets, a Phi that fixSSA makes for a demoted value keeps that value's origin.
- Built-ins: an undescribed value is an instance of a class that every executable has, or of one that shares a feature with the entry that hands it out. In a file without types no parser is made for a guessed target that lacks a name read on the result. A made name of an import is answered by the image only for a type that makes data of the text.
- Size: flow's walks take their functor by reference; partition asks for its median at three places.

### Outside `aot/`

- `Source/WTF/wtf/EnumTraits.h`: the table of `enumName()` ends at the last named enumerator.
- `interpreter/Interpreter.{h,cpp}`: `getStackTrace()` takes an optional filter, so an embedder's walk ends at the last frame it shows, and `appendStackTrace()` hands each run of compiled code to one walk.
- `interpreter/StackVisitor.cpp`, `runtime/JSModuleLoader.cpp`, `runtime/JSMicrotask.cpp`, `runtime/ScriptExecutable.cpp`, `runtime/Error.cpp`: a few lines each for the frames, the load promise and the realms above.
- `runtime/JSObject.{h,cpp}`, `runtime/JSObjectInlines.h`: `putDirectInternal()` in the mode that defines an own property leaves for `putDirectToCompiledClosure()` where the object is a class object of compiled code, under `ENABLE(AOT)`.
- `runtime/ProxyObject.{h,cpp}`: under `ENABLE(AOT)`, `getWithTrap()`, `hasWithTrap()`, `setWithTrap()`, `finishHas()` and `finishSet()` for a caller that has read the trap off the handler itself.
- `heap/MarkedBlock.cpp`, `heap/PreciseAllocation.cpp`: under `useZombieMode` a sweep overwrites dead cells also where it makes no free list.
- `heap/ReservedRangeHeap.{h,cpp}`, `heap/MarkedBlockCage.cpp`: `tryEnsureRoomForNewThreads()` says whether each of so many threads that have taken nothing from the range yet can take the pieces that a VM takes at its start.
- `heap/HeapProfiler.cpp`, `inspector/ScriptCallStackFactory.h`, `tools/VMInspector.h`: an include or a declaration of what they themselves use.
- `bytecompiler/NodesCodegen.cpp`: a class member under a private name of built-in code is defined under that name.
- `b3/`: a pinned constant in a register of the other bank is read through `BitwiseCast` (arm64), an access whose trap is attributed to it takes `[base + index]` only beside a pinned base, the log of moved loads follows blocks that are split or freed, and very many stack slots get their places from live ranges.
- `heap/MarkedBlockCage.{h,cpp}`, `heap/CagedAlignedMemoryAllocator.cpp`: the VMs that share a range keep one last reserve for all of them, `tryAllocateAlignedFromSharedReserve()` takes from it where a VM has no reserve of its own, and each VM of a shared range keeps three chunks from its first blocks on.
- `heap/MarkedBlock.cpp`, `JSTests/stress/marked-block-decommitted-pages-are-not-read.js`: under `useZombieMode` a sweep leaves pages alone that it has given back; the test also runs in that mode.
- `runtime/ObjectConstructor.cpp`: under `ENABLE(AOT)`, `Object.freeze()` calls `AOT::PropertyWalk::prepareForFreeze()` ahead of the freeze.
- `b3/B3FixSSA.cpp`, `b3/B3Policy.{h,cpp}`, `b3/B3Variable.h`, `b3/testb3_bun.h`: under `keepsOriginsOfDemotedValues`, a policy that only the AOT client sets, a Phi that fixSSA makes for a demoted value keeps that value's origin.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant