Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions bench/goldens/fnew_Bench.RefLoop.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
chunk: Bench.RefLoop.lua
runtime: LuaJIT 2.1.1741730670
main-chunk FNEW: 8
function-body FNEW: 12
total FNEW: 20
prototypes: 21
function-body FNEW sites:
Bench.RefLoop.lua:6
Bench.RefLoop.lua:6
Bench.RefLoop.lua:9
Bench.RefLoop.lua:9
Bench.RefLoop.lua:17
Bench.RefLoop.lua:16
Bench.RefLoop.lua:15
Bench.RefLoop.lua:19
Bench.RefLoop.lua:20
Bench.RefLoop.lua:28
Bench.RefLoop.lua:27
Bench.RefLoop.lua:41
8 changes: 8 additions & 0 deletions bench/goldens/tnew_Bench.RefLoop.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
chunk: Bench.RefLoop.lua
runtime: LuaJIT 2.1.1741730670
main-chunk TNEW+TDUP: 5
function-body TNEW+TDUP: 1
total TNEW+TDUP: 6
prototypes: 21
function-body TNEW+TDUP sites:
Bench.RefLoop.lua:19 TDUP
10 changes: 10 additions & 0 deletions bench/goldens/trace_ref_loop.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
spec: ref_loop
runtime: LuaJIT 2.1.1741730670
workload: n=100000 reps=4 result=4999950000
aborts (distinct site -- reason):
Bench.RefLoop.lua:41 -- NYI: bytecode FNEW
bytecode end state (J*=compiled, I*=blacklisted):
Bench.RefLoop.lua:11 JFUNCF
Bench.RefLoop.lua:35 JFORI
Bench.RefLoop.lua:35 JFORL
counts: aborts=1 compiled=3 blacklisted=0
15 changes: 15 additions & 0 deletions bench/macro/ref_loop.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- A hot ST loop accumulating through a non-escaping local STRef. Unboxed
-- (issue #239) the loop body is plain arithmetic on a Lua local; boxed,
-- every iteration allocates the modify record and indexes the cell table.
return {
artifact = "Bench.RefLoop",
n = 100000,
drive = function(mod, n)
return mod.run(n)
end,
ideal = function(n)
local acc = 0
for i = 0, n - 1 do acc = acc + i end
return acc
end,
}
43 changes: 43 additions & 0 deletions changelog.d/20260727_150000_unisay_unbox_ref_cells.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
### Added

- Non-escaping `Ref`/`STRef` cells unbox to plain mutable Lua locals
(#239). A cell compiles to a one-field heap table — `new v` allocates
`{value = v}`, every `read`/`write`/`modify` pays a field access — yet
a Lua local captured by inner closures is itself a shared mutable slot
(an upvalue), so when the cell never flows anywhere as a whole value
the table buys nothing. A `Let`-bound run of `new` whose every use is
a recognised operation now lowers to `local r = v`, reads to `r`, and
writes/modifies to assignments, with a literal `modify` function
beta-reduced at emission so its `{state, value}` record is never
allocated. An ST loop accumulator previously compiled to

```lua
local acc = Control_Monad_ST_Internal_new(0)()
for i = 0, n + 1 - 1 do
Control_Monad_ST_Internal_modifyImpl(function(s_S_0)
local sPrime_S_0 = s_S_0 + i
return { state = sPrime_S_0, value = sPrime_S_0 }
end)(acc)()
end
return Control_Monad_ST_Internal_read(acc)()
```

now becomes allocation-free straight-line code:

```lua
local acc = 0
for i = 0, n + 1 - 1 do
local s_S_0 = acc
local sPrime_S_0 = s_S_0 + i
acc = sPrime_S_0
end
return acc
```

Recognition is by qualified name over the cell primitives of
`Effect.Ref` and `Control.Monad.ST.Internal` plus the ST functor's
foreign `map_` (the shape `void (modify f r)` inlines to), and only
run positions lower. A cell used as a first-class value anywhere —
stored, returned, passed to an unknown function — keeps its boxed
form, the soundness guard pinned by `Golden.RefUnbox.Test` (see
`Language.PureScript.Backend.Lua.RefUnbox`).
Loading