fix(LLVMCG): llvmlite Function 创建事务化 + 不再吞 fail-fast,悬空名字不再复现
类别:a 编译器缺陷(TPC 侧)
现象:编译 StubMergerIR.vp 时 `_EmitExternalFuncDeclLlvm` 报「函数声明名字悬空占用:
'50741ad4b4b0934d.LLVMTypeCore.__init__' 已进 module.scope,但 module.globals 中无对应
对象」,且该 FailFastError 被 `_LoadModuleDeclarationsFromFile` 的节点级 `except Exception`
吞成一行 WARN「忽略节点 ClassDef LLVMTypeCore 异常」后 continue。
机制(判据已写进代码注释与报文字段):
1) llvmlite 的 `Function.__init__` 不是事务性的:名字在 `_set_name` 即进 module.scope,
之后逐个建 Argument/ReturnValue 才做类型断言,最后才 add_global;中间任一步抛错都不
回滚 scope ⇒ 留下「globals 查无、scope 已占用」的悬空名字,后续同名创建只能报裸
DuplicatedNameError(或本仓的「名字悬空占用」),而真因——上一处被吞的创建失败——已丢失。
修法:BaseGen 新增 `InstallFuncCreateGuard()`(随模块导入装一次),在**失败路径**上
回滚名字(补 llvmlite 缺的那半截事务,模块状态可重试)并抛 FailFastError,报文含
函数名 / 本次签名 / 原始异常 / 创建调用点(栈上首个非 llvmlite、非包装函数的帧);
`DuplicatedNameError` 原样上抛——那是各调用点有意处理的正常分支(如「已存在则复用」)。
2) 首建失败与该 FailFastError 被 5 处兜底吞掉,使真因退化成一行 WARN:
· HandlesImports._LoadModuleDeclarationsFromFile 节点级 except(本现场的直接来源)
· HandlesImports stub 声明加载循环的两层 except
· HandlesImports._TryLoadFuncDeclsFromOutputStub 的裸 `except Exception: pass`
· HandlesFunctions._replace_function_decl 的 `except Exception: return old_func`
(此处 old_func 必为 None ⇒ 调用点把 None 当函数声明用)
修法:一律 `except FailFastError: raise`;其余异常维持原有口径(严格模式上抛 / 其余告警)。
验证:4 例冒烟(非法签名 → FailFastError 且报文含调用点;名字已回滚 ⇒ 同名可重试成功;
重复创建仍是 DuplicatedNameError 且旧名未被回滚;合法创建零副作用)全部通过;
BaseGen / HandlesImports / HandlesFunctions 三文件 py_compile 通过。
This commit is contained in:
1 parent
b06dac1d6b
commit
8f1ec5b715
3 files changed
+113
-3
No files matched your search
@@ -1305,6 +1305,11 @@ class FunctionHandle(BaseHandle):
|
||||
if MangledName == FuncName or FuncName not in Gen._export_extern_funcs:
|
||||
Gen.functions[FuncName] = Gen.functions[MangledName]
|
||||
except Exception as _e:
|
||||
# fail-fast 一律上抛(本仓既定口径):吞掉它 = 声明静默缺失 +
|
||||
# 半注册名字留给下一轮,真因与现场脱节。
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||||
if isinstance(_e, _FFE):
|
||||
raise
|
||||
if _config_mode == "strict":
|
||||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||||
self.Trans.FunctionDefCache[FuncName] = Node
|
||||
@@ -1315,7 +1320,13 @@ class FunctionHandle(BaseHandle):
|
||||
if old_func is None:
|
||||
try:
|
||||
return ir.Function(Gen.module, NewFuncType, name=MangledName)
|
||||
except Exception: # 回退:函数已存在时返回旧函数
|
||||
except Exception as _e: # 回退:函数已存在时返回旧函数
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||||
if isinstance(_e, _FFE):
|
||||
# 创建中途失败(如形参/返回类型不是 llvmlite Type)由 BaseGen 的
|
||||
# Function 守卫转成 fail-fast:必须上抛,原实现会 `return old_func`
|
||||
# (此处 old_func 必为 None)⇒ 调用点把 None 当声明用。
|
||||
raise
|
||||
return old_func
|
||||
try:
|
||||
old_name: str = old_func.name
|
||||
|
||||
@@ -664,6 +664,15 @@ class ImportHandle(BaseHandle):
|
||||
else:
|
||||
self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name)
|
||||
except Exception as _e:
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||||
if isinstance(_e, _FFE):
|
||||
# fail-fast 一律上抛(本仓既定口径:FailFastError 不被吞,见
|
||||
# HandlesBase.py 的说明)。原实现把它与其它异常一起吞成一行
|
||||
# "忽略节点 … 异常" WARN 并 continue ⇒ 该节点(如某个 ClassDef 的
|
||||
# 全部方法声明)被整条静默丢弃,创建中途失败的**半注册名字**留到
|
||||
# 下一轮才以"函数声明名字悬空占用"暴露,真因与现场彻底脱节
|
||||
# (现场:50741ad4b4b0934d.LLVMTypeCore.__init__)。
|
||||
raise
|
||||
_vlog().warning(
|
||||
f"HandlesImports[_LoadModuleDeclarationsFromFile]: 忽略节点 "
|
||||
f"{type(node).__name__} {getattr(node, 'name', '?')} 异常: {_e}",
|
||||
@@ -3624,10 +3633,18 @@ class ImportHandle(BaseHandle):
|
||||
short_name = func_name.split('.', 1)[1]
|
||||
Gen.functions[short_name] = func
|
||||
except Exception as _e:
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||||
if isinstance(_e, _FFE):
|
||||
# 声明创建/签名的 fail-fast 必须上抛(见 _LoadModuleDeclarationsFromFile
|
||||
# 同款说明):吞掉它会让该声明静默缺失、并把半注册名字留给下一轮。
|
||||
raise
|
||||
if _config_mode == "strict":
|
||||
raise
|
||||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||||
except Exception as _e:
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||||
if isinstance(_e, _FFE):
|
||||
raise
|
||||
if _config_mode == "strict":
|
||||
raise
|
||||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||||
@@ -3707,9 +3724,19 @@ class ImportHandle(BaseHandle):
|
||||
short_fn = func_name.split('.', 1)[1]
|
||||
Gen.functions[short_fn] = func
|
||||
loaded_any = True
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as _e:
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||||
if isinstance(_e, _FFE):
|
||||
# 见 _LoadModuleDeclarationsFromFile 同款说明:fail-fast 必须上抛。
|
||||
# 原实现是裸 `except Exception: pass` —— 创建中途失败(半注册名字)
|
||||
# 会在这里被彻底吞掉,后续同名创建只能报"名字悬空占用"。
|
||||
raise
|
||||
_vlog().warning(
|
||||
f"按需加载 output stub 声明失败: '{stripped}' → {_e}", "Exception")
|
||||
except Exception as _e:
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||||
if isinstance(_e, _FFE):
|
||||
raise
|
||||
if _config_mode == "strict":
|
||||
raise
|
||||
_vlog().warning(f"按需加载 output stub 失败: {_e}", "Exception")
|
||||
|
||||
@@ -79,6 +79,78 @@ def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str
|
||||
raise FailFastError(_Msg)
|
||||
|
||||
|
||||
_TPC_FuncInitGuardInstalled: bool = False
|
||||
|
||||
|
||||
def InstallFuncCreateGuard() -> None:
|
||||
"""给 llvmlite 的 `ir.Function.__init__` 装一次守卫:创建中途失败**不留悬空名字**。
|
||||
|
||||
背景(llvmlite 的构造不是事务性的):`Function.__init__` 的顺序是
|
||||
super().__init__ → `_set_name` → `module.scope.register(name)` 【名字占用】
|
||||
→ 逐形参 `Argument(...)` / `ReturnValue(...)`(内部 `assert isinstance(typ, types.Type)`)
|
||||
→ `parent.add_global(self)` 【进入 globals】
|
||||
中间任一步抛错都不会回滚 scope ⇒ globals 查无、scope 却已占用的"悬空名字"。此后同名
|
||||
创建只会得到 llvmlite 的裸 DuplicatedNameError(报文只有名字),或本仓
|
||||
`_CreateOrReuseFuncDecl` 的"名字悬空占用"fail-fast —— 两者都只能指向"上一处失败被
|
||||
吞了",而**那一处**的调用点与异常早已丢失(现场:50741ad4b4b0934d.LLVMTypeCore.__init__,
|
||||
上游首建失败被 HandlesImports 的节点级 `except Exception` 吞成一行 WARN)。
|
||||
|
||||
本守卫做两件事(均在**失败路径**上,合法创建零额外开销):
|
||||
1. 回滚本次注册的名字(`scope._useset.discard`)——补上 llvmlite 缺的那半截事务,
|
||||
模块状态可重试,不再积累悬空名字;
|
||||
2. 抛 FailFastError,报文给出函数名、签名、原始异常,以及**发起创建的 TPC 调用点**
|
||||
(栈上第一个不在 llvmlite 包内、也不是本包装函数的帧)。
|
||||
`DuplicatedNameError`(名字已被占用)原样上抛:那是各调用点**有意**处理的正常分支
|
||||
(如 `_replace_function_decl` 的"已存在则复用"),不属于"创建中途失败"语义。
|
||||
"""
|
||||
global _TPC_FuncInitGuardInstalled
|
||||
if _TPC_FuncInitGuardInstalled:
|
||||
return
|
||||
_TPC_FuncInitGuardInstalled = True
|
||||
_OrigInit: Any = ir.Function.__init__
|
||||
|
||||
def _GuardedFuncInit(self: Any, module: Any, ftype: Any, name: Any) -> None:
|
||||
try:
|
||||
_OrigInit(self, module, ftype, name)
|
||||
return
|
||||
except Exception as _e:
|
||||
from llvmlite.ir._utils import DuplicatedNameError as _Dup
|
||||
if isinstance(_e, _Dup):
|
||||
raise
|
||||
# 名字已在 `_set_name` 阶段注册(进入本分支即证明 register 成功)⇒ 回滚
|
||||
_scope: Any = getattr(module, 'scope', None)
|
||||
_useset: Any = getattr(_scope, '_useset', None)
|
||||
if _useset is not None:
|
||||
_useset.discard(name)
|
||||
_useset.discard(str(name))
|
||||
import traceback as _tb
|
||||
_site: str = '(栈中未找到非 llvmlite 帧)'
|
||||
for _fr in reversed(_tb.extract_stack()):
|
||||
_fn: str = _fr.filename.replace('\\', '/')
|
||||
if 'site-packages/llvmlite' in _fn or _fr.name == '_GuardedFuncInit':
|
||||
continue
|
||||
_site = f"{_fr.filename}:{_fr.lineno} (in {_fr.name})"
|
||||
break
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||
raise FailFastError(
|
||||
f"函数创建失败(已回滚悬空名字):'{name}'"
|
||||
f"\n · 本次签名: {ftype}"
|
||||
f"\n · 原始异常: {type(_e).__name__}: {_e}"
|
||||
f"\n · 创建调用点: {_site}"
|
||||
f"\n 成因:llvmlite 的 Function 构造不是事务性的——名字在 `_set_name` 就进了"
|
||||
f" module.scope,之后逐个建 Argument/ReturnValue 时才做类型断言,最后才"
|
||||
f" add_global。任何形参 / 返回类型不是 llvmlite Type(典型 None:注解或 stub"
|
||||
f" declare 的类型没解析出来)都会在「已注册、未进 globals」之间失败 ⇒ 留下"
|
||||
f"悬空名字,后续同名创建只报裸 DuplicatedNameError 或「名字悬空占用」。"
|
||||
f"\n 本守卫已回滚名字(模块状态可重试);请修上面那一行的签名来源。"
|
||||
) from _e
|
||||
|
||||
ir.Function.__init__ = _GuardedFuncInit
|
||||
|
||||
|
||||
InstallFuncCreateGuard()
|
||||
|
||||
|
||||
class _GuardIrBuilder(ir.IRBuilder):
|
||||
"""默认 IR 构建器:在 GEP/load/store 处对**指针操作数**做类型守卫。
|
||||
|
||||
|
||||
Reference in new issue
Block a user