frame allocs: a local shadowing a function is no edge, the drain's calls are no edges, HEAP keeps only a growth off the arena, and the arena starts at its first use

- the call graph is by name, and a local or a parameter named as a function (a float bd, part, bx)
  linked to that function: a name the function binds itself is never an edge now.
- drain_actions, generated, calls every reducer; a reducer is reached from its action's dispatch,
  so the drain's calls are not edges.
- a fresh value flowing into a local that also holds kept memory is still the frame's (storing it
  anywhere kept would have made it ESC): HEAP now keeps only a push's growth off the arena.
- the arena starts at its first use rather than at the first frame mark, so boot's temporaries are
  scratch too - dead once the Start handlers return - and a scratch site is never a birth.
Plus ludic.hints' rail and three of ludic.update's one-off lines declared. The arena goldens pass
poisoned. Maroon Lake (d79d189f): 39/11/66 -> 30/9/0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 20:23:28 +03:00
parent 8228597ade
commit d483c9283c
9 changed files with 62846 additions and 61736 deletions

View file

@ -147,6 +147,15 @@ function es_type(e: Node) -> pointer {
}
return null
}
# LOCAL: never kept (ESC); and for a push's growth, whose buffer is its list's, the list may not be
# one that outlives the frame (HEAP) either. A fresh value that flows into a local also holding kept
# memory is still the frame's - storing it anywhere kept would have made it ESC
function es_local_class(c: int, grow: bool) -> bool {
if (g_es_flag[c] & ES_ESC) != 0 { return false }
if grow and (g_es_flag[c] & ES_HEAP) != 0 { return false }
return true
}
# a value's class, or -1 when its type holds no reference
function es_ref(e: Node) -> int {
let v = es_val(e)
@ -655,7 +664,7 @@ function escape_analyse() -> void {
let spans = fi < 0 or g_es_fspans[fi]
# LOCAL only when it can neither be kept (ESC) nor share storage with what is (HEAP): a push's
# growth into a parameter's list is the list's own buffer, and the list may be a state's
if c >= 0 and (g_es_flag[c] & (ES_ESC | ES_HEAP)) == 0 and not spans {
if c >= 0 and es_local_class(c, g_es_site_grow[s]) and not spans {
n.uns = ES_SCRATCH
g_es_local_n += 1
} else {
@ -674,7 +683,7 @@ function escape_analyse() -> void {
let c2 = g_es_site_cls[u]
let fi2 = g_es_site_fn[u]
let spans2 = fi2 < 0 or g_es_fspans[fi2]
if n2.uns == ES_SCRATCH and (c2 < 0 or (g_es_flag[c2] & (ES_ESC | ES_HEAP)) != 0 or spans2) { n2.uns = 0 }
if n2.uns == ES_SCRATCH and (c2 < 0 or not es_local_class(c2, g_es_site_grow[u]) or spans2) { n2.uns = 0 }
u += 1
}
}