From b02bedae9adc10a984fe2907d71785c9917e97b8 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 5 Sep 2026 12:02:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(link):=20=E4=B8=BA=20weak/linkonce=20?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E4=B8=8E=E5=85=A8=E5=B1=80=E8=A1=A5=20COMDAT?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=20COFF=20=E4=B8=8B=E8=B7=A8?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E9=87=8D=E5=A4=8D=E7=AC=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit COFF 下无 COMDAT 的 weak/linkonce 定义会被 llc 展开成强符号 .weak..default(weak external 实现副本)。rowptr 新一代 includes 让泛型特化(list[rowptr[AST]].__before_init__)与跨模块共享常量(PYI_BUF_SIZE)在多个模块重复定义 → ld.lld duplicate symbol。 在 Phase2Translator 三处 dso_local 后处理簇后调用 _AddWeakComdat:函数定义补 ' comdat'、全局补 ', comdat'(置于 align/section 前),并在模块头加 comdat any 声明。llc/ld.lld 端到端已验证(双 obj 同符号链接无重复报错)。 --- lib/Projectrans/Phase2Translator.py | 62 +++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index 1890225..6eeaf29 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -501,6 +501,65 @@ def _count_braces_outside_strings(text: str) -> int: return count +def _AddWeakComdat(ll_content: str) -> str: + """为 weak/linkonce 系列 函数/全局 定义补 COMDAT(COFF/PE 必需)。 + + COFF 下无 COMDAT 的 weak/linkonce 定义会被 llc 展开成强符号 + `.weak..default`(weak external 的实现副本)。同一弱定义出现在 + 多个模块时(泛型特化方法如 `e94ef9e139461915.list[rowptr[AST]]. + __before_init__`、跨模块共享常量如 PYI_BUF_SIZE),副本同名 → + ld.lld 报 duplicate symbol。补 COMDAT(any) 后 llc 输出单一 COMDAT + 成员,链接器自动去重(llc/ld.lld 端到端已实验验证)。 + """ + if (' linkonce ' not in ll_content and ' linkonce_odr ' not in ll_content + and ' weak_odr ' not in ll_content and ' weak ' not in ll_content): + return ll_content + weak_links = 'linkonce|linkonce_odr|weak|weak_odr|extern_weak' + comdat_names: list[str] = [] + + # 1) 函数定义:define [dso_local] ... @name(...) { → @name(...) comdat { + def _fn_repl(m: re.Match) -> str: + comdat_names.append(m.group(3)) + return m.group(1) + ' comdat ' + m.group(4) + ll_content = re.sub( + r'^(define\s+(?:dso_local\s+)?(?:%s)\s+[^@]*@("?)([^"\s\(]+)\2[^{]*)(\{)' % weak_links, + _fn_repl, ll_content, flags=re.MULTILINE) + + # 2) 全局定义:@name = [dso_local] (global|constant) → 值后补 ', comdat' + # (comdat 需位于 align/section 等尾部属性之前,符合 LLVM IR 文法) + def _g_repl(m: re.Match) -> str: + line = m.group(0) + if ' comdat' in line: + return line + comdat_names.append(m.group(3)) + ins = re.search(r',\s*(?:align|section|partition|code_model)\b', line) + if ins: + return line[:ins.start()] + ', comdat' + line[ins.start():] + return line + ', comdat' + ll_content = re.sub( + r'^(@("?)([^"=\s]+)\2\s*=\s*(?:%s)\s+(?:dso_local\s+)?(?:global|constant)[^\n]*)' % weak_links, + _g_repl, ll_content, flags=re.MULTILINE) + + if not comdat_names: + return ll_content + decls: list[str] = [] + for name in dict.fromkeys(comdat_names): + if re.match(r'^[0-9A-Za-z_$.-]+$', name): + decls.append(f'${name} = comdat any') + else: + decls.append(f'$"{name}" = comdat any') + comdat_block = '\n'.join(decls) + '\n' + lines = ll_content.splitlines(True) + header_end = 0 + for i, line in enumerate(lines): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + header_end = i + 1 + continue + break + return ''.join(lines[:header_end]) + comdat_block + ''.join(lines[header_end:]) + + class Phase2Translator: """阶段二:使用声明接口翻译源文件""" @@ -1812,6 +1871,7 @@ class Phase2Translator: opt_content = re.sub(r'(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', opt_content, flags=re.MULTILINE) opt_content = re.sub(r'(@[\w.]+)\s*=\s*(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr)\s+(global|constant)', r'\1 = \2 dso_local \3', opt_content) opt_content = re.sub(r'(@[\w.]+)\s*=\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|dso_local)(global|constant)', r'\1 = dso_local \2', opt_content) + opt_content = _AddWeakComdat(opt_content) with open(optimized_ll, 'w', encoding='utf-8') as f: f.write(opt_content) merged_obj = os.path.join(self.output_dir, '_merged.obj') @@ -2045,6 +2105,7 @@ class Phase2Translator: ll_content = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', ll_content, flags=re.MULTILINE) ll_content = re.sub(r'^(@[\w.]+)\s*=\s*(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr)\s+(global|constant)', r'\1 = \2 dso_local \3', ll_content, flags=re.MULTILINE) ll_content = re.sub(r'^(@[\w.]+)\s*=\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|dso_local)(global|constant)', r'\1 = dso_local \2', ll_content, flags=re.MULTILINE) + ll_content = _AddWeakComdat(ll_content) # LLVM 22+: 移除同一模块中已有 define 的 declare 语句(LLVM 22 将 declare+define 视为重复定义) defined_funcs: set[str] = set() for m in re.finditer(r'^define\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(', ll_content, re.MULTILINE): @@ -2795,6 +2856,7 @@ class Phase2Translator: ll_content = re.sub(r'^(@[\w.]+)\s*=\s*external\s+global\s+(\w[\w*]*)\s+(-?\d+|null|zeroinitializer|true|false)$', r'\1 = linkonce global \2 \3', ll_content, flags=re.MULTILINE) ll_content = re.sub(r'^(@[\w.]+)\s*=\s*(private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr)\s+(global|constant)', r'\1 = \2 dso_local \3', ll_content, flags=re.MULTILINE) ll_content = re.sub(r'^(@[\w.]+)\s*=\s+(?!private|internal|available_externally|linkonce|weak|common|appending|extern_weak|linkonce_odr|weak_odr|dso_local)(global|constant)', r'\1 = dso_local \2', ll_content, flags=re.MULTILINE) + ll_content = _AddWeakComdat(ll_content) # stub_decls 注入(复用预收集的 stub_decls) if stub_decls: