Merge branch 'lang/memory-fnref' into lang/foundations
This commit is contained in:
commit
dc614a6a33
6 changed files with 62658 additions and 61981 deletions
25
examples/lang/arena_fnval.ludic
Normal file
25
examples/lang/arena_fnval.ludic
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# arena_fnval.ludic - a record made by a function called through a function value (a factory in a
|
||||||
|
# field, as ludic.ui's UiClass.make is) and kept by a pool across frames. The analysis cannot see
|
||||||
|
# that call, so a function taken as a value keeps what it returns: built with --arena and poisoned on
|
||||||
|
# every reset it prints `5 1053`, as the heap does.
|
||||||
|
program ArenaFnval {
|
||||||
|
property Item { v: int = 0, name: string = "" }
|
||||||
|
property Maker { make: fn(int) -> Item = null }
|
||||||
|
property Pool { items: []Item = new []Item }
|
||||||
|
function item_new(f: int) -> Item { return new Item { v: f, name: `item {f}` } }
|
||||||
|
function step(p: Pool, m: Maker, f: int) -> void {
|
||||||
|
if f % 100 == 3 { push(p.items, m.make(f)) }
|
||||||
|
}
|
||||||
|
entry {
|
||||||
|
let p = new Pool
|
||||||
|
let m = new Maker
|
||||||
|
m.make = fn item_new
|
||||||
|
for f in 0 .. 500 {
|
||||||
|
step(p, m, f)
|
||||||
|
Mem.frame()
|
||||||
|
}
|
||||||
|
var sum = 0
|
||||||
|
for i in 0 .. len(p.items) { sum += p.items[i].v + len(p.items[i].name) }
|
||||||
|
print(`{len(p.items)} {sum}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ var g_es_aok: int = 0 # inside a statement under @
|
||||||
var g_es_site_aok: []bool = new []bool # the site is (not reported by the region rule)
|
var g_es_site_aok: []bool = new []bool # the site is (not reported by the region rule)
|
||||||
var g_es_site_grow: []bool = new []bool # the site is a push's growth (25.5's capacities, not the rule)
|
var g_es_site_grow: []bool = new []bool # the site is a push's growth (25.5's capacities, not the rule)
|
||||||
var g_es_done: bool = false
|
var g_es_done: bool = false
|
||||||
|
var g_es_fnrefs: []pointer = new []pointer # functions taken as values (fn f): called from anywhere
|
||||||
var g_es_consumed: []Node = new []Node # text used up where it is made (a + or == operand): freed at once
|
var g_es_consumed: []Node = new []Node # text used up where it is made (a + or == operand): freed at once
|
||||||
var g_es_ft: []int = new []int # flow edges
|
var g_es_ft: []int = new []int # flow edges
|
||||||
var g_es_ff: []int = new []int
|
var g_es_ff: []int = new []int
|
||||||
|
|
@ -175,7 +176,11 @@ function es_is_mem_frame(n: Node) -> bool {
|
||||||
function es_val(e: Node) -> int {
|
function es_val(e: Node) -> int {
|
||||||
if e == null { return -1 }
|
if e == null { return -1 }
|
||||||
let k = e.kind
|
let k = e.kind
|
||||||
if k == E_STR or k == E_INT or k == E_BOOL or k == E_FLOAT or k == E_NULL or k == E_FNREF { return -1 }
|
if k == E_FNREF { # a function handed on as a value: whoever calls it keeps what it returns
|
||||||
|
if e.s != null { push(g_es_fnrefs, e.s) }
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if k == E_STR or k == E_INT or k == E_BOOL or k == E_FLOAT or k == E_NULL { return -1 }
|
||||||
if k == E_ID {
|
if k == E_ID {
|
||||||
let c = es_local(e.s)
|
let c = es_local(e.s)
|
||||||
if c >= 0 { return c }
|
if c >= 0 { return c }
|
||||||
|
|
@ -572,10 +577,13 @@ function escape_analyse() -> void {
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < len(prog) {
|
while i < len(prog) {
|
||||||
let d = prog[i]
|
let d = prog[i]
|
||||||
if (d.kind == N_FN or d.kind == N_SYS or d.kind == N_MAIN) and d.s != null and ck_tab_get(g_es_fk, g_es_fv, d.s) == null {
|
# an entry has no name, and must be walked all the same: what it keeps, it keeps for good
|
||||||
|
var dn = d.s
|
||||||
|
if d.kind == N_MAIN and dn == null { dn = "$entry" }
|
||||||
|
if (d.kind == N_FN or d.kind == N_SYS or d.kind == N_MAIN) and dn != null and ck_tab_get(g_es_fk, g_es_fv, dn) == null {
|
||||||
let h = new Node
|
let h = new Node
|
||||||
h.ival = len(g_es_fnode)
|
h.ival = len(g_es_fnode)
|
||||||
ck_tab_put(g_es_fk, g_es_fv, d.s, h)
|
ck_tab_put(g_es_fk, g_es_fv, dn, h)
|
||||||
push(g_es_fnode, d)
|
push(g_es_fnode, d)
|
||||||
push(g_es_fspans, d.kind == N_MAIN)
|
push(g_es_fspans, d.kind == N_MAIN)
|
||||||
push(g_es_fparam, len(g_es_flag))
|
push(g_es_fparam, len(g_es_flag))
|
||||||
|
|
@ -626,6 +634,15 @@ function escape_analyse() -> void {
|
||||||
es_walk(g_onlisten[li].a)
|
es_walk(g_onlisten[li].a)
|
||||||
li += 1
|
li += 1
|
||||||
}
|
}
|
||||||
|
# a function taken as a value is called where the analysis cannot see (a UiClass's make, a step
|
||||||
|
# list, a System's tick): its result is kept by whoever calls it, whatever it returns
|
||||||
|
var fr = 0
|
||||||
|
while fr < len(g_es_fnrefs) {
|
||||||
|
let fs = es_fns_for(g_es_fnrefs[fr])
|
||||||
|
var fq = 0
|
||||||
|
while fq < len(fs) { es_escape(g_es_fret[fs[fq]]); fq += 1 }
|
||||||
|
fr += 1
|
||||||
|
}
|
||||||
es_solve()
|
es_solve()
|
||||||
# a node analysed more than once (a field's default, walked at every `new` of its record) is
|
# a node analysed more than once (a field's default, walked at every `new` of its record) is
|
||||||
# scratch only if every walk found it LOCAL: mark the LOCAL ones, then take the mark off any node
|
# scratch only if every walk found it LOCAL: mark the LOCAL ones, then take the mark off any node
|
||||||
|
|
@ -681,6 +698,12 @@ function escape_report() -> void {
|
||||||
file_write(file_stderr(), line, len(line))
|
file_write(file_stderr(), line, len(line))
|
||||||
s += 1
|
s += 1
|
||||||
}
|
}
|
||||||
|
var fl = 0
|
||||||
|
while fl < len(g_es_fnrefs) {
|
||||||
|
let fm = `escape: fn value {g_es_fnrefs[fl]} ({itoa(len(es_fns_for(g_es_fnrefs[fl])))} functions)\n`
|
||||||
|
file_write(file_stderr(), fm, len(fm))
|
||||||
|
fl += 1
|
||||||
|
}
|
||||||
let tot = `escape: {itoa(g_es_local_n)} local, {itoa(g_es_esc_n)} kept\n`
|
let tot = `escape: {itoa(g_es_local_n)} local, {itoa(g_es_esc_n)} kept\n`
|
||||||
file_write(file_stderr(), tot, len(tot))
|
file_write(file_stderr(), tot, len(tot))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ function deps_keeps(f: pointer, seen: []bool) -> void {
|
||||||
while s < len(g_es_site) {
|
while s < len(g_es_site) {
|
||||||
let c = g_es_site_cls[s]
|
let c = g_es_site_cls[s]
|
||||||
let fi = g_es_site_fn[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] {
|
if c >= 0 and fi >= 0 and g_es_fnode[fi].s != null 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 d = g_es_fnode[fi]
|
||||||
let k = dr_index(d.s)
|
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) {
|
if k >= 0 and seen[k] and not fr_alloc_ok(d.s) and d.file != null and not is_runtime_file(d.file) {
|
||||||
|
|
@ -319,7 +319,7 @@ function deps_births(f: pointer, seen: []bool) -> void {
|
||||||
let c = g_es_site_cls[s]
|
let c = g_es_site_cls[s]
|
||||||
let fi = g_es_site_fn[s]
|
let fi = g_es_site_fn[s]
|
||||||
let site = g_es_site[s]
|
let site = g_es_site[s]
|
||||||
if c >= 0 and fi >= 0 and (g_es_flag[c] & (ES_ESC | ES_FREED)) == 0 and not g_es_site_aok[s] and not g_es_site_grow[s] {
|
if c >= 0 and fi >= 0 and g_es_fnode[fi].s != null and (g_es_flag[c] & (ES_ESC | ES_FREED)) == 0 and not g_es_site_aok[s] and not g_es_site_grow[s] {
|
||||||
let d = g_es_fnode[fi]
|
let d = g_es_fnode[fi]
|
||||||
let k = dr_index(d.s)
|
let k = dr_index(d.s)
|
||||||
let inframe = k >= 0 and seen[k]
|
let inframe = k >= 0 and seen[k]
|
||||||
|
|
|
||||||
62302
selfhost/ludicc.seed.ll
62302
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
|
|
@ -827,6 +827,14 @@ function arena_defaults_case() -> void {
|
||||||
if (poisoned == "497 124747") { ok(lbl) } else { bad2(lbl, `poisoned [{poisoned}]`) }
|
if (poisoned == "497 124747") { ok(lbl) } else { bad2(lbl, `poisoned [{poisoned}]`) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function arena_fnval_case() -> void {
|
||||||
|
let lbl = "arena_fnval.ludic (--arena: what a function called through a function value returns is kept; a pool holding it survives the resets)"
|
||||||
|
let b = `{tmp_dir()}/e_arena_fnval`
|
||||||
|
if not shq(`bin/ludicc --arena examples/lang/arena_fnval.ludic -o {b} 2>/dev/null`) { bad2(lbl, "did not build"); return }
|
||||||
|
let poisoned = capture_line(`R3D_ARENA_CHECK=1 R3D_ALLOC_FENCE=off {b} < /dev/null`)
|
||||||
|
if (poisoned == "5 1053") { ok(lbl) } else { bad2(lbl, `poisoned [{poisoned}]`) }
|
||||||
|
}
|
||||||
|
|
||||||
function arena_pool_case() -> void {
|
function arena_pool_case() -> void {
|
||||||
let lbl = "arena_pool.ludic (--arena: a generic push into a kept pool keeps its records and its buffer on the heap; 0xDD poison changes nothing)"
|
let lbl = "arena_pool.ludic (--arena: a generic push into a kept pool keeps its records and its buffer on the heap; 0xDD poison changes nothing)"
|
||||||
let b = `{tmp_dir()}/e_arena_pool`
|
let b = `{tmp_dir()}/e_arena_pool`
|
||||||
|
|
@ -1137,6 +1145,7 @@ function cmd_dev_test() -> int {
|
||||||
alloc_fence_leak_case()
|
alloc_fence_leak_case()
|
||||||
alloc_fence_auto_case()
|
alloc_fence_auto_case()
|
||||||
arena_pool_case()
|
arena_pool_case()
|
||||||
|
arena_fnval_case()
|
||||||
arena_defaults_case()
|
arena_defaults_case()
|
||||||
feat_case("lang/nested_templates", "", "outer [inner 3 {x}] \"`}\" end", "nested_templates.ludic (a template literal inside another's hole)")
|
feat_case("lang/nested_templates", "", "outer [inner 3 {x}] \"`}\" end", "nested_templates.ludic (a template literal inside another's hole)")
|
||||||
feat_case("lang/aliases", "", "10 5 3", "aliases.ludic (L6: a namespace method declared as an alias of a function, labelled or by its parameters)")
|
feat_case("lang/aliases", "", "10 5 3", "aliases.ludic (L6: a namespace method declared as an alias of a function, labelled or by its parameters)")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue