mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
The backend gives every function that returns a complete record literal a result-slot entry, `%into.<fn>(...params, dst)`, which stores the literal's fields into caller frame storage. The cycle analysis counted those stores as writes after construction, so a record type that refers to itself and is never written after construction carried the cycle collector's header, its live count and its candidate checks in release builds, even when nothing called the entry. Dev builds, which add no entries, left it acyclic. The destination is frame storage of a caller that only projects the value (stack-objects.ts): no object can point to it, so its fields cannot lie on a cycle. computeCycleMutability now skips those stores, under the same condition emit-statements.ts already uses to emit them as initializations (resultSlotStore in the new result-slots.ts). Every other store still makes its field mutable. The same facts mark such fields as never replaced, so reads of them borrow instead of retaining. A program that rebuilds a tree of 2^21 - 1 object literals 12 times runs in 1.19 s instead of 1.35 s and peaks at 68 MB instead of 84 MB: each node is 32 bytes instead of 40.