fix(gen): P2 失效状态改存独立槽表,不再给 VarEntry 加字段
现象:任何工程编译都在函数形参注册刚结束处 access-violation(pc=0,逐字节可复现)。定位:逐层探针把崩点压到 FuncGenDef 的 pre_scan 段,而该段对 Check(body 仅 if+return)应为空操作,唯一变量是 VarEntry 这个热结构被加了 Freed 字段。修法:撤销 VarEntry.Freed,改用 VarGenMixin 的独立失效槽表 _g_freed_slots(键 = 变量槽 alloca,整次翻译内唯一,故全局累积、无需按函数 reset/restore);_FreedMark/_FreedCheck/_FreedClearSlot 三个 API 替换字段读写,P2 语义不变。同时清除全部 TEMP 探针。
This commit is contained in:
1 parent
95b076581a
commit
dd24dc670f
10 files changed
+58
-47
No files matched your search
@@ -840,8 +840,6 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
|||||||
done_flags[i] = 1
|
done_flags[i] = 1
|
||||||
completed_this_pass += 1
|
completed_this_pass += 1
|
||||||
continue
|
continue
|
||||||
# [TEMP dbg] 阶段日志:定位 Phase A 崩溃点(定位后移除)
|
|
||||||
VLogger.info(f"PhaseA translating: {ent.Path}", "PhaseA")
|
|
||||||
# Compute the package name (the directory part relative to source_dir)
|
# Compute the package name (the directory part relative to source_dir)
|
||||||
src_dir_len_pa: sizet = string.strlen(source_dir)
|
src_dir_len_pa: sizet = string.strlen(source_dir)
|
||||||
pkg_pa: str = None
|
pkg_pa: str = None
|
||||||
|
|||||||
@@ -661,8 +661,6 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
|||||||
# Returns bytes written (0 on failure); rel_path should be absolute for error attribution.
|
# Returns bytes written (0 on failure); rel_path should be absolute for error attribution.
|
||||||
def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf: bytes, buf_size: sizet) -> sizet:
|
def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf: bytes, buf_size: sizet) -> sizet:
|
||||||
"""Generate .vpi stub content from an AST into buf"""
|
"""Generate .vpi stub content from an AST into buf"""
|
||||||
# [TEMP dbg] 阶段日志:进入 vpi 生成(定位崩溃点,定位后移除)
|
|
||||||
VLogger.info(f"vpi.gen: {rel_path}", "StubGen")
|
|
||||||
if tree is None or buf is None or buf_size == 0: return 0
|
if tree is None or buf is None or buf_size == 0: return 0
|
||||||
|
|
||||||
# Error attribution: point "current file" at the file being stubbed. Callers must pass
|
# Error attribution: point "current file" at the file being stubbed. Callers must pass
|
||||||
|
|||||||
@@ -535,10 +535,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
|||||||
if arg_node.kind() != ast.ASTKind.Name: return 0
|
if arg_node.kind() != ast.ASTKind.Name: return 0
|
||||||
nm: ast.Name* = ast.Name*(arg_node)
|
nm: ast.Name* = ast.Name*(arg_node)
|
||||||
if nm.id is None: return 0
|
if nm.id is None: return 0
|
||||||
ent: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
return self.Gen.VarGen._FreedMark(self.Gen.VarGen.lookup_var(trans.SymTab, nm.id))
|
||||||
if ent is None: return 0
|
|
||||||
ent.Freed = 1
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# _translate_method_call - translate obj.method(args) into a call with the
|
# _translate_method_call - translate obj.method(args) into a call with the
|
||||||
# object pointer (obj_ptr, an alloca of Ptr(StructTy)) as the self argument.
|
# object pointer (obj_ptr, an alloca of Ptr(StructTy)) as the self argument.
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
|||||||
# P2 willFree:读取已被"消费"的变量(其所有权被 @life(willFree(...)) 标注的调用夺取)→ fail-fast。
|
# P2 willFree:读取已被"消费"的变量(其所有权被 @life(willFree(...)) 标注的调用夺取)→ fail-fast。
|
||||||
# 只在**读**路径检查;对同名变量重新赋值会清除失效态(见 AssignGenMixin)。
|
# 只在**读**路径检查;对同名变量重新赋值会清除失效态(见 AssignGenMixin)。
|
||||||
if trans is not None:
|
if trans is not None:
|
||||||
ent_rd: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
|
if self.Gen.VarGen._FreedCheck(self.Gen.VarGen.lookup_var(trans.SymTab, nm_id)) != 0:
|
||||||
if ent_rd is not None and ent_rd.Freed != 0:
|
|
||||||
self.Gen.TypeGenErr.fatal_error(node, f"cannot use '{nm_id}': its value was released by a call annotated with @life(willFree(...)) and is no longer valid", "LifetimeError")
|
self.Gen.TypeGenErr.fatal_error(node, f"cannot use '{nm_id}': its value was released by a call annotated with @life(willFree(...)) and is no longer valid", "LifetimeError")
|
||||||
# CDefine compile-time constant: return the constant (float -> double, int -> i32).
|
# CDefine compile-time constant: return the constant (float -> double, int -> i32).
|
||||||
# Existing variables win over CDefine table entries injected from vpi stubs (lookup_var guard).
|
# Existing variables win over CDefine table entries injected from vpi stubs (lookup_var guard).
|
||||||
|
|||||||
@@ -585,8 +585,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
|||||||
nm_ao: ast.Name* = ast.Name*(uo.operand)
|
nm_ao: ast.Name* = ast.Name*(uo.operand)
|
||||||
if nm_ao.id is not None:
|
if nm_ao.id is not None:
|
||||||
# P2 willFree:对已失效变量取地址 → fail-fast(与读取路径同一口径)
|
# P2 willFree:对已失效变量取地址 → fail-fast(与读取路径同一口径)
|
||||||
ve_ao: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_ao.id)
|
if self.Gen.VarGen._FreedCheck(self.Gen.VarGen.lookup_var(trans.SymTab, nm_ao.id)) != 0:
|
||||||
if ve_ao is not None and ve_ao.Freed != 0:
|
|
||||||
self.Gen.TypeGenErr.fatal_error(node, f"cannot take the address of '{nm_ao.id}': its value was released by a call annotated with @life(willFree(...)) and is no longer valid", "LifetimeError")
|
self.Gen.TypeGenErr.fatal_error(node, f"cannot take the address of '{nm_ao.id}': its value was released by a call annotated with @life(willFree(...)) and is no longer valid", "LifetimeError")
|
||||||
# self/cls object reference: return the object pointer itself (Ptr(Struct)), not the pointer variable's address (Ptr(Ptr(Struct))).
|
# self/cls object reference: return the object pointer itself (Ptr(Struct)), not the pointer variable's address (Ptr(Ptr(Struct))).
|
||||||
if string.strcmp(nm_ao.id, "self") == 0 or string.strcmp(nm_ao.id, "cls") == 0:
|
if string.strcmp(nm_ao.id, "self") == 0 or string.strcmp(nm_ao.id, "cls") == 0:
|
||||||
|
|||||||
@@ -255,8 +255,6 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
|||||||
"""Translate a function definition; returns the number of added variables (usually 0)."""
|
"""Translate a function definition; returns the number of added variables (usually 0)."""
|
||||||
fd: ast.FunctionDef* = ast.FunctionDef*(node)
|
fd: ast.FunctionDef* = ast.FunctionDef*(node)
|
||||||
if fd is None or fd.name is None: return 0
|
if fd is None or fd.name is None: return 0
|
||||||
# [TEMP dbg] 阶段日志:定位崩溃到具体函数(定位后移除)
|
|
||||||
VLogger.info(f"TR.fn: {fd.name}", "TR")
|
|
||||||
|
|
||||||
# PEP 695 generic function templates are not emitted directly (same rule as forward_declare_functions):
|
# PEP 695 generic function templates are not emitted directly (same rule as forward_declare_functions):
|
||||||
# clones are emitted at call sites by _specialize_generic_function; here we only ensure the template is registered.
|
# clones are emitted at call sites by _specialize_generic_function; here we only ensure the template is registered.
|
||||||
@@ -386,7 +384,6 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
|||||||
|
|
||||||
# Record value-reference (&T / &temp T) parameter modes for call-site auto-address.
|
# Record value-reference (&T / &temp T) parameter modes for call-site auto-address.
|
||||||
self._register_func_auto_addr_modes(trans, mangled_name, fd)
|
self._register_func_auto_addr_modes(trans, mangled_name, fd)
|
||||||
VLogger.info("TR.p1.auto_addr.done", "TR")
|
|
||||||
|
|
||||||
# Register the CExport function in the global table (for cross-module calls). t.CExport definitions use bare
|
# Register the CExport function in the global table (for cross-module calls). t.CExport definitions use bare
|
||||||
# names (@strlen); cross-module calls consult the table to use the bare name instead of @{sha1}.func.
|
# names (@strlen); cross-module calls consult the table to use the bare name instead of @{sha1}.func.
|
||||||
@@ -761,7 +758,6 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
|||||||
# Reset the in-chain nonlocal heap prebuild flag (built once per function; multiple nested function definitions
|
# Reset the in-chain nonlocal heap prebuild flag (built once per function; multiple nested function definitions
|
||||||
# share one heap, preventing repeated malloc + double VarEntry redirection).
|
# share one heap, preventing repeated malloc + double VarEntry redirection).
|
||||||
trans._nl_chain_done = 0
|
trans._nl_chain_done = 0
|
||||||
VLogger.info("TR.p1c.state.saved", "TR")
|
|
||||||
|
|
||||||
# Pre-scan the body: 1) Global/Nonlocal declarations first (Python semantics: they apply to the whole function
|
# Pre-scan the body: 1) Global/Nonlocal declarations first (Python semantics: they apply to the whole function
|
||||||
# scope regardless of position, so register before any assignment); 2) AnnAssign to create local allocas early.
|
# scope regardless of position, so register before any assignment); 2) AnnAssign to create local allocas early.
|
||||||
@@ -775,17 +771,13 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
|||||||
stmt: ast.AST* = body.get(bi)
|
stmt: ast.AST* = body.get(bi)
|
||||||
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
|
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
|
||||||
|
|
||||||
VLogger.info("TR.p1d.prescan.done", "TR")
|
|
||||||
|
|
||||||
# M3 owned slife(§4.2 函数级 scope guard):进入函数体前把登记区间起点抬到当前栈顶。
|
# M3 owned slife(§4.2 函数级 scope guard):进入函数体前把登记区间起点抬到当前栈顶。
|
||||||
# 表是函数间共享的一段栈式区间,嵌套函数各自独立;退出时由 _SlifeRestore 弹回外层起点。
|
# 表是函数间共享的一段栈式区间,嵌套函数各自独立;退出时由 _SlifeRestore 弹回外层起点。
|
||||||
# 放在这里(而非更早)是因为上方存在多条提前返回分支(泛型模板/async/extern),
|
# 放在这里(而非更早)是因为上方存在多条提前返回分支(泛型模板/async/extern),
|
||||||
# 只有从这里开始才保证"有 reset 必有 restore"。
|
# 只有从这里开始才保证"有 reset 必有 restore"。
|
||||||
old_slife_base: int = self.Gen.VarGen._SlifeReset(pool)
|
old_slife_base: int = self.Gen.VarGen._SlifeReset(pool)
|
||||||
VLogger.info("TR.p2.slife_reset.done", "TR")
|
|
||||||
|
|
||||||
# Translate the function body
|
# Translate the function body
|
||||||
VLogger.info("TR.p3.body.begin", "TR")
|
|
||||||
if body is not None:
|
if body is not None:
|
||||||
bn2: sizet = len(body)
|
bn2: sizet = len(body)
|
||||||
for bi2 in range(bn2):
|
for bi2 in range(bn2):
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ class VarEntry:
|
|||||||
# 生命期标签(LifeTag,见文件头 define LIFE_*):该变量**当前值**的生命期。
|
# 生命期标签(LifeTag,见文件头 define LIFE_*):该变量**当前值**的生命期。
|
||||||
# 追加在末尾以保持既有字段偏移不变。默认 LIFE_UNKNOWN(未登记 = 保守放行)。
|
# 追加在末尾以保持既有字段偏移不变。默认 LIFE_UNKNOWN(未登记 = 保守放行)。
|
||||||
Life: int
|
Life: int
|
||||||
# P2 willFree:1=该变量已被某个带 @life(willFree(...)) 标注的调用"消费",其后再读/取址/传参
|
# 注:P2 的"已失效"状态**不放在这里** —— 给这个热结构加字段会改变布局、牵连整条变量注册
|
||||||
# 即 LifetimeError(fail-fast)。追加在末尾以保持既有字段偏移不变;add_var 显式置 0。
|
# 路径(现场:任何工程编译时都在"函数形参注册刚结束"处 access-violation)。改由
|
||||||
Freed: int
|
# VarGenMixin 的独立失效槽表 _g_freed_slots 保存(键 = 变量槽 alloca,槽本身唯一)。
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Scope - scope node (formerly HandlesVar.Scope)
|
# Scope - scope node (formerly HandlesVar.Scope)
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
|||||||
|
|
||||||
def translate_stmt(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
def translate_stmt(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
"""Translate a single statement with a depth guard; returns the number of new variables"""
|
"""Translate a single statement with a depth guard; returns the number of new variables"""
|
||||||
# [TEMP dbg] 阶段日志:语句级(定位崩溃到具体语句,定位后移除)
|
|
||||||
if node is not None: VLogger.info(f"stmt.kind={node.kind()}", "TR")
|
|
||||||
if trans is not None:
|
if trans is not None:
|
||||||
# --traceback: statement-level DWARF location (no-op outside a DI-attached
|
# --traceback: statement-level DWARF location (no-op outside a DI-attached
|
||||||
# define). Every instruction then carries ", !dbg !N".
|
# define). Every instruction then carries ", !dbg !N".
|
||||||
|
|||||||
@@ -381,8 +381,6 @@ novirtual class Translator:
|
|||||||
"""Translate the AST into LLVM IR."""
|
"""Translate the AST into LLVM IR."""
|
||||||
if tree is None: return 1
|
if tree is None: return 1
|
||||||
if _mbuddy is None: return 1
|
if _mbuddy is None: return 1
|
||||||
# [TEMP dbg] 阶段日志:定位崩溃点(定位后移除)
|
|
||||||
VLogger.info(f"TR.enter sha1={self.ModuleSha1}", "TR")
|
|
||||||
|
|
||||||
pool: memhub.MemBuddy* = _mbuddy
|
pool: memhub.MemBuddy* = _mbuddy
|
||||||
|
|
||||||
@@ -455,8 +453,6 @@ novirtual class Translator:
|
|||||||
# (现场:Test/NewLifetimeTest 未 import stdlib,拿不到 stdlib vpi 的 declare)。
|
# (现场:Test/NewLifetimeTest 未 import stdlib,拿不到 stdlib vpi 的 declare)。
|
||||||
free_func: mlirlite.Function* = mlirlite.create_declare(decl_pool, mod, "free", void_ty_decl)
|
free_func: mlirlite.Function* = mlirlite.create_declare(decl_pool, mod, "free", void_ty_decl)
|
||||||
if free_func is not None: mlirlite.add_param(decl_pool, free_func, i8_ptr_ty, "p")
|
if free_func is not None: mlirlite.add_param(decl_pool, free_func, i8_ptr_ty, "p")
|
||||||
# [TEMP dbg] 阶段日志:模块前言完成(定位崩溃点,定位后移除)
|
|
||||||
VLogger.info("TR.prelude.done", "TR")
|
|
||||||
|
|
||||||
# Check whether the user defined a main function
|
# Check whether the user defined a main function
|
||||||
has_user_main: int = 0
|
has_user_main: int = 0
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ define SCOPE_BLOCK = 2
|
|||||||
# 0 号槽覆写外层登记项,且内层出口会把外层的对象一起 free 掉。
|
# 0 号槽覆写外层登记项,且内层出口会把外层的对象一起 free 掉。
|
||||||
# 登记表容量(P5:128 → 256)。超限时 **fail-fast**(不再静默退化为"不登记 = 不释放"的泄漏)。
|
# 登记表容量(P5:128 → 256)。超限时 **fail-fast**(不再静默退化为"不登记 = 不释放"的泄漏)。
|
||||||
define _SLIFE_MAX = 256
|
define _SLIFE_MAX = 256
|
||||||
|
# P2 willFree 失效槽表:_g_freed_slots[0.._g_freed_count) 是"已被标注调用消费过的变量槽 alloca"。
|
||||||
|
# **刻意不放进 VarEntry**(给热结构加字段会动布局、牵连整条变量注册路径)。
|
||||||
|
# 键 = alloca,槽在整次翻译中唯一,所以是"全局累积表",无需按函数 reset/restore。
|
||||||
|
define _FREED_MAX = 4096
|
||||||
|
_g_freed_slots: sizet* = None
|
||||||
|
_g_freed_count: int = 0
|
||||||
_g_slife_owned: sizet* = None
|
_g_slife_owned: sizet* = None
|
||||||
_g_slife_count: int = 0
|
_g_slife_count: int = 0
|
||||||
_g_slife_top: int = 0
|
_g_slife_top: int = 0
|
||||||
@@ -288,41 +294,69 @@ novirtual class VarGenMixin:
|
|||||||
"""Add a variable to the variable table"""
|
"""Add a variable to the variable table"""
|
||||||
if name is None or alloca is None or vars_ptr is None: return 1
|
if name is None or alloca is None or vars_ptr is None: return 1
|
||||||
if var_count >= HT.MAX_VARS: return 1
|
if var_count >= HT.MAX_VARS: return 1
|
||||||
VLogger.info("dbg.a1.enter", "TR")
|
|
||||||
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
|
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
|
||||||
entry_addr: uint64 = uint64(vars_ptr) + var_count * entry_size
|
entry_addr: uint64 = uint64(vars_ptr) + var_count * entry_size
|
||||||
entry: GenTypes.VarEntry* = GenTypes.VarEntry*(entry_addr)
|
entry: GenTypes.VarEntry* = GenTypes.VarEntry*(entry_addr)
|
||||||
VLogger.info(f"dbg.a2.entry esz={entry_size} cnt={var_count}", "TR")
|
# Deep-copy the variable name into stdlib (survives pool reset): storing the AST Name.id pointer lets
|
||||||
|
# later allocations overwrite it, so find_var's strcmp silently misses and rebuilds a degenerate alloca.
|
||||||
name_len: sizet = string.strlen(name)
|
name_len: sizet = string.strlen(name)
|
||||||
VLogger.info(f"dbg.a3.namelen={name_len}", "TR")
|
|
||||||
name_copy: bytes = stdlib.malloc(name_len + 1)
|
name_copy: bytes = stdlib.malloc(name_len + 1)
|
||||||
if name_copy is None: return 1
|
if name_copy is None: return 1
|
||||||
string.strcpy(name_copy, name)
|
string.strcpy(name_copy, name)
|
||||||
VLogger.info("dbg.a4.copied", "TR")
|
|
||||||
entry.Name = name_copy
|
entry.Name = name_copy
|
||||||
VLogger.info("dbg.a5.name", "TR")
|
|
||||||
entry.Alloca = alloca
|
entry.Alloca = alloca
|
||||||
entry.Used = 1
|
entry.Used = 1
|
||||||
|
# Explicitly zero the const flag (do not rely on the entry's zero initialization): set_var_const
|
||||||
|
# sets it to 1 only for real const objects, so zeroing avoids false positives.
|
||||||
entry.IsConst = 0
|
entry.IsConst = 0
|
||||||
VLogger.info("dbg.a6.const", "TR")
|
# 生命期标签显式初始化:未登记 = LIFE_UNKNOWN(保守放行)。
|
||||||
entry.Life = GenTypes.LIFE_UNKNOWN
|
entry.Life = GenTypes.LIFE_UNKNOWN
|
||||||
VLogger.info("dbg.a7.life", "TR")
|
|
||||||
# P2 willFree 的 entry.Freed **不在这里写**:init_vars 已对整块 memset 清零(见 init_vars),
|
|
||||||
# 逐个字段写末端偏移在"结构体刚加字段"时容易踩到;统一依赖整块清零。
|
|
||||||
# [S3 dual write] Value-type Spec (alloca.Ty is Ptr(value type), so take its pointee)
|
# [S3 dual write] Value-type Spec (alloca.Ty is Ptr(value type), so take its pointee)
|
||||||
entry.Spec = self._spec_of_var_alloca(alloca)
|
entry.Spec = self._spec_of_var_alloca(alloca)
|
||||||
VLogger.info("dbg.a8.spec", "TR")
|
return 0
|
||||||
|
|
||||||
|
# ---- P2 willFree 失效槽表 API(不改 VarEntry 布局) ----
|
||||||
|
def _FreedMark(self, slot: mlirlite.Value*) -> int:
|
||||||
|
"""把变量槽标为"已失效"(被 @life(willFree(...)) 标注的调用消费过);1=已标记 / 0=未处理"""
|
||||||
|
global _g_freed_slots, _g_freed_count
|
||||||
|
if slot is None: return 0
|
||||||
|
if self._FreedCheck(slot) != 0: return 1
|
||||||
|
if _g_freed_slots is None:
|
||||||
|
_g_freed_slots = sizet*(stdlib.malloc(_FREED_MAX * 8))
|
||||||
|
if _g_freed_slots is None: return 0
|
||||||
|
if _g_freed_count >= _FREED_MAX:
|
||||||
|
self.Gen.TypeGenErr.fatal_error(None, "the willFree 'released variable' table is full (capacity 4096): too many released variables in one translation", "LifetimeError")
|
||||||
|
return 0
|
||||||
|
_g_freed_slots[_g_freed_count] = sizet(slot)
|
||||||
|
_g_freed_count += 1
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def _FreedCheck(self, slot: mlirlite.Value*) -> int:
|
||||||
|
"""该变量槽是否已被标为失效;1=已失效(其后再读/取址/传参即 LifetimeError)/ 0=有效"""
|
||||||
|
if slot is None or _g_freed_slots is None: return 0
|
||||||
|
i: int = 0
|
||||||
|
while i < _g_freed_count:
|
||||||
|
if uint64(_g_freed_slots[i]) == uint64(slot): return 1
|
||||||
|
i += 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _FreedClearSlot(self, slot: mlirlite.Value*) -> int:
|
||||||
|
"""清除某槽的失效态(对同名变量重新赋值后调用);1=清除过 / 0=本来不在表里"""
|
||||||
|
global _g_freed_count
|
||||||
|
if slot is None or _g_freed_slots is None: return 0
|
||||||
|
i: int = 0
|
||||||
|
while i < _g_freed_count:
|
||||||
|
if uint64(_g_freed_slots[i]) == uint64(slot):
|
||||||
|
_g_freed_count -= 1
|
||||||
|
_g_freed_slots[i] = _g_freed_slots[_g_freed_count]
|
||||||
|
return 1
|
||||||
|
i += 1
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def clear_var_freed(self, symtab: GenTypes.SymbolTable*, name: str) -> int:
|
def clear_var_freed(self, symtab: GenTypes.SymbolTable*, name: str) -> int:
|
||||||
"""P2 willFree:对同名变量重新赋值后清除失效态;1=清除过 / 0=无该项
|
"""P2 willFree:对同名变量重新赋值后清除失效态;1=清除过 / 0=无该项"""
|
||||||
|
if symtab is None or name is None: return 0
|
||||||
变量已存在时赋值走 get_or_create_sym(不重建 VarEntry),所以失效态必须显式清除——
|
return self._FreedClearSlot(self.lookup_var(symtab, name))
|
||||||
否则 `p = malloc(8); free(p); p = malloc(16); Use(p)` 会被误报。"""
|
|
||||||
e: GenTypes.VarEntry* = self.lookup_var_entry(symtab, name)
|
|
||||||
if e is None: return 0
|
|
||||||
e.Freed = 0
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# _spec_of_var_alloca - build a TypeSpec from the alloca's value type (S3 dual write).
|
# _spec_of_var_alloca - build a TypeSpec from the alloca's value type (S3 dual write).
|
||||||
# alloca.Ty is Ptr(var_ty), so the pointee is the value type; None means failure or an unsupported type.
|
# alloca.Ty is Ptr(var_ty), so the pointee is the value type; None means failure or an unsupported type.
|
||||||
|
|||||||
Reference in new issue
Block a user