Repository navigation
Borrow and move references whose owners provably outlive them - #848
Merged
Merged
Conversation
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
cramforce
force-pushed
the
perf/static-immutability
branch
from
October 11, 2026 03:45
2ae8459 to
6ddaa2a
Compare
ReferenceEffects answers one bit per function: does anything remove any heap edge? Any reference store in the transitive callees (including a constructor's stores into its own fresh `this` behind every `new`, a closure call, or atomics in symbol-id allocation) disabled borrowing of field-read and rebound-local arguments, walk locals, borrowed returns and loop element borrows. backend/llvm/reference-writes.ts keeps the location instead: per-function transitive write sets over class fields (keyed by hierarchy root), record fields, array elements, map entries, boxes, globals and the current function's own locals, with key 0 as an unknown heap write. Locals stay exact under unknown writes: no callee can rebind a caller's unboxed local. Constructor stores into their own `this` drop out of `new`/`super` summaries (checked: constructors run only through `new` and `super`). Indirect calls resolve closure targets through function-typed parameters, captures and adapters, otherwise the union of all closure bodies; runtime library families that run no user code count as inert. Uses: - arguments of borrowed parameters (direct, `new`, virtual) and of projection-only array-read parameters borrow when neither the callee nor a later argument writes a location they depend on; - stable locals initialized by a frame-unowned projection borrow when the rest of their block writes none of its locations; optional element reads into projection-only locals likewise; - loop element borrows need only an iteration that writes no array element (nor the array's binding); - walk locals and borrowed returns are admitted in bodies whose whole write set misses the walked locations. tsc-ts e469e33 self-check (single-threaded, executed, non-null and non-immortal RC operations in emitted code, measurement build): 505.3M -> 307.6M (-39%) before return moves (next commit). Output identical. Build time unchanged (98.5 s vs 99.9 s, Linux).
`return x` of a local whose slot is an owned scope entry of the return type loaded the value, retained it for the caller and released the local at scope exit. Outside finally regions the return now takes over the local's reference: no retain, and the local's entry is skipped by the return's releases. tsc-ts e469e33 self-check, executed non-null non-immortal RC operations in emitted code: 320.2M -> 307.6M (-3.9%) on top of the previous commit.
Safe index reads of possibly-missing elements lower to lifted helpers (%arr.idxOr.N) that returned an owned element. Walks now admit required element reads of walked arrays (an index whose computation removes no edge), so these helpers qualify as borrowed returns: their body lends the element its array keeps alive (borrowing the optional read directly), and receivers consume the call's result without a retain/release pair, as for any borrowed-return call. Paged lookups such as `this.pages[id >> 8]?.[id & 255]` no longer retain the page array. Corpus 4630-4633 renumbered from 4620-4623 (checkerhot uses 4620/4621); 4634 pins paged lookups and a read followed by a removal.
…ubset The natively compiled compiler rejected compound assignments to typed-array elements, Uint32Array.some, a chained Set.add and a structural Pick of ImmutableFields; bit operations now go through small helpers. The dense reference-read test truncates its array first: a bare element return is now lent as a borrowed return.
cramforce
force-pushed
the
perf/rc-elision-si
branch
from
October 11, 2026 03:45
9dfbe1d to
0b74483
Compare
Two expectations predate the reference-write analysis and return moves on this branch. `keep(item, items) { items.push(item); return item; }` now qualifies as a borrowed-return function: its write set (array elements) misses the walked parameter, so the body retains only the appended element, returns the parameter +0 while the caller still owns it, and the owned adapter retains the result before releasing its parameters. In `snapshot`, `return saved` moves the saved alias's owned scope entry into the result rather than retaining it again and releasing the local at scope exit, leaving one retain.
… element The reference-write analysis borrows an argument when neither the callee nor a later argument writes a location it depends on. A later argument that only throws a fresh Error writes no array element, so `read(items[0], fail())` now borrows the element from the borrowed parameter's array and has nothing to release on any edge; the test pins that. The exception-edge release is now exercised by a later argument that pops the array before throwing, which still retains the snapshot and releases it before the exceptional return.
# Conflicts: # packages/compiler/test/ts7/baselines/order-parity.json
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Borrow references whose locations nothing in their extent can write
Commits:
3be18844,318038e4(self-hosted subset fix),caa7096d(TS baselines).Borrowing decisions so far asked
ReferenceEffectsone question per function: does anything in it (or its callees) remove any heap edge? In tsc-ts almost every interesting callee does somewhere (a cache store, a constructor initializing its own fresh object, a closure call), so field-read arguments, rebound locals, walk locals, borrowed returns and loop elements were retained and released around most calls.backend/llvm/reference-writes.tscomputes, per function and transitively, which reference locations can be overwritten or removed: class fields (keyed by hierarchy root), record fields, array elements, map/set entries, boxes, globals and the current function's own locals, plus one "unknown heap write" location that never covers a local (no callee can rebind a caller's unboxed local). Constructor stores into their ownthisare excluded fromnew/supersummaries (verified: constructors run only throughnewandsuper). Indirect calls resolve their closure targets through function-typed parameters, captures and adapter helpers. Fields thatImmutableFields(#838) proves construction-only add no location outside constructors.A borrowed read is valid while nothing in its extent writes a location it depends on. Uses:
new, virtual calls) and projection-only array-read arguments: the callee and the later arguments;const t = node.type), and optional element reads into locals: the rest of their block;Measurements (tsc-ts e469e33, Linux 8 vCPU, interleaved medians; "st" = single-threaded, "c8" =
--checkers 8), whole stack on top of #838:Executed RC operations in emitted code (self-check, non-null and non-immortal): 452.8M -> 252.9M (-44%). Binary 22.96 -> 22.37 MB. Build time unchanged. Runtime suite (
--layouts=4 --runs=16) vs #838: geomean -1.8%, no workload slower, sizes unchanged or smaller.Corpus: 4630 (call arguments), 4631 (aliases), 4632 (loops and walks), 4633 (callbacks), each with cases where the overwritten field or element is the value's last owner.
Move owned locals into return values (
95ecb6d9)return xof an owned local loaded, retained and then released the local at scope exit. Outside finally regions the return now takes the local's reference. -3.9% executed RC operations on the self-check. Corpus 4633.Lend optional element reads as borrowed returns (
a225df63)The lifted helpers for
arr[i]reads ofT | undefinedelements (%arr.idxOr.N) returned an owned element. Walks now admit required element reads, so the helpers become borrowed returns (their body borrows the optional read directly) and receivers consume them without a retain/release, like any borrowed-return call. Removes the page-array retain in paged lookups such asthis.pages[id >> 8]?.[id & 255](checkerhot's handoff; e469e33 has no paged stores, so the self-check counts barely move). Corpus 4634.Stacked on #838 (static immutability): builds on its construction-only facts.