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: