Problem
Codegen-level rewrites can strand module-scope bindings that nothing reads anymore. The IR-level dead-code elimination runs before code generation, so a binding it kept alive can lose its last reference when a later lowering rewrites the use sites away: the native-loop lowering (#233) absorbs loop-combinator calls, and the cell unboxing (#239) absorbs read/write/modify calls and drops the discarded unit value of the void wrapper. The emitted chunk then still carries the accessor binding — and the module-scope table M it lives in — as pure load-time dead weight. From test/ps/output/Golden.NativeLoopsST.Test/golden.lua after #239, where M.Data_Unit_unit has zero remaining reads in the whole artifact:
local M = {}
local Data_Unit_foreign = { unit = {} }
M.Data_Unit_unit = Data_Unit_foreign.unit
The same artifact previously bound local Data_Unit_unit = … and read it once per loop iteration; after the unboxing the read is gone but the binding (and now the M table that exists only to hold it) remains.
Approach
A Lua-level sweep after code generation and the storage passes: count references to each module-scope binding (both the M.name field form and the localized local name form) in the finished chunk, drop assignments whose target is never read, and drop the local M = {} declaration itself when no field of it survives. The reference counting must treat the module export table (the final return { … }) and entry-point call as roots. Repeat to a fixpoint, since dropping one binding can orphan the bindings only it referenced.
Relations
Surfaced by #239 (cell unboxing) and applies equally to shapes left by #233 (native loops). Purely an output-cleanliness/load-time win; steady-state performance is unaffected.
Verification
The Golden.NativeLoopsST.Test, Golden.MixedDiscardFloat.Test and Golden.RefUnbox.Test goldens lose their stranded accessor lines (and, where nothing else uses it, the local M = {} header); eval goldens are byte-identical.
Problem
Codegen-level rewrites can strand module-scope bindings that nothing reads anymore. The IR-level dead-code elimination runs before code generation, so a binding it kept alive can lose its last reference when a later lowering rewrites the use sites away: the native-loop lowering (#233) absorbs loop-combinator calls, and the cell unboxing (#239) absorbs
read/write/modifycalls and drops the discardedunitvalue of thevoidwrapper. The emitted chunk then still carries the accessor binding — and the module-scope tableMit lives in — as pure load-time dead weight. Fromtest/ps/output/Golden.NativeLoopsST.Test/golden.luaafter #239, whereM.Data_Unit_unithas zero remaining reads in the whole artifact:The same artifact previously bound
local Data_Unit_unit = …and read it once per loop iteration; after the unboxing the read is gone but the binding (and now theMtable that exists only to hold it) remains.Approach
A Lua-level sweep after code generation and the storage passes: count references to each module-scope binding (both the
M.namefield form and the localizedlocal nameform) in the finished chunk, drop assignments whose target is never read, and drop thelocal M = {}declaration itself when no field of it survives. The reference counting must treat the module export table (the finalreturn { … }) and entry-point call as roots. Repeat to a fixpoint, since dropping one binding can orphan the bindings only it referenced.Relations
Surfaced by #239 (cell unboxing) and applies equally to shapes left by #233 (native loops). Purely an output-cleanliness/load-time win; steady-state performance is unaffected.
Verification
The
Golden.NativeLoopsST.Test,Golden.MixedDiscardFloat.TestandGolden.RefUnbox.Testgoldens lose their stranded accessor lines (and, where nothing else uses it, thelocal M = {}header); eval goldens are byte-identical.