From 9d8aedec4a457909801f3f2575e9e19f39bbc38b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 29 Sep 2026 13:00:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E5=87=BD=E6=95=B0=E5=A3=B0?= =?UTF-8?q?=E6=98=8E=E5=88=9B=E5=BB=BA=E6=94=B6=E5=8F=A3=E5=88=B0=E5=B9=82?= =?UTF-8?q?=E7=AD=89=E5=85=A5=E5=8F=A3=20=5FCreateOrReuseFuncDecl=EF=BC=9B?= =?UTF-8?q?=E6=B8=85=E7=90=86=20DDEBUG=20=E6=8E=A2=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a 编译器缺陷(根因修复)+ 调试探针移除。 现场:翻译 CallGenFd.vp 时抛 llvmlite.ir._utils.DuplicatedNameError: '5d84ea579d83ef73.CallGenFd._translate_fakeduck_call'(llvmlite 的 NameScope 按名字去重,GlobalValue.deduplicate_name=False ⇒ 同名 global 建第二次必抛,且报文只给名字、不给两侧签名)。 根因:BaseGen._get_or_declare_function 建完 ir.Function 后**只加入 module.globals**(llvmlite 自动)而**未登记 Gen.functions**;而 HandlesFunctions._EmitFunctionLlvm 的 need_new 判据只看 Gen.functions (`MangledName not in Gen.functions`)⇒ 同一名字再建一次 → DuplicatedNameError; 该处 except 又把它吞掉(非 strict 模式只 LogWarning)→ 函数始终没进 Gen.functions → 后续其它创建点(FuncGen._get_or_declare_func / get_or_declare_c_func,同样只查 self.functions)再建 → 异常从别处逃逸到 Phase2Translator。 修复: 1. BaseGen 新增 _CreateOrReuseFuncDecl(name, func_type) —— 函数声明创建的 唯一幂等入口:module.globals 已有同名函数且**函数类型结构串相同**则复用 (并补登记 Gen.functions;FunctionType 无 __eq__,只能比 str); 类型不同则 fail-fast 报出两侧签名(declare/define 分裂的真因), 绝不静默复用错签名(调用点会按错签名传参)。 2. _get_or_declare_function 改走该入口(顺带补登记 Gen.functions); FuncGen._get_or_declare_func / get_or_declare_c_func 改走该入口; HandlesFunctions 定义路径改走该入口,且该处 except 对 FailFastError 直接 re-raise(不再吞)。 3. 清理遗留调试探针(含 sys.exit(1)):BaseGen 三处 [DDEBUG] (_get_or_declare_function 双点 / _mangle_func_name 前导点与结果双点)、 HandlesExprAsm 两处 [DDEBUG_ASM]、HandlesExprCall 一处 [DDEBUG] _HandleClosureCallLlvm。 --- lib/core/Handles/HandlesExprAsm.py | 10 ----- lib/core/Handles/HandlesExprCall.py | 9 ----- lib/core/Handles/HandlesFunctions.py | 12 +++++- lib/core/LLVMCG/BaseGen.py | 58 ++++++++++++++-------------- lib/core/LLVMCG/FuncGen.py | 15 +++---- 5 files changed, 47 insertions(+), 57 deletions(-) diff --git a/lib/core/Handles/HandlesExprAsm.py b/lib/core/Handles/HandlesExprAsm.py index 109e909..8cdb71c 100644 --- a/lib/core/Handles/HandlesExprAsm.py +++ b/lib/core/Handles/HandlesExprAsm.py @@ -310,11 +310,6 @@ class ExprAsmHandle(BaseHandle): def _mangle_asm_calls(self, template: str) -> str: Gen: LlvmCodeGenerator = self.Trans.LlvmGen sha1: str = getattr(Gen, 'module_sha1', '') - # DDEBUG: 检测 module_sha1 是否带尾随点(双点 bug 根因拦截) - if sha1 and sha1.endswith('.'): - import sys as _dbg_sys - print(f"[DDEBUG_ASM] module_sha1 带尾随点: sha1='{sha1}' len={len(sha1)} template='{template[:80]}'", file=_dbg_sys.stderr) - _dbg_sys.exit(1) if not sha1: return template @@ -324,11 +319,6 @@ class ExprAsmHandle(BaseHandle): if func_name.lower() in self._X86_REGISTERS: return m.group(0) mangled: str = Gen._mangle_func_name(func_name) - # DDEBUG: 检测 mangled 是否含双点(双点 bug 根因拦截) - if '..' in mangled: - import sys as _dbg_sys - print(f"[DDEBUG_ASM] mangled 含双点: func='{func_name}' mangled='{mangled}' sha1='{sha1}'", file=_dbg_sys.stderr) - _dbg_sys.exit(1) if '.' in mangled: return f'{prefix}\\22{mangled}\\22' return f"{prefix}{mangled}" diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index c8cc21c..6026f47 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -2479,15 +2479,6 @@ class ExprCallHandle(BaseHandle): def _HandleClosureCallLlvm(self, Node: ast.Call, FuncName: str) -> ir.Value: Gen = self.Trans.LlvmGen func = Gen.functions.get(FuncName) - # DDEBUG: 检测 func.name 含双点(说明 ir.Function 已被错误注册为双点名字) - if func is not None and '..' in getattr(func, 'name', ''): - import sys - sys.stderr.write(f"[DDEBUG] _HandleClosureCallLlvm DOUBLE-DOT func.name='{func.name}' FuncName='{FuncName}'\n") - sys.stderr.write(f"[DDEBUG] Gen.functions keys for '{FuncName}': {[k for k in list(Gen.functions.keys()) if '6ddf' in k or k == FuncName][:20]}\n") - import traceback - traceback.print_stack(file=sys.stderr, limit=10) - sys.stderr.flush() - sys.exit(1) if not func: if FuncName in self.Trans.FunctionDefCache: FuncDef = self.Trans.FunctionDefCache[FuncName] diff --git a/lib/core/Handles/HandlesFunctions.py b/lib/core/Handles/HandlesFunctions.py index 8f67ebf..5ecaab7 100644 --- a/lib/core/Handles/HandlesFunctions.py +++ b/lib/core/Handles/HandlesFunctions.py @@ -1253,7 +1253,13 @@ class FunctionHandle(BaseHandle): need_new = True if need_new: try: - func: ir.Function = ir.Function(Gen.module, FuncType, name=MangledName) + # 统一走幂等入口:module.globals 已有同名函数时复用(并补登记 + # Gen.functions),签名冲突则 fail-fast —— 绝不直接新建同名 + # ir.Function(llvmlite NameScope 按名去重,必抛 DuplicatedNameError; + # 现场:5d84ea579d83ef73.CallGenFd._translate_fakeduck_call 被此路径新建, + # 而 BaseGen._get_or_declare_function 已建过同名声明却未登记 + # Gen.functions ⇒ need_new 误判为真)。 + func: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, FuncType) func.attributes.add('noredzone') if hasattr(self, '_cdefine_funcs') and FuncName in self._cdefine_funcs: func.linkage = 'linkonce_odr' @@ -1270,6 +1276,10 @@ class FunctionHandle(BaseHandle): if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: Gen.functions[FuncName] = func except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError + if isinstance(_e, FailFastError): + # 签名冲突 = declare/define 分裂,必须终止(不吞) + raise if _config_mode == "strict": self.Trans.LogWarning(f"异常被忽略: {_e}") else: diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index 66ca029..b18f39e 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -871,33 +871,43 @@ class BaseGenMixin: return [p] return [] + def _CreateOrReuseFuncDecl(self, name: str, func_type: ir.FunctionType) -> ir.Function: + """函数声明创建的唯一幂等入口(所有"新建 ir.Function"的路径都应走这里)。 + + llvmlite 的 NameScope 按名字去重(GlobalValue.deduplicate_name=False): + 同名 global 再建一次必抛 llvmlite.ir._utils.DuplicatedNameError(只给名字、 + 不给两侧签名,现场:5d84ea579d83ef73.CallGenFd._translate_fakeduck_call)。 + 故此处: + · module.globals 已有同名函数且函数类型(按字符串比较,FunctionType 无 + __eq__ ⇒ 只能比结构串)相同 ⇒ **复用**并补登记 Gen.functions; + · 已有但类型不同 ⇒ fail-fast 报出两侧签名 —— 这是 declare / define 分裂的 + 真因,绝不静默复用错签名(会让调用点按错签名传参)。 + """ + existing: Any = self.module.globals.get(name) + if isinstance(existing, ir.Function): + _old_type: Any = getattr(existing, 'ftype', None) + if _old_type is None or str(_old_type) == str(func_type): + self.functions[name] = existing + return existing + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"同名函数声明签名冲突(declare/define 分裂):'{name}'" + f"\n · 已存在声明: {_old_type}" + f"\n · 本次要创建: {func_type}" + f"\n 拒绝静默复用(调用点会按错签名传参);请核对同一函数在声明侧" + f"(stub / .vpi)与定义侧的注解解析结果是否一致。") + func: ir.Function = ir.Function(self.module, func_type, name=name) + self.functions[name] = func + return func + def _get_or_declare_function(self, mangled_name: str, func_type: ir.FunctionType) -> ir.Function: - # DDEBUG: 检测 mangled_name 含双点(不应到达这里) - if '..' in mangled_name: - import sys - sys.stderr.write(f"[DDEBUG] _get_or_declare_function DOUBLE-DOT mangled_name='{mangled_name}'\n") - import traceback - traceback.print_stack(file=sys.stderr, limit=10) - sys.stderr.flush() - sys.exit(1) - g = self.module.globals.get(mangled_name) - if g and isinstance(g, ir.Function): - return g - return ir.Function(self.module, func_type, name=mangled_name) + return self._CreateOrReuseFuncDecl(mangled_name, func_type) def _get_function(self, name: str) -> ir.Function | None: func: ir.Function | None = self._find_function(name) return func def _mangle_func_name(self, name: str, module_name: str | None = None) -> str: - # DDEBUG: 检测即将产生双点的情况,立即打印并终止 - if name.startswith('.') or (module_name and module_name.endswith('.') and len(module_name) > 0): - import sys - sys.stderr.write(f"[DDEBUG] _mangle_func_name LEADING-DOT name='{name}' module_name='{module_name}'\n") - import traceback - traceback.print_stack(file=sys.stderr, limit=8) - sys.stderr.flush() - sys.exit(1) if name in self._export_funcs and not module_name: return name if name in self._export_extern_funcs: @@ -955,14 +965,6 @@ class BaseGenMixin: result = f"{self.module_sha1}.{name}" else: result = name - # DDEBUG: 检测返回结果中是否含双点 - if '..' in result: - import sys - sys.stderr.write(f"[DDEBUG] _mangle_func_name RESULT-DOUBLE-DOT name='{name}' module_name='{module_name}' result='{result}'\n") - import traceback - traceback.print_stack(file=sys.stderr, limit=10) - sys.stderr.flush() - sys.exit(1) return result def _get_source_line(self, lineno: int) -> str | None: diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 51fae57..ad3a49a 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -812,9 +812,10 @@ class FuncGenMixin: return fobj if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname): return fobj - func_decl: ir.Function = ir.Function(self.module, func_type, name=name) - self.functions[name] = func_decl - return func_decl + # module.globals 里可能已有同名函数而 Gen.functions 未登记(如 + # BaseGen._get_or_declare_function 的历史路径):直接新建必抛 llvmlite + # DuplicatedNameError(NameScope 按名去重)⇒ 统一走幂等入口。 + return self._CreateOrReuseFuncDecl(name, func_type) def get_or_declare_c_func(self, name: str, fallback_type: ir.FunctionType | None = None) -> ir.Function | None: if name in self.functions: @@ -831,11 +832,7 @@ class FuncGenMixin: raise _vlog().warning(f"查找桩函数类型失败: {_e}", "Exception") if stub_func_type: - func_decl: ir.Function = ir.Function(self.module, stub_func_type, name=name) - self.functions[name] = func_decl - return func_decl + return self._CreateOrReuseFuncDecl(name, stub_func_type) if fallback_type is not None: - func_decl: ir.Function = ir.Function(self.module, fallback_type, name=name) - self.functions[name] = func_decl - return func_decl + return self._CreateOrReuseFuncDecl(name, fallback_type) return None \ No newline at end of file