You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A function that always returns a constructed product allocates a table per call even when every caller immediately deconstructs the result. State-shaped code pays this on every bind (a fresh Tuple per step), uncons/splitAt-style helpers pay it per element. The allocation exists only to carry two or three values across a return boundary.
Approach
The result-side twin of the #24 worker/wrapper split (GHC calls it CPR, Constructed Product Result), with Lua's native multiple return values as the unboxed-tuple representation:
-- worker returns components, no tablelocalstep_w=function(s) returna, s2end-- wrapper reboxes for callers that consume the product as a valuelocalstep=function(s)
locala, s2=step_w(s)
return { ["$ctor"] ="...Tuple", value0=a, value1=s2 }
end
At a call site that immediately deconstructs the result, the inlined wrapper's rebox meets the #177 fold and the product never materializes. The termination side condition GHC's CPR carries (botCpr, unlifted results must not hide bottom) vanishes here: evaluation is strict, so the worker either reaches its return or the program has already diverged, and returning multiple values changes nothing about that.
What it takes, and why this is deliberately parked rather than next in line:
an IR representation for multi-value results (functions currently return exactly one value);
consumption-aware rewriting of call sites: only deconstructing sites bypass the wrapper, a result flowing away as a first-class value keeps going through it;
Amulet ships exactly this shape ("unboxed tuples as multiple simultaneous Lua values"), so there is a working precedent on the same target.
Prerequisites / Relations
Builds on the #24 machinery (landed in #202) and on #177 as the rebox canceller. Parked until #198, #180 and #186 have landed and the #172 counters show what share of allocations are immediately-deconstructed products; that number decides whether the plumbing above is worth it.
Verification / Measurement
An allocation counter on a State-shaped benchmark (one Tuple per bind step today) drops to near zero table allocations per step, while eval goldens stay byte-identical. #172 registers the end-to-end effect.
Problem
A function that always returns a constructed product allocates a table per call even when every caller immediately deconstructs the result. State-shaped code pays this on every bind (a fresh
Tupleper step),uncons/splitAt-style helpers pay it per element. The allocation exists only to carry two or three values across a return boundary.Approach
The result-side twin of the #24 worker/wrapper split (GHC calls it CPR, Constructed Product Result), with Lua's native multiple return values as the unboxed-tuple representation:
At a call site that immediately deconstructs the result, the inlined wrapper's rebox meets the #177 fold and the product never materializes. The termination side condition GHC's CPR carries (botCpr, unlifted results must not hide bottom) vanishes here: evaluation is strict, so the worker either reaches its return or the program has already diverged, and returning multiple values changes nothing about that.
What it takes, and why this is deliberately parked rather than next in line:
IR/Uncurry.hs.Amulet ships exactly this shape ("unboxed tuples as multiple simultaneous Lua values"), so there is a working precedent on the same target.
Prerequisites / Relations
Builds on the #24 machinery (landed in #202) and on #177 as the rebox canceller. Parked until #198, #180 and #186 have landed and the #172 counters show what share of allocations are immediately-deconstructed products; that number decides whether the plumbing above is worth it.
Verification / Measurement
An allocation counter on a State-shaped benchmark (one
Tupleper bind step today) drops to near zero table allocations per step, while eval goldens stay byte-identical. #172 registers the end-to-end effect.