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_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_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_ft: []int = new []int # flow edges
|
||||
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 {
|
||||
if e == null { return -1 }
|
||||
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 {
|
||||
let c = es_local(e.s)
|
||||
if c >= 0 { return c }
|
||||
|
|
@ -572,10 +577,13 @@ function escape_analyse() -> void {
|
|||
var i = 0
|
||||
while i < len(prog) {
|
||||
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
|
||||
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_fspans, d.kind == N_MAIN)
|
||||
push(g_es_fparam, len(g_es_flag))
|
||||
|
|
@ -626,6 +634,15 @@ function escape_analyse() -> void {
|
|||
es_walk(g_onlisten[li].a)
|
||||
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()
|
||||
# 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
|
||||
|
|
@ -681,6 +698,12 @@ function escape_report() -> void {
|
|||
file_write(file_stderr(), line, len(line))
|
||||
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`
|
||||
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) {
|
||||
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] {
|
||||
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 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) {
|
||||
|
|
@ -319,7 +319,7 @@ function deps_births(f: pointer, seen: []bool) -> void {
|
|||
let c = g_es_site_cls[s]
|
||||
let fi = g_es_site_fn[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 k = dr_index(d.s)
|
||||
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}]`) }
|
||||
}
|
||||
|
||||
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 {
|
||||
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`
|
||||
|
|
@ -1137,6 +1145,7 @@ function cmd_dev_test() -> int {
|
|||
alloc_fence_leak_case()
|
||||
alloc_fence_auto_case()
|
||||
arena_pool_case()
|
||||
arena_fnval_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/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