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
@@ -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