feat(lifetime): @life(escape/noescape) 多形参、硬逃逸位同命精化、归纳变量区间与跨模块常量特化

- GenApi:escape/noescape 标签支持多实参;新增 compute_escape_directives 与若干委托

- StructGenMixin:硬逃逸位(box[2])、常量与归纳变量区间折叠、_PackEscapeClear/compute_escape_clears

- Call 系:调用点同命裁剪(_FrameArgsEscapeClears)+ 跨模块 escg= 查询;写侧 Converter、读侧 VpiReader

- AssignGenMixin:裸 T()(无 new、无 __new__)判为帧生命期
This commit is contained in:
TermiNexus committed 2026-10-09 18:44:51 +08:00
1 parent 14a8dcb98b
commit fc797da542
8 files changed
+937 -36

No files matched your search

+116 -7
View File
@@ -361,15 +361,88 @@ def _WillFreeMergeTag(fn: ast.FunctionDef*, tag: ast.AST*, mask: int) -> int:
elif tc.func.kind() == ast.ASTKind.Attribute:
ta: ast.Attribute* = ast.Attribute*(tc.func)
tnm = ta.attr
if tnm is None or string.strcmp(tnm, "willFree") != 0:
fatal_error(tag, "unknown lifetime tag: only `willFree(<param>)` is defined for @life(...)", "LifetimeError")
if tnm is None:
fatal_error(tag, "@life(...) accepts only `willFree(<param>)` / `escape(<param>)` / `noescape(<param>)` tags", "LifetimeError")
return mask
if tc.args is None or len(tc.args) != 1:
fatal_error(tag, "@willFree(...) requires exactly one argument: a parameter name or its 0-based position", "LifetimeError")
is_wf: int = string.strcmp(tnm, "willFree")
is_esc: int = string.strcmp(tnm, "escape")
is_nesc: int = string.strcmp(tnm, "noescape")
if is_wf != 0 and is_esc != 0 and is_nesc != 0:
fatal_error(tag, "@life(...) accepts only `willFree(<param>)` / `escape(<param>)` / `noescape(<param>)` tags", "LifetimeError")
return mask
idx: int = _WillFreeArgIndex(fn, tc.args.get(0))
if idx < 0: return mask
return mask | (1 << idx)
if tc.args is None or len(tc.args) == 0:
fatal_error(tag, f"@life tag `{tnm}(...)` requires at least one argument: parameter names or 0-based positions", "LifetimeError")
return mask
# 一个标签可以带多个形参:`@life(noescape(x, s))`(逐个并入)
an_w: sizet = len(tc.args)
k_w: sizet = 0
while k_w < an_w:
idx: int = _WillFreeArgIndex(fn, tc.args.get(k_w))
k_w += 1
if idx < 0: continue
# escape/noescape 的位由 compute_escape_directives 收,这里只负责**校验**合法性
if is_wf != 0: mask = mask | (1 << idx)
return mask
# compute_escape_directives - 收 `@life(escape(...), noescape(...))` 的方向性契约。
# out[0] = 强制**逃逸**位(无条件,不受数据流/常量折叠影响)、out[1] = 强制**不逃逸**位。
# 只认 `@life(...)` 里的标签(独立的 `@willFree(...)` 叠加形式里不放这两个)。
# 同一形参同时标 escape 与 noescape → fail-fast(矛盾契约,绝不猜一边)。
def compute_escape_directives(fd: ast.AST*, out: sizet*) -> int:
"""解析 @life(...) 里的 escape/noescape 标签到 out[0]/out[1];0=完成 / 1=入参非法"""
if out is None: return 1
out[0] = 0
out[1] = 0
fn: ast.FunctionDef* = ast.FunctionDef*(fd)
if fn is None or fn.decorator_list is None: return 0
dl: list[ast.AST*]* = fn.decorator_list
dn: sizet = len(dl)
i: sizet = 0
while i < dn:
deco: ast.AST* = dl.get(i)
i += 1
if deco is None or deco.kind() != ast.ASTKind.Call: continue
dc: ast.Call* = ast.Call*(deco)
if dc.func is None or dc.func.kind() != ast.ASTKind.Name: continue
fname: ast.Name* = ast.Name*(dc.func)
if fname.id is None or string.strcmp(fname.id, "life") != 0: continue
if dc.args is None: continue
an: sizet = len(dc.args)
j: sizet = 0
while j < an:
tg: ast.AST* = dc.args.get(j)
j += 1
if tg is None or tg.kind() != ast.ASTKind.Call: continue
tcl: ast.Call* = ast.Call*(tg)
tnm2: str = None
if tcl.func is not None and tcl.func.kind() == ast.ASTKind.Name:
tn2: ast.Name* = ast.Name*(tcl.func)
tnm2 = tn2.id
if tnm2 is None: continue
is_esc2: int = string.strcmp(tnm2, "escape")
is_nesc2: int = string.strcmp(tnm2, "noescape")
if is_esc2 != 0 and is_nesc2 != 0: continue
if tcl.args is None or len(tcl.args) == 0:
fatal_error(tg, f"@life tag `{tnm2}(...)` requires at least one argument: parameter names or 0-based positions", "LifetimeError")
continue
an4: sizet = len(tcl.args)
k4: sizet = 0
while k4 < an4:
idx2: int = _WillFreeArgIndex(fn, tcl.args.get(k4))
k4 += 1
if idx2 < 0: continue
if is_esc2 == 0:
if ((int(out[1]) >> idx2) & 1) != 0:
fatal_error(tg, f"contradictory lifetime tags: parameter #{idx2} is marked both escape(...) and noescape(...)", "LifetimeError")
continue
out[0] = out[0] | (sizet(1) << idx2)
else:
if ((int(out[0]) >> idx2) & 1) != 0:
fatal_error(tg, f"contradictory lifetime tags: parameter #{idx2} is marked both escape(...) and noescape(...)", "LifetimeError")
continue
out[1] = out[1] | (sizet(1) << idx2)
return 0
def _WillFreeArgIndex(fn: ast.FunctionDef*, arg: ast.AST*) -> int:
@@ -412,6 +485,18 @@ def _WillFreeArgIndex(fn: ast.FunctionDef*, arg: ast.AST*) -> int:
return idx
def get_param_escapes_hard(name: str) -> int:
"""本文件函数"硬逃逸位"掩码(全局写 / 返回形参 / 传递闭包 / base 未定的字段写);0 = 无记录"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_hard(name)
return 0
def get_param_escapes_hard_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版硬逃逸位:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_hard_method(pool, class_name, method_name)
return 0
def get_param_willfree(name: str) -> int:
"""本文件函数的 willFree 形参掩码(第 k 位=第 k 个形参在本调用后失效);无记录返回 0"""
if _g_gen is not None: return _g_gen.StructGen.get_param_willfree(name)
@@ -437,6 +522,30 @@ def refine_escape_mask_method(pool: memhub.MemBuddy*, class_name: str, method_na
return mask
def compute_escape_clears(fd: ast.FunctionDef*, out_packed: sizet*, max: int) -> int:
"""算出可落 .vpi 的"常量守卫"记录条数(打包值写进 out_packed);0 = 没有"""
if _g_gen is not None: return _g_gen.StructGen.compute_escape_clears(None, fd, out_packed, max)
return 0
def build_const_env(cargs: list[ast.AST*]*, can: sizet, param_base: int) -> int:
"""把实参字面量装进常量环境;1 = 装了至少一个(供跨模块守卫套用)"""
if _g_gen is not None: return _g_gen.StructGen.build_const_env(None, cargs, can, param_base)
return 0
def apply_escape_clears(packed: sizet*, n: int, mask: int) -> int:
"""按常量环境套用守卫记录(清位);返回精化后的掩码"""
if _g_gen is not None: return _g_gen.StructGen.apply_escape_clears(packed, n, mask)
return mask
def const_env_ptr() -> sizet*:
"""取常量环境区指针(`build_const_env` 装填的那块,2 槽/形参);None = 不可用"""
if _g_gen is not None: return _g_gen.StructGen.const_env_ptr()
return None
# ---------------- find_struct_by_type (delegates to StructGenMixin.find_struct_by_type) ----------------
def find_struct_by_type(struct_ty: mlirlite.LLVMType*) -> GenTypes.StructEntry*:
"""Delegate to gen.find_struct_by_type"""