feat(gen): LifeTag 接到 new 描述符;vpi 文本缓存收敛为单一入口

life_of_expr 新增 ASTKind.New 分支(§2.3 矩阵):stack→LIFE_FRAME;heap/memgr 的 slife 同样按帧生命期(作用域末 RAII 释放,逃逸必悬垂);hlife→LIFE_HEAP。于是 new stack slife Foo() 的结果被赋给变量/返回时会被既有汇点判据 fail_if_life_escapes 拦住(该判据本来就是生命期驱动,无需再加语法特例)。

VpiReader 新增 _cache_vpi_annots_text 单一入口(ret/param/逃逸摘要一次喂齐),两个 vpi 加载点都改调它:从结构上消除"某个加载点漏喂某类缓存"这类静默退化(本次 EscapeXmodTest 漏检的根因类别)。
This commit is contained in:
TermiNexus committed 2026-10-08 18:19:15 +08:00
1 parent 8a98051759
commit 9e55e909a8
2 files changed
+29 -12

No files matched your search

+19 -12
View File
@@ -864,14 +864,27 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int:
f.close()
if bytes_read > 0:
if bytes_read < VPI_READ_BUF_SIZE: vpi_buf[bytes_read] = 0
_cache_vpi_return_annots_text(vpi_buf, src_sha1)
# v3:同一次加载里把行尾逃逸摘要标记也收进缓存(两者共享"本模块是否已加载"的判定)
_cache_vpi_escape_annots_text(vpi_buf, src_sha1)
# 单一入口:一次喂齐 ret / param / 逃逸摘要(见 _cache_vpi_annots_text 注释)
_cache_vpi_annots_text(vpi_buf, src_sha1)
stdlib.free(vpi_buf)
stdlib.free(vpi_path)
return 1
# _cache_vpi_annots_text - 所有"从 vpi 文本解析"的缓存**单一入口**。
# 任何新增的文本缓存都加在这里,不要再散落到各加载点——漏喂一处就会**静默退化**
# (现场:逃逸摘要只在 _ensure_vpi_ret_annot_loaded 里喂,而 stub 注入加载点没喂,
# 前者开头又有"本模块 ret 注解已缓存即 return 1"的短路,于是跨模块逃逸查询永久查空,
# EscapeXmodTest 漏检)。
def _cache_vpi_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
"""一次性把 vpi 文本派生出的所有缓存喂齐;1=完成 / 0=入参非法"""
if vpi_buf is None or src_sha1 is None: return 0
_cache_vpi_return_annots_text(vpi_buf, src_sha1)
_cache_vpi_param_annots_text(vpi_buf, src_sha1)
_cache_vpi_escape_annots_text(vpi_buf, src_sha1)
return 1
# _cache_vpi_escape_annots_text - extract the trailing `# esc=<mask> ret=<ret>` markers.
# v3 跨模块逃逸摘要:不依赖 AST,按行扫描(与 _cache_vpi_return_annots_text 同构)。
def _cache_vpi_escape_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
@@ -1799,15 +1812,9 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v
# Cache method return annotations from vpi_buf text into stdlib (no AST tree: deep nodes
# may be corrupted later, but the raw vpi_buf text stays valid until the function ends).
_cache_vpi_return_annots_text(vpi_buf, src_sha1)
# Likewise cache param annotations (tree param lists may be truncated;
# _inject_*_declare uses this cache to complete declare param counts).
_cache_vpi_param_annots_text(vpi_buf, src_sha1)
# v3 逃逸摘要必须**在所有 vpi 加载点**都喂一份:_ensure_vpi_ret_annot_loaded 开头有
# "本模块 ret 注解已缓存 → 直接 return 1" 的短路,若某模块先经本函数(stub 注入)
# 加载、而这里没喂逃逸缓存,后续跨模块逃逸查询就会永久命中不到(现场:EscapeXmodTest
# 漏检——box.vpi 里 `# esc=2` 明明写对了,查询侧却查空)。
_cache_vpi_escape_annots_text(vpi_buf, src_sha1)
# 单一入口:ret / param / 逃逸摘要一次喂齐(见 _cache_vpi_annots_text 注释)。
# 本函数是 stub 注入路径的加载点,历史上这里漏喂逃逸摘要 → 跨模块漏检。
_cache_vpi_annots_text(vpi_buf, src_sha1)
# 3. walk top-level AST nodes
ch: list[ast.AST*]* = tree.children