feat(gen): slife 释放前调度析构 __del__,并禁止手动 del(§3.6 落地)
- 释放统一入口 _SlifeEmitGuardedFree 改为「先析构、再 free」;析构名定案 __del__(对齐 TPC LLVMCG/MemoryOps._emit_local_heap_frees 的释放路径;del obj 那条语句走的仍是 __delete__)。 - 查表沿 MRO / 跨模块 stub 回退(复用 ExprGenOps._resolve_dunder_by_class);类未定义 __del__ 时**不加任何分支**,常见路径零开销。 - 析构用条件分支而不是 select:未拥有的路径必须完全不进析构体(析构体里会解引用 self,传 null 进去会踩空);块标签走 trans._label_counter,避免同函数内重名。 - §2.4 判据5 扩展:对 slife 对象手动 del obj → LifetimeError(否则析构跑两次)。hlife 路径不受影响。 - 为把 trans 传进析构查找,_SlifeEmitFrees / _SlifeEmitGuardedFree / _SlifeDropOwned 加 trans 形参,9 处调用点同步。
This commit is contained in:
1 parent
78b2052445
commit
2178970ff7
7 files changed
+75
-21
No files matched your search
@@ -687,12 +687,12 @@ novirtual class AssignGenMixin:
|
||||
# _SlifeDropOwned - 该槽已被登记为 slife 绑定时交还所有权:释放它当前拥有的对象、影子清 0。
|
||||
# 用于所有"槽里不再是编译器拥有的对象"的赋值(覆盖赋值 / 别名拷贝 / 外来指针 / None)。
|
||||
# 未登记 → no-op(该槽从未被 `new heap slife` 绑定过,编译器不负责它)。
|
||||
def _SlifeDropOwned(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_slot: mlirlite.Value*) -> int:
|
||||
"""释放该槽当前拥有的对象并置"不拥有";1=处理过 / 0=未登记"""
|
||||
def _SlifeDropOwned(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, target_slot: mlirlite.Value*) -> int:
|
||||
"""释放该槽当前拥有的对象并置"不拥有"(先按 §3.6 调度 `__del__`);1=处理过 / 0=未登记"""
|
||||
if target_slot is None: return 0
|
||||
sh: mlirlite.Value* = self.Gen.VarGen._SlifeShadow(target_slot)
|
||||
if sh is None: return 0
|
||||
self.Gen.VarGen._SlifeEmitGuardedFree(pool, builder, mod, target_slot, sh)
|
||||
self.Gen.VarGen._SlifeEmitGuardedFree(pool, builder, mod, trans, target_slot, sh)
|
||||
self.Gen.VarGen._SlifeNoteStore(pool, builder, sh, 0)
|
||||
return 1
|
||||
|
||||
@@ -710,7 +710,7 @@ novirtual class AssignGenMixin:
|
||||
if self.Gen.CallGenCtor._new_is_owned_heap(nn) != 0:
|
||||
sh: mlirlite.Value* = self.Gen.VarGen._SlifeEnsureOwned(pool, builder, target_slot)
|
||||
if sh is None: return 0
|
||||
self._SlifeDropOwned(pool, builder, mod, target_slot)
|
||||
self._SlifeDropOwned(pool, builder, mod, trans, target_slot)
|
||||
self.Gen.VarGen._SlifeNoteStore(pool, builder, sh, 1)
|
||||
return 1
|
||||
# 自赋值(p = p):保持原状;否则会"先释放再放弃所有权",把对象漏掉
|
||||
@@ -719,7 +719,7 @@ novirtual class AssignGenMixin:
|
||||
if rn.id is not None:
|
||||
src_slot: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, rn.id)
|
||||
if src_slot is not None and uint64(src_slot) == uint64(target_slot): return 0
|
||||
self._SlifeDropOwned(pool, builder, mod, target_slot)
|
||||
self._SlifeDropOwned(pool, builder, mod, trans, target_slot)
|
||||
return 0
|
||||
|
||||
def _is_addrlike_call(self, node: ast.AST*) -> int:
|
||||
|
||||
@@ -832,7 +832,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
if ret_none_cn_m.const_kind == ast.CONST_NONE: _is_void = 1
|
||||
if _is_void != 0:
|
||||
# M3:隐式收尾也是函数出口,先释放本方法登记的 slife
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans)
|
||||
mlirlite.build_ret_void(func_builder)
|
||||
elif last_is_ctrl != 0 and trans._ctrl_merge_reachable == 0:
|
||||
# Non-void with a control-flow last statement and a structurally unreachable merge ->
|
||||
@@ -844,7 +844,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
_m_zero_val: mlirlite.Value* = mlirlite.ConstZero(pool, ret_ty)
|
||||
if _m_zero_val is not None:
|
||||
# M3:隐式收尾出口,先释放 slife 再返回
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans)
|
||||
mlirlite.build_ret(func_builder, _m_zero_val)
|
||||
else: self.Gen.TypeGenErr.fatal_error(fd, "method flow falls off the end without an explicit return", "TypeError")
|
||||
else: self.Gen.TypeGenErr.fatal_error(fd, "method flow falls off the end without an explicit return", "TypeError")
|
||||
|
||||
@@ -820,7 +820,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if ret_none_cn.const_kind == ast.CONST_NONE: _is_void = 1
|
||||
if _is_void != 0:
|
||||
# M3:隐式收尾也是一个函数出口,先释放本函数登记的 slife 再返回
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans)
|
||||
mlirlite.build_ret_void(func_builder)
|
||||
elif last_is_ctrl != 0 and trans._ctrl_merge_reachable == 0:
|
||||
# A non-void function whose last statement is control flow with a structurally unreachable merge (all
|
||||
@@ -832,7 +832,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
zero_val: mlirlite.Value* = mlirlite.ConstZero(pool, ret_ty)
|
||||
if zero_val is not None:
|
||||
# M3:隐式收尾出口,先释放 slife 再返回
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans)
|
||||
mlirlite.build_ret(func_builder, zero_val)
|
||||
else: self.Gen.TypeGenErr.fatal_error(node, "function flow falls off the end without an explicit return", "TypeError")
|
||||
else: self.Gen.TypeGenErr.fatal_error(node, "function flow falls off the end without an explicit return", "TypeError")
|
||||
|
||||
@@ -224,7 +224,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if last_stmt is not None and last_stmt.kind() == ast.ASTKind.Return: last_is_return = 1
|
||||
if last_is_return == 0:
|
||||
# M3:隐式收尾也是函数出口,先释放本嵌套函数登记的 slife
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans)
|
||||
zero_val: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
mlirlite.build_ret(func_builder, zero_val)
|
||||
|
||||
|
||||
@@ -535,17 +535,17 @@ novirtual class StmtGenCifMixin:
|
||||
is_float_ret = 1
|
||||
if is_void_ret != 0:
|
||||
# M3 owned slife(§4.2):每个 return 都是函数出口,先释放本函数登记的 slife
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans)
|
||||
mlirlite.build_ret_void(builder)
|
||||
return 0
|
||||
if is_ptr_ret != 0:
|
||||
null_val: mlirlite.Value* = mlirlite.ConstNull(pool, ret_ty, "null")
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans)
|
||||
mlirlite.build_ret(builder, null_val)
|
||||
return 0
|
||||
if is_float_ret != 0:
|
||||
zero_f: mlirlite.Value* = mlirlite.ConstFloat(pool, ret_ty, 0.0)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans)
|
||||
mlirlite.build_ret(builder, zero_f)
|
||||
return 0
|
||||
if ret_ty is None:
|
||||
@@ -569,7 +569,7 @@ novirtual class StmtGenCifMixin:
|
||||
case mlirlite.LLVMType.Void():
|
||||
rv_void = 1
|
||||
if rv_void != 0:
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans)
|
||||
mlirlite.build_ret_void(builder)
|
||||
return 0
|
||||
if ret_ty_rt is not None:
|
||||
@@ -633,7 +633,7 @@ novirtual class StmtGenCifMixin:
|
||||
val = self.Gen.ExprGenBase.coerce_to_type(builder, val, ret_ty_rt, node, trans)
|
||||
# M3 owned slife(§4.2):返回值已算完,此处是常规出口——先释放 slife 再 ret
|
||||
# (必须在 val 求值之后:`return p.x` 之类若先释放 p 再读就成了 use-after-free)。
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod)
|
||||
self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans)
|
||||
mlirlite.build_ret(builder, val)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -100,6 +100,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if tgt is None: continue
|
||||
# del obj -> __delete__ (matches TPC HandlesDelete.py; no-op if undefined)
|
||||
if tgt.kind() == ast.ASTKind.Name:
|
||||
# §2.4 判据5:slife 对象由编译器独占(出口自动 __del__ + free),手动 del 会让析构跑两次
|
||||
nm_del: ast.Name* = ast.Name*(tgt)
|
||||
if nm_del is not None and nm_del.id is not None:
|
||||
del_slot: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_del.id)
|
||||
if self.Gen.VarGen._SlifeOwned(del_slot) != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(tgt, f"cannot `del {nm_del.id}`: it is a `slife` object owned by the compiler (its destructor `__del__` runs and its storage is freed automatically at the end of the scope); a manual del would destroy it twice", "LifetimeError")
|
||||
del_obj: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, tgt, None, 0, trans)
|
||||
if del_obj is not None:
|
||||
del_st_nm: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(del_obj)
|
||||
|
||||
@@ -10,6 +10,7 @@ import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
|
||||
|
||||
# VarGenMixin - variable/scope symbol table Mixin
|
||||
@@ -493,13 +494,60 @@ novirtual class VarGenMixin:
|
||||
mlirlite.build_store(builder, v, sh)
|
||||
return 1
|
||||
|
||||
def _SlifeEmitGuardedFree(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, alloca: mlirlite.Value*, sh: mlirlite.Value*) -> int:
|
||||
"""`free(select(sh != 0, load 槽, null))`:只释放该槽**确实拥有**的对象。
|
||||
def _SlifeEmitDtorBranch(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, alloca: mlirlite.Value*, sh: mlirlite.Value*) -> int:
|
||||
"""§3.6 析构调度:拥有该对象时先调 `__del__`(对齐 TPC MemoryOps._emit_local_heap_frees:先析构、再 free)。
|
||||
|
||||
用 icmp + select 而不是分支块:释放点可能插在任意位置(每个 return、覆盖赋值处),
|
||||
建块要处理支配关系与合流,select 无分支、无合流,安全得多。sh=0 时等价 free(NULL)。"""
|
||||
用**条件分支**而不是 select:未拥有的路径上必须完全不进析构体(析构体里会解引用 self)。
|
||||
类(含 MRO / 跨模块 stub)没定义 `__del__` 时**不加任何分支**,直接返回——常见路径零开销。
|
||||
析构名的选择:与 TPC 的**释放**路径一致用 `__del__`(`del obj` 那条语句才是 `__delete__`)。"""
|
||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||
if alloca is None or sh is None: return 0
|
||||
if alloca.Ty is None or alloca.Ty.Pointee == 0: return 0
|
||||
elem: mlirlite.LLVMType* = alloca.Ty.Pointee # 变量类型(T*)
|
||||
if elem is None or self.Gen.ExprGenBase.is_ptr_type(elem) == 0: return 0
|
||||
struct_ty: mlirlite.LLVMType* = elem.Pointee # T
|
||||
if struct_ty is None or struct_ty.kind() != mlirlite.LLVMType.Struct: return 0
|
||||
cls_nm: str = self.Gen.StructGen.get_class_name_by_type(pool, struct_ty)
|
||||
if cls_nm is None: return 0
|
||||
if self.Gen.ExprGenOps._resolve_dunder_by_class(pool, mod, cls_nm, "__del__", trans, struct_ty) is None: return 0
|
||||
func: mlirlite.Function* = builder.Func
|
||||
if func is None: return 0
|
||||
zero8: mlirlite.Value* = mlirlite.const_int(pool, 8, 0)
|
||||
if zero8 is None: return 0
|
||||
shv: mlirlite.Value* = mlirlite.build_load(builder, mlirlite.Int8(pool), sh)
|
||||
if shv is None: return 0
|
||||
cond: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, shv, zero8)
|
||||
if cond is None: return 0
|
||||
cur: mlirlite.Value* = mlirlite.build_load(builder, alloca.Ty.Pointee, alloca)
|
||||
if cur is None: return 0
|
||||
# 唯一标签:用 trans._label_counter(与 translate_if 同源,避免同函数内重名标签)
|
||||
cnt: int = trans._label_counter
|
||||
trans._label_counter = cnt + 1
|
||||
name_buf: str = pool.alloc(32)
|
||||
if name_buf is None: return 0
|
||||
viperlib.snprintf(name_buf, 32, "slife.dtor.%d", cnt)
|
||||
dtor_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
|
||||
viperlib.snprintf(name_buf, 32, "slife.dtor.end.%d", cnt)
|
||||
merge_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
|
||||
if dtor_bb is None or merge_bb is None: return 0
|
||||
mlirlite.build_cond_br(builder, cond, dtor_bb, merge_bb)
|
||||
mlirlite.position_at_end(builder, dtor_bb)
|
||||
# `%cur` 定义在分支块之前,支配 dtor_bb 与 merge_bb 两边 ✓
|
||||
self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, cur, "__del__", trans, None, 0)
|
||||
mlirlite.build_br(builder, merge_bb)
|
||||
mlirlite.position_at_end(builder, merge_bb)
|
||||
return 1
|
||||
|
||||
def _SlifeEmitGuardedFree(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, alloca: mlirlite.Value*, sh: mlirlite.Value*) -> int:
|
||||
"""释放该槽**确实拥有**的对象:先按 §3.6 调度析构 `__del__`(若类定义了),再 `free`。
|
||||
|
||||
释放本体用 icmp + select 而不是分支块:释放点可能插在任意位置(每个 return、覆盖赋值处),
|
||||
select 无分支、无合流,安全得多;sh=0 时取 null,等价 free(NULL),槽里的外来指针/未初始化
|
||||
值绝不参与释放。析构那一步另有条件分支(见 _SlifeEmitDtorBranch)。"""
|
||||
if pool is None or builder is None or alloca is None or sh is None: return 0
|
||||
if alloca.Ty is None or alloca.Ty.Pointee == 0: return 0
|
||||
# §3.6:先析构,再 free
|
||||
self._SlifeEmitDtorBranch(pool, builder, mod, trans, alloca, sh)
|
||||
zero8: mlirlite.Value* = mlirlite.const_int(pool, 8, 0)
|
||||
if zero8 is None: return 0
|
||||
shv: mlirlite.Value* = mlirlite.build_load(builder, mlirlite.Int8(pool), sh)
|
||||
@@ -536,7 +584,7 @@ novirtual class VarGenMixin:
|
||||
j += 1
|
||||
return 1
|
||||
|
||||
def _SlifeEmitFrees(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*) -> int:
|
||||
def _SlifeEmitFrees(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*) -> int:
|
||||
"""函数出口(所有 return / 隐式收尾):逆序释放**本函数**登记且仍拥有的 slife 对象。
|
||||
|
||||
防御性去重:同一变量槽若在表里出现多次(登记路径异常 / 旧条目残留),只发一次 free——
|
||||
@@ -547,7 +595,7 @@ novirtual class VarGenMixin:
|
||||
slot: uint64 = uint64(_g_slife_owned[i * 2])
|
||||
sh: mlirlite.Value* = mlirlite.Value*(_g_slife_owned[i * 2 + 1])
|
||||
if sh is not None and self._SlifeSlotFirstSeen(i, slot) != 0:
|
||||
self._SlifeEmitGuardedFree(pool, builder, mod, mlirlite.Value*(_g_slife_owned[i * 2]), sh)
|
||||
self._SlifeEmitGuardedFree(pool, builder, mod, trans, mlirlite.Value*(_g_slife_owned[i * 2]), sh)
|
||||
i -= 1
|
||||
return 0
|
||||
|
||||
|
||||
Reference in new issue
Block a user