feat(gen): 逃逸摘要传递闭包(M2.3 v2)

- walker 认值位置上的调用(Return/Assign/AnnAssign/Expr 的 value 为 Call)。 - 传递规则:g(p,...) 中 g 会逃逸的实参位若绑到本函数形参 p,则 p 也逃逸。 - 注册完再重扫 2 遍(单调 OR-in)。已知:超过 2 层的链仍可能漏,真正收敛需改为扫描到不变为止。
This commit is contained in:
TermiNexus committed 2026-10-08 13:47:54 +08:00
1 parent 52fd89decf
commit 1fb0f4a4ea
1 file changed
+57
+57
View File
@@ -283,6 +283,10 @@ novirtual class StructGenMixin:
# 从根上消除"槽里是坏指针 → strcmp 崩在 0xffff…"这一整类问题。
self._RegisterFuncEscapeSummary(esc_pool, fd, fd.name)
# v2 传递闭包:再迭代两次,让 `f` 经调用点继承 `g` 的逃逸(单调 OR-in,稳定即收敛)
for pi2 in range(2):
self._RescanEscapes(esc_pool, tree)
def is_file_func(self, name: str) -> int:
"""Return 1 when the bare name is a top-level function declared in this file."""
if name is None or self.FileFuncNames is None: return 0
@@ -422,6 +426,18 @@ novirtual class StructGenMixin:
st: ast.AST* = stmts.get(i)
if st is None: continue
k: int = st.kind()
# v2 传递闭包:语句的值若是调用 g(...),把 g 的逃逸位经实参传递上来
vnode: ast.AST* = None
if k == ast.ASTKind.Return:
vnode = ast.Return*(st).value
elif k == ast.ASTKind.Assign:
vnode = ast.Assign*(st).value
elif k == ast.ASTKind.AnnAssign:
vnode = ast.AnnAssign*(st).value
elif k == ast.ASTKind.Expr:
vnode = ast.Expr*(st).value
if vnode is not None and vnode.kind() == ast.ASTKind.Call:
self._PropagateCallEscapes(fd, ast.Call*(vnode), box)
if k == ast.ASTKind.Return:
rt: ast.Return* = ast.Return*(st)
pi: int = self._ParamIndex(fd, rt.value)
@@ -451,6 +467,47 @@ novirtual class StructGenMixin:
self._ScanEscapes(fd, frn.orelse, gset, box, depth + 1)
return 0
def _PropagateCallEscapes(self, fd: ast.FunctionDef*, cl: ast.Call*, box: sizet*) -> int:
"""v2 传递:`g(p, ...)` 里 g 会逃逸的实参位若绑到本函数形参 p → p 也逃逸"""
if fd is None or cl is None or box is None: return 0
if cl.func is None or cl.func.kind() != ast.ASTKind.Name: return 0
cnm: ast.Name* = ast.Name*(cl.func)
if cnm.id is None: return 0
gmask: int = self.get_param_escapes(cnm.id)
if gmask == 0: return 0
if cl.args is None: return 0
na: sizet = len(cl.args)
for j in range(na):
if ((gmask >> int(j)) & 1) == 0: continue
pi: int = self._ParamIndex(fd, cl.args.get(j))
if pi >= 0: box[0] = box[0] | (sizet(1) << pi)
return 0
def _RescanEscapes(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int:
"""v2 传递闭包的一次迭代:以既有掩码为起点重扫本文件全部顶层函数(单调 OR-in)"""
if pool is None or tree is None or self.EscTable is None: return 1
ch: list[ast.AST*]* = tree.children
if ch is None: return 1
cn: sizet = len(ch)
for ci in range(cn):
child: ast.AST* = ch.get(ci)
if child is None: continue
if child.kind() != ast.ASTKind.FunctionDef: continue
fd: ast.FunctionDef* = ast.FunctionDef*(child)
if fd is None or fd.name is None: continue
idx: int = self._EscLookup(fd.name)
if idx < 0: continue
gset: hashtable.HashTable* = hashtable.HashTable(pool)
if gset is not None: self._CollectGlobalNames(fd.children, gset, 0)
box: sizet* = sizet*(pool.alloc(16))
if box is None: continue
box[0] = self.EscTable[idx * 3 + 1]
box[1] = self.EscTable[idx * 3 + 2]
self._ScanEscapes(fd, fd.children, gset, box, 0)
self.EscTable[idx * 3 + 1] = box[0]
self.EscTable[idx * 3 + 2] = box[1]
return 0
def get_file_module_sha1(self) -> str:
"""Return the module SHA1 of the file being translated (None if unregistered)."""
return self.FileModuleSha1