feat(lifetime): @life(escape/noescape) 升为调用点最终裁决(契约不可被推断推翻)
StructGenMixin: 新增 EscDirTable(3 槽/项:[名哈希, 强制逃逸位, 强制不逃逸位]), _RegisterFuncEscapeSummary 把 compute_escape_summary 落在 box[3]/box[4] 的指令掩码单独落表 (与 EscTable 的"推断结果"分开);新增 _DirLookup / get_param_escape_force / get_param_noescape_force。 compute_escape_summary 的指令输出缓冲由 g+68 改为 box[3](同一内存,语义显式化)。 CallGenMixin: 在常量特化、同命裁剪、跨模块回退**全部做完之后**,最后套用一次指令: esc_mask = (esc_mask | escape_force) & ~noescape_force。这样 escape(...) 不会被"死分支/同命"折掉, noescape(...) 也不会被任何上游路径重新解释成"逃逸"。
This commit is contained in:
1 parent
8881dea2e2
commit
fc7c5d5560
3 files changed
+78
-9
No files matched your search
@@ -192,6 +192,13 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if fld_only != 0:
|
||||
cl_sf: int = self._FrameArgsEscapeClears(trans, cargs, can, fld_only)
|
||||
if cl_sf != 0: esc_mask = esc_mask & (~cl_sf)
|
||||
# ==== 声明式指令最终裁决(@life(escape/noescape):unsafe 但强制)====
|
||||
# 必须放在所有推断/裁剪**之后**:常量特化、同命裁剪、跨模块回退都改不动契约。
|
||||
# escape(...) 无条件置位、noescape(...) 无条件清位。
|
||||
fe_m: int = GenApi.get_param_escape_force(func_name)
|
||||
ne_m: int = GenApi.get_param_noescape_force(func_name)
|
||||
if fe_m != 0 or ne_m != 0:
|
||||
esc_mask = (esc_mask | fe_m) & (~ne_m)
|
||||
if esc_mask != 0:
|
||||
_dummy_ptr: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
for ai in range(can):
|
||||
|
||||
@@ -497,6 +497,20 @@ def get_param_escapes_hard_method(pool: memhub.MemBuddy*, class_name: str, metho
|
||||
return 0
|
||||
|
||||
|
||||
# ---- 声明式指令(@life(escape/noescape))的最终裁决掩码 ----
|
||||
# 契约表与推断表分开:调用点在所有推断/裁剪之后最后套用,任何推导都不能推翻。
|
||||
def get_param_escape_force(name: str) -> int:
|
||||
"""`@life(escape(...))` 强制逃逸位掩码;0 = 无该标注"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.get_param_escape_force(name)
|
||||
return 0
|
||||
|
||||
|
||||
def get_param_noescape_force(name: str) -> int:
|
||||
"""`@life(noescape(...))` 强制不逃逸位掩码;0 = 无该标注"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.get_param_noescape_force(name)
|
||||
return 0
|
||||
|
||||
|
||||
# escape_leak_line - **错误路径专用**:被调者第 want_pi 号形参的"首个逃逸写入点"行号。
|
||||
# 只在 fail-fast 报错时调用(会重扫一次被调者函数体);跨模块拿不到 AST 时返回 0。
|
||||
def escape_leak_line(name: str, want_pi: int) -> int:
|
||||
|
||||
@@ -173,7 +173,12 @@ novirtual class StructGenMixin:
|
||||
# escape_leak_line 重扫被调者函数体前,把"想要的形参位"放进 EscLeakWant;_MarkEscapingStore
|
||||
# 命中该位时把**首个**造成逃逸的写入点行号写进 EscLeakLine。-1 = 不采集(登记/落盘路径恒为 -1)。
|
||||
EscLeakWant: int
|
||||
EscLeakLine: int
|
||||
EscLeakLine: int
|
||||
# ── 声明式指令表(`@life(escape(...))` / `@life(noescape(...))`)──
|
||||
# 3 槽/项:[名哈希, 强制逃逸位, 强制不逃逸位]。调用点在**所有推断与裁剪之后**最后套用,
|
||||
# 是不可被推翻的契约("unsafe 但强制")。每文件重置计数。
|
||||
EscDirTable: sizet*
|
||||
EscDirCount: int
|
||||
|
||||
def InitStructGen(self) -> int:
|
||||
"""Initialize struct-mixin fields; called inline from subclass constructors."""
|
||||
@@ -203,7 +208,9 @@ novirtual class StructGenMixin:
|
||||
self.EscFieldTable = None
|
||||
self.EscFieldCount = 0
|
||||
self.EscLeakWant = -1
|
||||
self.EscLeakLine = 0
|
||||
self.EscLeakLine = 0
|
||||
self.EscDirTable = None
|
||||
self.EscDirCount = 0
|
||||
self.StructNameIdx = None
|
||||
self.StructTypeIdx = None
|
||||
self.StructIdxCap = 0
|
||||
@@ -358,6 +365,11 @@ novirtual class StructGenMixin:
|
||||
self.EscFieldTable = sizet*(esc_pool.alloc(esc_cap * 16))
|
||||
if self.EscFieldTable is not None: string.memset(self.EscFieldTable, 0, esc_cap * 16)
|
||||
self.EscFieldCount = 0
|
||||
# 声明式指令表(escape/noescape):同样只分配一次,每文件重置计数
|
||||
if self.EscDirTable is None:
|
||||
self.EscDirTable = sizet*(esc_pool.alloc(esc_cap * 24))
|
||||
if self.EscDirTable is not None: string.memset(self.EscDirTable, 0, esc_cap * 24)
|
||||
self.EscDirCount = 0
|
||||
self.EscFuncCount = 0
|
||||
self.EscConstEnv = None
|
||||
self.EscIndCount = 0
|
||||
@@ -512,11 +524,36 @@ novirtual class StructGenMixin:
|
||||
if idx < 0: return 0
|
||||
return int(self.EscTable[idx * 3 + 1])
|
||||
|
||||
def get_ret_borrow(self, name: str) -> int:
|
||||
"""返回借用标记:1=该函数直接返回某形参;无记录返回 0"""
|
||||
idx: int = self._EscLookup(name)
|
||||
if idx < 0: return 0
|
||||
return int(self.EscTable[idx * 3 + 2])
|
||||
def get_ret_borrow(self, name: str) -> int:
|
||||
"""返回借用标记:1=该函数直接返回某形参;0=无记录"""
|
||||
idx: int = self._EscLookup(name)
|
||||
if idx < 0: return 0
|
||||
return int(self.EscTable[idx * 3 + 2])
|
||||
|
||||
# ---- 声明式指令(@life(escape/noescape)):调用点的**最终裁决**表 ----
|
||||
# 与 EscTable 分开存:EscTable 的掩码是"推断结果"(能被常量特化/同命裁剪改动),
|
||||
# 指令是"契约"(不能被任何推断推翻),调用点在所有推断之后最后套用一次。
|
||||
def _DirLookup(self, name: str) -> int:
|
||||
"""指令表项下标;未找到返回 -1"""
|
||||
if name is None or self.EscDirTable is None: return -1
|
||||
h: sizet = self._NameHash(name)
|
||||
i: int = 0
|
||||
while i < self.EscDirCount:
|
||||
if self.EscDirTable[i * 3] == h: return i
|
||||
i += 1
|
||||
return -1
|
||||
|
||||
def get_param_escape_force(self, name: str) -> int:
|
||||
"""`@life(escape(...))` 强制逃逸位掩码;0 = 无该标注"""
|
||||
idx: int = self._DirLookup(name)
|
||||
if idx < 0: return 0
|
||||
return int(self.EscDirTable[idx * 3 + 1])
|
||||
|
||||
def get_param_noescape_force(self, name: str) -> int:
|
||||
"""`@life(noescape(...))` 强制不逃逸位掩码;0 = 无该标注"""
|
||||
idx: int = self._DirLookup(name)
|
||||
if idx < 0: return 0
|
||||
return int(self.EscDirTable[idx * 3 + 2])
|
||||
|
||||
# ---- P2 willFree:声明式标注表(直接来自 @life(willFree(...)) / @willFree(...),不做数据流) ----
|
||||
def _WillFreeLookup(self, name: str) -> int:
|
||||
@@ -599,8 +636,10 @@ novirtual class StructGenMixin:
|
||||
# 显式标注(声明式契约)**在扫描之后**叠加:escape 无条件置位、noescape 无条件清位。
|
||||
# 顺序是关键:常量折叠发生在扫描里,所以 `escape(p)` 不会被"死分支"折掉 —— 契约的本意
|
||||
# 正是"编译器看不见的路径";`noescape(p)` 也不会被任何推导重新置位。
|
||||
# 借 g 的空槽(68/69,此刻 env 已用完)当输出缓冲,不依赖任何额外 scratch。
|
||||
ann_out: sizet* = sizet*(uint64(g) + 68 * 8)
|
||||
# 输出缓冲借用 box[3]/box[4](= 槽 68/69,此刻 env 已用完),不依赖额外 scratch;
|
||||
# 这两个槽同时把**声明式指令**交给调用方(_RegisterFuncEscapeSummary 落 EscDirTable →
|
||||
# 调用点最后再套用一次,那是不可被推断推翻的最终裁决)。
|
||||
ann_out: sizet* = sizet*(uint64(box) + 3 * 8)
|
||||
if GenApi.compute_escape_directives(fd, ann_out) == 0:
|
||||
ef_m: int = int(ann_out[0])
|
||||
ne_m: int = int(ann_out[1])
|
||||
@@ -1311,6 +1350,15 @@ novirtual class StructGenMixin:
|
||||
self.EscFuncTable[fs * 2 + 0] = sizet(self._NameHash(key))
|
||||
self.EscFuncTable[fs * 2 + 1] = sizet(fd)
|
||||
self.EscFuncCount = fs + 1
|
||||
# 声明式指令(@life(escape/noescape))单独落表:调用点在所有推断/裁剪之后最后套用。
|
||||
# box[3]/box[4] 由 compute_escape_summary 借给 compute_escape_directives 当输出(无标注时恒 0)。
|
||||
if self.EscDirTable is not None and self.EscDirCount < 1024:
|
||||
if box[3] != 0 or box[4] != 0:
|
||||
d_s: int = self.EscDirCount
|
||||
self.EscDirTable[d_s * 3 + 0] = sizet(self._NameHash(key))
|
||||
self.EscDirTable[d_s * 3 + 1] = box[3]
|
||||
self.EscDirTable[d_s * 3 + 2] = box[4]
|
||||
self.EscDirCount = d_s + 1
|
||||
return 0
|
||||
|
||||
def _CollectGlobalNames(self, stmts: list[ast.AST*]*, gset: sizet*, depth: int) -> int:
|
||||
|
||||
Reference in new issue
Block a user