globals initialized in the order their initializers need each other
A state's field default reading a registry (jn_life_sp: []int = jn_life_species_new(), which reads
Species[sp].population) ran before the registry was filled, because globals were initialized in
declaration order: every gate scenario crashed in L_init_globals. Each global's initializer is now
followed - through the functions it calls and a record's field defaults - to the globals it reads,
and those are initialized first (a depth-first post-order; the source order kept between globals
that need nothing of each other, and in a cycle). Putting every state last is not enough: some
tables read a state's instance too. A test (a state whose default reads a registry declared after
it) crashes on d483c92 and prints '2 4' now; Maroon Lake's headless game loads and plays 180 frames.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d483c9283c
commit
4d3ecfb72c
3 changed files with 57485 additions and 54871 deletions
|
|
@ -77,8 +77,13 @@ function emit_init_fn(name: pointer, runtime: bool) -> void {
|
|||
falloc = buf_new()
|
||||
let saved = code
|
||||
code = fbody
|
||||
var i = 0
|
||||
while i < len(prog) {
|
||||
# in the order their initializers need each other: a state's field default may read a registry
|
||||
# (Species[sp].population) and a table may read a state's instance, whatever order they were declared in
|
||||
let order = gi_order()
|
||||
var oi = 0
|
||||
while oi < len(order) {
|
||||
let i = order[oi]
|
||||
oi += 1
|
||||
let d = prog[i]
|
||||
if d.kind == N_VAR and global_needs_init_code(d) and is_runtime_state_var(d) == runtime {
|
||||
# an initializer is the global's own file's code: its errors, and what its module
|
||||
|
|
@ -96,7 +101,6 @@ function emit_init_fn(name: pointer, runtime: bool) -> void {
|
|||
let cv = coerce_code(v, d.ty) # any conversion is its own line, before the store
|
||||
emit(" store "); emit(llty(d.ty)); emit(" "); emit(cv); emit(", ptr @g_"); emit(d.s); emit("\n")
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
emit(" ret void\n")
|
||||
code = saved
|
||||
|
|
@ -355,3 +359,103 @@ function emit_str_slice() -> void {
|
|||
emith(" %op = getelementptr inbounds i8, ptr %out, i32 %i\n store i8 %c, ptr %op\n %i1 = add i32 %i, 1\n br label %loop\n")
|
||||
emith("fin:\n %tp = getelementptr inbounds i8, ptr %out, i32 %len\n store i8 0, ptr %tp\n ret ptr %out\n}\n")
|
||||
}
|
||||
|
||||
# ---- the order globals are initialized in ----------------------------------------------------
|
||||
# each global's initializer, followed through the functions it calls and a record's field defaults,
|
||||
# names the globals it reads; they are initialized first (a depth-first post-order, the source order
|
||||
# kept between globals that need nothing of each other, and where a cycle leaves no order)
|
||||
var g_gi_idx_k: []pointer = new []pointer
|
||||
var g_gi_idx_v: []Node = new []Node
|
||||
var g_gi_fn_seen: []pointer = new []pointer
|
||||
var g_gi_state: []int = new []int # 0 not visited, 1 on the path, 2 placed
|
||||
var g_gi_out: []int = new []int
|
||||
|
||||
function gi_index(name: pointer) -> int {
|
||||
let h = ck_tab_get(g_gi_idx_k, g_gi_idx_v, name)
|
||||
if h == null { return -1 }
|
||||
return h.ival
|
||||
}
|
||||
|
||||
function gi_fn_seen(name: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(g_gi_fn_seen) { if (g_gi_fn_seen[i] == name) { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
# the globals an expression reads, into out, through calls and defaults (each function once per global)
|
||||
function gi_reads(n: Node, out: []int) -> void {
|
||||
if n == null { return }
|
||||
if (n.kind == E_ID or n.kind == E_FNREF) and n.s != null {
|
||||
let k = gi_index(n.s)
|
||||
if k >= 0 {
|
||||
let d = prog[k]
|
||||
if d.kind == N_VAR { push(out, k) }
|
||||
if d.kind == N_FN and not gi_fn_seen(n.s) {
|
||||
push(g_gi_fn_seen, n.s)
|
||||
gi_reads(d.a, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if n.kind == E_CALL and n.a != null and n.a.kind == E_ID and n.a.s != null {
|
||||
let k = gi_index(n.a.s)
|
||||
if k >= 0 and prog[k].kind == N_FN and not gi_fn_seen(n.a.s) {
|
||||
push(g_gi_fn_seen, n.a.s)
|
||||
gi_reads(prog[k].a, out)
|
||||
}
|
||||
}
|
||||
if n.kind == E_NEW and n.s != null {
|
||||
let lay = find_comp(n.s)
|
||||
if lay != null and lay.kids != null {
|
||||
var f = 0
|
||||
while f < len(lay.kids) { gi_reads(lay.kids[f].a, out); f += 1 }
|
||||
}
|
||||
}
|
||||
gi_reads(n.a, out)
|
||||
gi_reads(n.b, out)
|
||||
gi_reads(n.c, out)
|
||||
if n.kids != null {
|
||||
var i = 0
|
||||
while i < len(n.kids) { gi_reads(n.kids[i], out); i += 1 }
|
||||
}
|
||||
}
|
||||
|
||||
function gi_visit(i: int) -> void {
|
||||
if g_gi_state[i] != 0 { return }
|
||||
g_gi_state[i] = 1
|
||||
let d = prog[i]
|
||||
if d.a != null {
|
||||
let needs = new []int
|
||||
g_gi_fn_seen = new []pointer
|
||||
gi_reads(d.a, needs)
|
||||
var k = 0
|
||||
while k < len(needs) {
|
||||
if needs[k] != i and g_gi_state[needs[k]] == 0 { gi_visit(needs[k]) }
|
||||
k += 1
|
||||
}
|
||||
}
|
||||
g_gi_state[i] = 2
|
||||
push(g_gi_out, i)
|
||||
}
|
||||
|
||||
function gi_order() -> []int {
|
||||
if len(g_gi_out) > 0 { return g_gi_out }
|
||||
ck_tab_init(g_gi_idx_k, g_gi_idx_v)
|
||||
g_gi_state = new []int
|
||||
var i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
push(g_gi_state, 0)
|
||||
if (d.kind == N_VAR or d.kind == N_FN) and d.s != null and ck_tab_get(g_gi_idx_k, g_gi_idx_v, d.s) == null {
|
||||
let h = new Node
|
||||
h.ival = i
|
||||
ck_tab_put(g_gi_idx_k, g_gi_idx_v, d.s, h)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
i = 0
|
||||
while i < len(prog) {
|
||||
if prog[i].kind == N_VAR { gi_visit(i) }
|
||||
i += 1
|
||||
}
|
||||
return g_gi_out
|
||||
}
|
||||
|
|
|
|||
56139
selfhost/ludicc.seed.ll
56139
selfhost/ludicc.seed.ll
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue