memory_final's gate crashed in all 13 scenarios at the first frame of play, R3D_ARENA_CHECK=1 reading 0xDD in ludic.ui's nd_take: a node the pool keeps had come from the frame's scratch. Two holes: - a call to a generic (ui_kept(list, n)) names the generic, and the analysis knows only its instances (ui_kept$UiNode), so the callee looked unknown - and an unknown callee was taken to keep nothing. A generic's call now reaches every instance, and an unknown callee keeps everything it is handed, but for a short list of intrinsics known to keep nothing; view() shares its list's storage. - a push's growth into a parameter's list was LOCAL whenever the list was not seen kept, though a parameter may be a state's list. A site is LOCAL now only when its class is neither ESC nor HEAP. examples/lang/arena_pool.ludic is the shape (a pool keeping records across frames through a generic push): built with --arena it prints '7 3498' poisoned on every reset and with the arena off, in ludic-dev test. On the valley every ludic.ui pool site is kept; 5480 sites local, 6431 kept. Also, from ECS: a record's field defaults are stored into it when it is made, a global's initializer is kept, and a component's own functions are frame roots (they run while its page is open). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
40 lines
1.1 KiB
Text
40 lines
1.1 KiB
Text
# arena_pool.ludic - a pool that keeps its records across frames through a generic push (the shape of
|
|
# ludic.ui's nd_take and ui_kept). Built with --arena, a record the pool keeps must not come from the
|
|
# frame's scratch: run with R3D_ARENA_CHECK=1 (each reset filled with 0xDD) it prints what it prints
|
|
# with the arena off (R3D_ARENA=0) - `7 3498`.
|
|
program ArenaPool {
|
|
property Cell { v: int = 0, tag: string = "" }
|
|
property Pool { cells: []Cell = new []Cell, used: int = 0 }
|
|
function kept<T>(xs: []T, v: T) -> void { push(xs, v) }
|
|
function take(p: Pool, f: int) -> Cell {
|
|
if p.used < len(p.cells) {
|
|
let c = p.cells[p.used]
|
|
p.used += 1
|
|
return c
|
|
}
|
|
let c = new Cell { v: f }
|
|
kept(p.cells, c)
|
|
p.used += 1
|
|
return c
|
|
}
|
|
function step(p: Pool, f: int) -> int {
|
|
p.used = 0
|
|
var s = 0
|
|
for k in 0 .. (f % 8) {
|
|
let c = take(p, f)
|
|
c.v = f + k
|
|
s += c.v
|
|
}
|
|
return s
|
|
}
|
|
entry {
|
|
let p = new Pool
|
|
for f in 0 .. 500 {
|
|
step(p, f)
|
|
Mem.frame()
|
|
}
|
|
var sum = 0
|
|
for i in 0 .. len(p.cells) { sum += p.cells[i].v }
|
|
print(`{len(p.cells)} {sum}`)
|
|
}
|
|
}
|