fence: every runtime call is its line's site, and site 0 comes back when it returns

A block took whatever @lp_site held when it was made. Only malloc, calloc, realloc, concat, the number
texts, the float text, a substring and Text.* set one, so every other runtime helper - intern,
Text.repeat and the string builders, the Fs, Os, unicode, uuid and crypto helpers - was charged to
whichever line had allocated last (walk 9 blamed gvk_tex_storage, m4_new, kept_push$int and
survey_op_reward for intern's 16 B copies).

- Any `call ptr @lp_*` now takes a site of its own, its kind the callee's name when no better one is
  known (intern, str_repeat, fs_list, ...). What the helper makes, in however many blocks and
  through whichever helpers it calls in turn, is that line's.
- When the call returns, @lp_site goes back to site 0, now named "(runtime) (no site) unsited": a
  block made with no site of its own says so instead of borrowing the last one.
- The ECS stores' grows and a mod's registered stores - the only allocations emitted outside
  emit_bind - take a site each.

Golden alloc_fence_sites: two lines take turns keeping memory, Text.repeat and a record, every frame
judged. Each report names its own line (25 x +32 B str_repeat at :11, 25 x +16 B new Box at :12);
the toolchain before this charges 23 of the 32 B texts to the new Box line. The other fence and arena
goldens are unchanged; ludic.base's tests pass; the game's frame ratchets are 0 on main 7cb2b164.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 00:40:28 +03:00
parent 48a8caa292
commit 6ab98292d2
8 changed files with 98453 additions and 79775 deletions

View file

@ -96,10 +96,15 @@ function emit_site(kind: pointer) -> void {
emit(` store i32 {itoa(id)}, ptr @lp_site\n`)
}
# emit_bind's hook: a call that allocates gets a site first, its kind read off the callee; when the
# emit_bind's hook: a call into the runtime gets a site first, its kind read off the callee - what the
# callee allocates, however many blocks and through whichever helpers, is that line's - and
# g_site_reset puts site 0 back once it returns, so nothing after it is charged to it. When the
# expression being emitted is a LOCAL site (25.3) it also raises @lp_want for that one call
var g_site_reset: bool = false
function fence_bind(rest: pointer) -> bool {
g_site_reset = false
if not str_starts(rest, "call ptr @lp_") { return false }
g_site_reset = true
var alloc = true
if str_starts(rest, "call ptr @lp_malloc(") { emit_site("alloc") }
else if str_starts(rest, "call ptr @lp_calloc(") { emit_site("slice data") }
@ -109,7 +114,11 @@ function fence_bind(rest: pointer) -> bool {
else if str_starts(rest, "call ptr @lp_fp_str(") { emit_site("string(x)") }
else if str_starts(rest, "call ptr @lp_str_slice(") { emit_site("substring") }
else if str_starts(rest, "call ptr @lp_text_") { emit_site("Text"); alloc = false }
else { alloc = false }
else if str_starts(rest, "call ptr @lp_intern(") { emit_site("intern"); alloc = false }
else {
emit_site(fence_callee(rest)) # any other runtime helper: named by the callee
alloc = false
}
if alloc and g_arena and g_es_walked and g_es_node != null {
emit(" store i8 1, ptr @lp_want\n")
return true
@ -117,6 +126,14 @@ function fence_bind(rest: pointer) -> bool {
return false
}
# "call ptr @lp_fs_read(ptr %a)" -> "fs_read"
function fence_callee(rest: pointer) -> pointer {
let at = len("call ptr @lp_")
var e = at
while e < len(rest) and not (rest[e] == '(') { e += 1 }
return rest[at..e]
}
function fence_int(s: pointer) -> int {
var n = 0
var neg = false
@ -227,8 +244,8 @@ function emit_fence_runtime() -> void {
emit_fence_control()
emit_fence_report()
emit_fence_frame()
let unknown = emit_str_const("?")
g_site_fn[0] = unknown; g_site_file[0] = unknown; g_site_kind[0] = unknown
# site 0 is what allocates with no site of its own: every sited call puts it back when it returns
g_site_fn[0] = emit_str_const("(runtime)"); g_site_file[0] = emit_str_const("(no site)"); g_site_kind[0] = emit_str_const("unsited")
let n = len(g_site_line)
let sn = itoa(n)
var dflt = itoa(g_fence_mode)