escape (25.3a): which allocations never outlive their frame - the analysis, behind --escape-report; @alloc_ok on generics
emit_escape.ludic: every value is in a class, joined by flow edges (a let, an assignment, an argument into its parameter, a result into the call) and store edges (a field, an element, a push). HEAP (a parameter, a state, a global, what an unknown call hands back) flows forward; ESC (stored into something HEAP, into a global, into an event's fields or named values, handed to an unknown callee) flows backward, and from an ESC or HEAP target along a store. A load is its base's class. A site that is neither ESC nor in a function reaching Mem.frame is LOCAL (Node.uns = ES_SCRATCH). ludicc --escape-report prints each site and the totals; nothing is emitted differently yet - the arena that allocates the LOCAL sites is next. @alloc_ok on a generic now covers its instances (kept_push$NetFact is under kept_push's), and a statement's @alloc_ok is carried on the node (Node.uns), so a generic's clone keeps it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1e2a6bab5b
commit
8ca18725b6
7 changed files with 84334 additions and 74306 deletions
|
|
@ -7,18 +7,15 @@
|
|||
|
||||
var g_alloc_ok_names: []pointer = new []pointer # functions and handlers under @alloc_ok("why")
|
||||
var g_frame_fields: []pointer = new []pointer # record fields declared `@frame`
|
||||
var g_aok_nodes: []Node = new []Node # statements under @alloc_ok("why")
|
||||
var g_aok_pending: bool = false # @alloc_ok read, its function or handler not yet
|
||||
const FR_AOK_STMT: int = 78 # Node.uns on a statement under @alloc_ok("why")
|
||||
var g_aok_seen: bool = false # any statement is: bodies are searched only then
|
||||
|
||||
function fr_aok_stmt(n: Node) -> bool {
|
||||
var i = 0
|
||||
while i < len(g_aok_nodes) { if g_aok_nodes[i] == n { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
function fr_aok_stmt(n: Node) -> bool { return n.uns == FR_AOK_STMT and n.kind != E_ID }
|
||||
|
||||
# does this body hold a statement under @alloc_ok? (its function then keeps the scope's depth)
|
||||
function fr_has_aok(n: Node) -> bool {
|
||||
if n == null or len(g_aok_nodes) == 0 { return false }
|
||||
if n == null or not g_aok_seen { return false }
|
||||
if fr_aok_stmt(n) { return true }
|
||||
if fr_has_aok(n.a) or fr_has_aok(n.b) or fr_has_aok(n.c) { return true }
|
||||
if n.kids != null {
|
||||
|
|
@ -41,9 +38,16 @@ function fr_is_frame_field(name: pointer) -> bool {
|
|||
return false
|
||||
}
|
||||
|
||||
# a generic's instance (kept_push$NetFact) is under its declaration's @alloc_ok
|
||||
function fr_alloc_ok(name: pointer) -> bool {
|
||||
var base = name
|
||||
var d = 0
|
||||
while d < len(name) {
|
||||
if name[d] == '$' { base = name[0..d]; d = len(name) }
|
||||
d += 1
|
||||
}
|
||||
var i = 0
|
||||
while i < len(g_alloc_ok_names) { if (g_alloc_ok_names[i] == name) { return true }; i += 1 }
|
||||
while i < len(g_alloc_ok_names) { if (g_alloc_ok_names[i] == base) or (g_alloc_ok_names[i] == name) { return true }; i += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue