fix(link): 为 weak/linkonce 函数与全局补 COMDAT,修复 COFF 下跨模块重复符号
COFF 下无 COMDAT 的 weak/linkonce 定义会被 llc 展开成强符号 .weak.<name>.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 同符号链接无重复报错)。
This commit is contained in:
1 parent
cb8eef3316
commit
b02bedae9a
1 file changed
+62
@@ -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.<name>.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 <linkage> [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 = <linkage> [dso_local] (global|constant) <init> → 值后补 ', 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:
|
||||
|
||||
Reference in new issue
Block a user