region rule (25.3c): keep() and intern(), frame_keeps, and --arena-strict
keep(x) copies a string, a slice (header and elements) or a record (shallow) onto the heap; intern(s) hands back one heap string per distinct text from a fixed table in the runtime (FNV-1a, 65536 slots, copied the first time; past 49152 only copied). Both are how frame code keeps what it made on purpose: the escape analysis takes the copy as the heap's and leaves the argument LOCAL. The analysis now records why a class escapes (the store, the event, the global it reached) and ludic deps lists every allocation frame code makes and keeps - fkeep lines, 'ludic deps --keeps', the frame_keeps number --check ratchets - leaving out what is under @alloc_ok and a push's growth (25.5's capacities). --arena-strict (or 'arena strict') makes each an error naming the store, before anything is emitted. A test: a template stored into a state is the one error; keep and intern of the next two, an @alloc_ok push and a scratch temporary are not; 195 frames of arena resets under R3D_ARENA_CHECK=1 later the kept and interned texts read as made, and intern gives the same string. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
cd71da9330
commit
a0b030290b
10 changed files with 80528 additions and 76677 deletions
|
|
@ -190,4 +190,40 @@ function deps_frame(f: pointer) -> void {
|
|||
i += 1
|
||||
}
|
||||
deps_line(f, `frame_allocs {itoa(total)}`)
|
||||
deps_keeps(f, seen)
|
||||
}
|
||||
|
||||
var g_arena_strict: bool = false # --arena-strict / `arena strict`: a keep in frame code is an error
|
||||
# 25.3's region rule: an allocation in frame code that is kept past its frame (stored into a state,
|
||||
# a queue, a global, an event) is a `fkeep` line - and under strict an error naming the store.
|
||||
# keep(...) / intern(...) is the copy on purpose; @alloc_ok("why") says it is bounded
|
||||
function deps_keeps(f: pointer, seen: []bool) -> void {
|
||||
escape_analyse()
|
||||
var n = 0
|
||||
var s = 0
|
||||
while s < len(g_es_site) {
|
||||
let c = g_es_site_cls[s]
|
||||
let fi = g_es_site_fn[s]
|
||||
if c >= 0 and fi >= 0 and (g_es_flag[c] & ES_ESC) != 0 and not g_es_site_aok[s] and not g_es_site_grow[s] {
|
||||
let d = g_es_fnode[fi]
|
||||
let k = dr_index(d.s)
|
||||
if k >= 0 and seen[k] and not fr_alloc_ok(d.s) and d.file != null and not is_runtime_file(d.file) {
|
||||
let site = g_es_site[s]
|
||||
let why = g_es_why[c]
|
||||
var at = "?"
|
||||
if why != null and why.file != null { at = `{why.file}:{itoa(why.line)}` }
|
||||
var kind = fr_alloc_kind(site, false)
|
||||
if kind == null { kind = "text" }
|
||||
deps_line(f, `fkeep {kind} {d.file}:{itoa(site.line)} {at} {fr_chain(k)}`)
|
||||
if g_arena_strict {
|
||||
let m = `{d.file}:{itoa(site.line)}: error: frame code keeps what it makes: this {kind} is kept at {at} ({fr_chain(k)}) - keep(...) or intern(...) to copy it on purpose, or @alloc_ok("why")\n`
|
||||
file_write(file_stderr(), m, len(m))
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
}
|
||||
s += 1
|
||||
}
|
||||
deps_line(f, `frame_keeps {itoa(n)}`)
|
||||
if g_arena_strict and n > 0 { exit(1) }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue