fix(gen): P4 自别名重复赋值不得释放共持对象

q = p; q = p 这类自别名重复赋值在覆盖释放时会把两个槽共持的对象提前释放(q 与 p 同时悬垂)。新增 _SlifeDropOwnedUnlessSame:在 IR 里比较 target 与源槽的当前值,相同则跳过整个覆盖释放(含析构调度),不同才走常规保护式释放。P4 转移改用它。
This commit is contained in:
TermiNexus committed 2026-10-09 08:45:09 +08:00
1 parent 3209a7a633
commit efb4a54428
2 files changed
+39 -3

No files matched your search

+3 -2
View File
@@ -768,8 +768,9 @@ novirtual class AssignGenMixin:
if self.Gen.VarGen._SlifeOwned(src_slot) != 0:
tsh: mlirlite.Value* = self.Gen.VarGen._SlifeEnsureOwned(pool, builder, target_slot)
if tsh is not None:
# 先把 q 原有对象按覆盖释放处理,再转移影子(顺序与插入顺序一致)
self._SlifeDropOwned(pool, builder, mod, trans, target_slot)
# 覆盖释放 target 原有对象:若它与源是**同一个对象**(自别名 `q = p; q = p`)
# 则跳过,否则会把两个槽共持的对象提前释放。
self.Gen.VarGen._SlifeDropOwnedUnlessSame(pool, builder, mod, trans, target_slot, src_slot)
self.Gen.VarGen._SlifeNoteStore(pool, builder, tsh, 1)
ssh: mlirlite.Value* = self.Gen.VarGen._SlifeShadow(src_slot)
if ssh is not None: self.Gen.VarGen._SlifeNoteStore(pool, builder, ssh, 0)
+36 -1
View File
@@ -588,7 +588,42 @@ novirtual class VarGenMixin:
if r is None: return 0
return 1
def _SlifeSlotFirstSeen(self, idx: int, slot: uint64) -> int:
def _SlifeDropOwnedUnlessSame(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, target_slot: mlirlite.Value*, src_slot: mlirlite.Value*) -> int:
"""P4:覆盖释放 target 的当前对象;若它与 src 的当前值相同(自别名 `q = p; q = p`)则整体跳过。
没有这个运行时判等,重复自别名赋值会把两个槽共同持有的对象释放掉(q 与 p 同时悬垂);
判等放在 IR 里做(编译期无法知道两个槽是否指向同一对象)。"""
if target_slot is None or src_slot is None or trans is None: return 0
sh: mlirlite.Value* = self._SlifeShadow(target_slot)
if sh is None or target_slot.Ty is None: return 0
el_ty: mlirlite.LLVMType* = target_slot.Ty.Pointee
qv: mlirlite.Value* = mlirlite.build_load(builder, el_ty, target_slot)
pv: mlirlite.Value* = mlirlite.build_load(builder, el_ty, src_slot)
if qv is None or pv is None: return 0
same: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, qv, pv)
if same is None: return 0
cur_func: mlirlite.Function* = trans._cur_func
if cur_func is None: return 0
cnt: int = trans._label_counter
trans._label_counter = cnt + 1
nm_free: str = pool.alloc(32)
if nm_free is None: return 0
viperlib.snprintf(nm_free, 32, "slife.xfer.free.%d", cnt)
nm_end: str = pool.alloc(32)
if nm_end is None: return 0
viperlib.snprintf(nm_end, 32, "slife.xfer.end.%d", cnt)
free_bb = mlirlite.create_block(pool, cur_func, nm_free)
end_bb = mlirlite.create_block(pool, cur_func, nm_end)
if free_bb is None or end_bb is None: return 0
# 相同 → 直接到 end(不释放);不同 → 走常规"保护式释放"(含 §3.6 析构调度)
mlirlite.build_cond_br(builder, same, end_bb, free_bb)
mlirlite.position_at_end(builder, free_bb)
self._SlifeEmitGuardedFree(pool, builder, mod, trans, target_slot, sh)
mlirlite.build_br(builder, end_bb)
mlirlite.position_at_end(builder, end_bb)
return 0
def _SlifeSlotFirstSeen(self, idx: int, slot: uint64) -> int:
"""槽 slot 在 (idx, _g_slife_top) 里是否已经出现过;1=本次首次出现(该释放)/ 0=重复(跳过)"""
j: int = idx + 1
while j < _g_slife_top: