a program's states made on first injection, and the ECS stores started at 8 slots
Maroon Lake's launcher watcher - a process that only spawns the game and waits - held 58 MB, 48 MB of it MALLOC_SMALL. Measured with malloc stack logging it was not the state defaults but L_grow: every program sized every property type's per-entity store to MAX_ENT (1024) slots at start, 1,583 stores, entities or none. And every state record was made with its defaults in L_init_globals before Boot. - emit_lazy.ludic: a program's (not the runtime's) state global is left out of L_init_globals, and every read of it calls @S_<global>(), which makes it on first call from its own initializer - after every registry and plain global, so a default may read them (the init-order crash cannot come back through a state). What a getter makes is declared (@lp_fdecl): a state first touched in play is made once and not judged as a frame's keep. A function value's trampoline calls the getter too. - L_grow starts the stores at ECS_FIRST (8) and doubles as entities come, as it always did past MAX_ENT. The watcher (with the game's watch step moved before the systems' defs): 57 MB -> 11 MB; the only state it makes is UiState (14 KB). What is left: AppKit's window, opened by rt_init before main for any windowed program (~4 MB), and the runtime's font, image and 2D inits (~1.2 MB). Goldens: the arena, fence, value and json goldens; the 36 ui examples; 30 of the 32 ECS test cases (sprite_render and sprite_atlas time out under a plain runner with the toolchain before this too). Package tests: ludic.base, save, settings, i18n. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
90b23e11e9
commit
12fdc0717e
8 changed files with 57983 additions and 57067 deletions
50
selfhost/backend/emit_lazy.ludic
Normal file
50
selfhost/backend/emit_lazy.ludic
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# emit_lazy.ludic - a program's state made when it is first injected, not before Boot: a process that
|
||||
# takes a short path through the program (the launcher's watcher, which only spawns and waits) never
|
||||
# makes the states it does not touch - the camera's frame buffers, the town's tables, the pools.
|
||||
# @S_<global>() is the getter every read of a state global calls: made on first call, by its own
|
||||
# initializer, after every registry and plain global (L_init_globals) - so a default may read them.
|
||||
# What it makes is declared (@lp_fdecl): a state first touched in play is made once, not kept per frame.
|
||||
function is_lazy_state(d: Node) -> bool {
|
||||
if d == null or d.kind != N_VAR or d.uns != 1 or d.a == null { return false }
|
||||
return str_prefix(d.s, "state$") and is_state_ty(d.ty) and not is_runtime_file(d.file)
|
||||
}
|
||||
|
||||
function emit_state_getters() -> void {
|
||||
var i = 0
|
||||
while i < len(prog) {
|
||||
if is_lazy_state(prog[i]) { emit_state_getter(prog[i]) }
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
function emit_state_getter(d: Node) -> void {
|
||||
let name = `S_{d.s}`
|
||||
fence_enter(name)
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
|
||||
ret_ty = "ptr"
|
||||
let fbody = buf_new()
|
||||
falloc = buf_new()
|
||||
let saved = code
|
||||
code = fbody
|
||||
g_err_file = d.file
|
||||
g_err_line = d.line
|
||||
emit(` %p0 = load ptr, ptr @g_{d.s}\n %z0 = icmp eq ptr %p0, null\n br i1 %z0, label %make, label %have\nhave:\n ret ptr %p0\nmake:\n`)
|
||||
emit(" %decl.in = atomicrmw add ptr @lp_fdecl, i32 1 monotonic\n")
|
||||
let v = emit_expr(d.a)
|
||||
let cv = coerce_code(v, d.ty)
|
||||
emit(` store ptr {cv}, ptr @g_{d.s}\n`)
|
||||
emit(" %decl.out = atomicrmw sub ptr @lp_fdecl, i32 1 monotonic\n")
|
||||
emit(` ret ptr {cv}\n`)
|
||||
code = saved
|
||||
emit(`define ptr @{name}() {{\nentry:\n`)
|
||||
emit(buf_str(falloc))
|
||||
emit(" br label %start\nstart:\n")
|
||||
emit(buf_str(fbody))
|
||||
emit("}\n\n")
|
||||
}
|
||||
|
||||
# a read of a state global: its getter's answer, or null when it is not lazy
|
||||
function lazy_state_read(g: Node, s: pointer) -> pointer {
|
||||
if not is_lazy_state(g) { return null }
|
||||
return emit_bind(`call ptr @S_{s}()`)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue