diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index 53dc1c7..65976e0 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -153,7 +153,29 @@ _DWARF_TAG_ANY_RE = re.compile(r'DW_TAG_\w+') _DWARF_TAG_SUBPROGRAM_RE = re.compile(r'DW_TAG_subprogram\b') _DWARF_LOWPC_RE = re.compile(r'DW_AT_low_pc\s*\(\s*(0x[0-9A-Fa-f]+)\s*\)') _DWARF_HIGHPC_RE = re.compile(r'DW_AT_high_pc\s*\(\s*(0x[0-9A-Fa-f]+)\s*\)') -_DWARF_ATNAME_RE = re.compile(r'DW_AT_name\s*\("((?:[^"\\]|\\.)*)"\)') +_DWARF_ATNAME_RE = re.compile(r'DW_AT_name\s*\("((?:[^"\\]|\\.)*)"\)') + +# --------------------------------------------------------------- +# `_split_ll` 的逐行正则**预编译**(性能:该函数对合并后的 .ll 逐行跑两遍, +# prof 实测 43.9万行级输入下 re 模块级函数的缓存查找(re._compile)达 1970 万次 +# ≈ 7s,另有 3100 万次 re.Pattern.match;每次 `re.match(字面量, ...)` 都要做 +# "模式串哈希 → 缓存查找"。预编译成模块级常量后省掉这层查找,且匹配路径更短。 +# 语义与逐字写法完全一致(模式串一字未改)。 +# --------------------------------------------------------------- +_RE_DEFINE_SHA1: re.Pattern[str] = re.compile(r'define\s+[^@]*@"?([a-f0-9]+)\.') +_RE_DEFINE_NAME: re.Pattern[str] = re.compile(r'define\s+[^@]*@"?([^"\s(]+)"?') +_RE_DECLARE_NAME: re.Pattern[str] = re.compile(r'declare\s+[^@]*@"?([^"\s(]+)"?') +_RE_LLVM_INTRINSIC: re.Pattern[str] = re.compile(r'@("?)llvm\.') +_RE_AT_NAME: re.Pattern[str] = re.compile(r'(@\S+)') +_RE_SHA1_16_DOT: re.Pattern[str] = re.compile(r'^[a-f0-9]{16}\.') +_RE_DEFINE_PREFIX: re.Pattern[str] = re.compile(r'^define\s+') +_RE_DEFINE_LINKAGE: re.Pattern[str] = re.compile( + r'\b(linkonce_odr|weak_odr|linkonce|weak|common|appending|internal|private)\s+') +_RE_ALWAYSINLINE: re.Pattern[str] = re.compile(r'\balwaysinline\s+') +# `_compile_one` 的逐行符号扫描/全局去重里用到的两条(同一预编译理由) +_RE_AT_EXTERNAL: re.Pattern[str] = re.compile(r'@\S+\s*=\s*external\s+') +_RE_DECLARE_FULL: re.Pattern[str] = re.compile( + r'^declare\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(') def _ParseDwarfLineRows(dump_text: str) -> list[tuple[int, str, int]]: @@ -1614,13 +1636,19 @@ class Phase2Translator: defined_names = set() for line in ll_content.splitlines(True): + # 第一遍只关心 `define ...` 行,而合并后的 .ll 里这类行占比极低。 + # `'define' not in line` 是 C 级子串扫描 ⇒ 绝大多数行连 strip 都不做 + # (prof 实测 str.strip 1.79 亿次 / str.startswith 2.37 亿次是全流程 + # 最大的 Python 侧热点,本函数两遍逐行扫描是其主要来源)。 + if 'define' not in line: + continue stripped = line.strip() if stripped.startswith('define '): - dm = re.match(r'define\s+[^@]*@"?([a-f0-9]+)\.', stripped) + dm = _RE_DEFINE_SHA1.match(stripped) if dm and module_sha1 is None: module_sha1 = dm.group(1) - dm2 = re.match(r'define\s+[^@]*@"?([^"\s(]+)"?', stripped) + dm2 = _RE_DEFINE_NAME.match(stripped) if dm2: defined_names.add(dm2.group(1)) @@ -1630,12 +1658,14 @@ class Phase2Translator: global_brace_depth = 0 for line in ll_content.splitlines(True): - stripped = line.strip() - + # 续行(函数体指令 / 全局初始化器)在合并 .ll 里占绝大多数,而这两支 + # 只需要"行内容 + 花括号计数" ⇒ 不进 strip(前导/尾随空白不含花括号, + # _count_braces_outside_strings 的结果逐字不变)。prof 实测 str.strip + # 1.79 亿次为最大 Python 侧热点,逐行 strip 是其主要来源。 if in_global_def: text_lines.append(line) - global_type_parts.append(stripped) - global_brace_depth += _count_braces_outside_strings(stripped) + global_type_parts.append(line.strip()) + global_brace_depth += _count_braces_outside_strings(line) if global_brace_depth <= 0: full_type = ' '.join(global_type_parts).strip() depth = 0 @@ -1659,18 +1689,19 @@ class Phase2Translator: if in_define: text_lines.append(line) - brace_depth += _count_braces_outside_strings(stripped) + brace_depth += _count_braces_outside_strings(line) if brace_depth <= 0: in_define = False continue + stripped = line.strip() if stripped.startswith('define '): text_lines.append(line) in_define = True brace_depth = _count_braces_outside_strings(stripped) - decl_line = re.sub(r'^define\s+', 'declare ', stripped) - decl_line = re.sub(r'\b(linkonce_odr|weak_odr|linkonce|weak|common|appending|internal|private)\s+', '', decl_line) - decl_line = re.sub(r'\balwaysinline\s+', '', decl_line) + decl_line = _RE_DEFINE_PREFIX.sub('declare ', stripped) + decl_line = _RE_DEFINE_LINKAGE.sub('', decl_line) + decl_line = _RE_ALWAYSINLINE.sub('', decl_line) paren_pos = decl_line.rfind(')') if paren_pos >= 0: decl_line = decl_line[:paren_pos + 1] @@ -1698,10 +1729,10 @@ class Phase2Translator: # LLVM 保留全局(如 --hotspot-sites 的 @llvm.global_ctors;名字含点故 # 最终 IR 保留引号)是 appending 定义,禁止改写为 "external global" # 存根(否则 .ll 重组后 llc 报错) - if re.match(r'@("?)llvm\.', stripped): + if _RE_LLVM_INTRINSIC.match(stripped): continue if ' internal ' not in stripped and ' private ' not in stripped: - name_match = re.match(r'(@\S+)', stripped) + name_match = _RE_AT_NAME.match(stripped) if name_match: var_name = name_match.group(1) global_pos = stripped.find(' global ') @@ -1736,7 +1767,7 @@ class Phase2Translator: continue if stripped.startswith('declare'): - dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped) + dm = _RE_DECLARE_NAME.match(stripped) if dm: fname = dm.group(1) if fname in defined_names: @@ -1747,7 +1778,7 @@ class Phase2Translator: fname_sha1 = fname[:dot_pos] if module_sha1 and fname_sha1 != module_sha1 and len(fname_sha1) >= 16: continue - if re.match(r'^[a-f0-9]{16}\.', fname): + if _RE_SHA1_16_DOT.match(fname): continue stub_lines.append(line) continue @@ -2475,10 +2506,10 @@ class Phase2Translator: for i, line in enumerate(ll_lines_for_dedup): stripped = line.strip() if stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): - gm = re.match(r'(@\S+)', stripped) + gm = _RE_AT_NAME.match(stripped) if gm: gname = gm.group(1) - is_external = re.match(r'@\S+\s*=\s*external\s+', stripped) is not None + is_external = _RE_AT_EXTERNAL.match(stripped) is not None if is_external: external_globals[gname] = i else: @@ -2507,7 +2538,7 @@ class Phase2Translator: else: existing_symbols[name] = 'full' elif stripped.startswith('declare'): - dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped) + dm = _RE_DECLARE_NAME.match(stripped) if dm: fname = dm.group(1) existing_declares.add(fname) @@ -2525,11 +2556,11 @@ class Phase2Translator: for line in ll_content.splitlines(): stripped = line.strip() if stripped.startswith('define'): - dm = re.match(r'define\s+[^@]*@"?([^"\s(]+)"?', stripped) + dm = _RE_DEFINE_NAME.match(stripped) if dm: existing_defines.add(dm.group(1)) elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): - gm = re.match(r'(@\S+)', stripped) + gm = _RE_AT_NAME.match(stripped) if gm: existing_global_defs.add(gm.group(1)) for dep_name, dep_sha1 in deps.items(): @@ -2547,7 +2578,7 @@ class Phase2Translator: if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: continue if stripped.startswith('declare'): - dm = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', stripped) + dm = _RE_DECLARE_NAME.match(stripped) if dm: fname = dm.group(1) if 'zdef_alloc' in fname: @@ -2562,7 +2593,7 @@ class Phase2Translator: if has_struct and not existing_has_struct: for i, l in enumerate(all_stub_lines): if l is not None and l.strip().startswith('declare'): - em = re.match(r'declare\s+[^@]*@"?([^"\s(]+)"?', l.strip()) + em = _RE_DECLARE_NAME.match(l.strip()) if em and em.group(1) == fname: all_stub_lines[i] = line break @@ -2606,7 +2637,7 @@ class Phase2Translator: else: all_stub_lines.append(line) elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): - gm = re.match(r'(@\S+)', stripped) + gm = _RE_AT_NAME.match(stripped) if gm and (gm.group(1) in existing_global_defs or gm.group(1) in stub_global_defs): continue if gm: @@ -2670,7 +2701,7 @@ class Phase2Translator: lines: list[str] = ll_content.splitlines(True) kept_lines: list[str] = [] for line in lines: - m = re.match(r'^declare\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(', line) + m = _RE_DECLARE_FULL.match(line) if m and m.group(1) in defined_funcs: continue kept_lines.append(line) @@ -2680,7 +2711,7 @@ class Phase2Translator: dedup_lines: list[str] = ll_content.splitlines(True) dedup_kept: list[str] = [] for line in dedup_lines: - dm = re.match(r'^declare\s+(?:dso_local\s+)?[^@]*@\"?([^"\s\(]+)\"?\s*\(', line) + dm = _RE_DECLARE_FULL.match(line) if dm: fname = dm.group(1) if fname in seen_declares: diff --git a/lib/core/LLVMCG/IRMetadataFix.py b/lib/core/LLVMCG/IRMetadataFix.py index 9f50f73..b07df80 100644 --- a/lib/core/LLVMCG/IRMetadataFix.py +++ b/lib/core/LLVMCG/IRMetadataFix.py @@ -27,8 +27,9 @@ _NamedDefinition = re.compile(r'^!([A-Za-z][\w.]*)\s*=\s*(.*)$') _ListForm = re.compile(r'^!\{(.*)\}$', re.DOTALL) # 纯 id 引用 `!N` _PureId = re.compile(r'^!(\d+)$') -# Decimal digit set for the no-regex fast rewrite path. -_DIGITS: frozenset[str] = frozenset('0123456789') +# 前导数字字符集(str 形式):供 str.lstrip 的 **C 级**扫描用 +# (_RewritePlain 在合并 .ll 上是百万级调用,原逐字符 Python 循环是 self time 主源)。 +_DIGITS_STR: str = '0123456789' def _SplitTopLevel(Content: str) -> list[str]: @@ -92,10 +93,11 @@ def _RewritePlain(Line: str, StrMap: dict[str, str]) -> str: Ap = Out.append Get = StrMap.get for Part in Parts[1:]: - d: int = 0 n: int = len(Part) - while d < n and Part[d] in _DIGITS: - d += 1 + # 前导数字个数改由 str.lstrip 的 **C 级**扫描给出(原逐字符 Python 循环 + # `while d < n and Part[d] in _DIGITS`;本函数 prof 实测 5.8M 次调用, + # 是 self time 最大的项目内函数)。语义与循环逐字等价:都是"前导数字个数"。 + d: int = n - len(Part.lstrip(_DIGITS_STR)) if not d: Ap('!') Ap(Part) @@ -160,9 +162,12 @@ def SanitizeIRMetadata(text: str) -> str: Blocks: list[tuple[int, int]] = [] # [Start, End) i: int = 0 while i < LineCount: - if Segments[i].startswith('!'): + # 单个字符的判定用索引切片(seg[:1])替代 str.startswith:本循环对合并 .ll + # 的**每一行**各跑一次(prof 实测 str.startswith 237M 次为全流程最大热点), + # 索引切片无方法查找开销、语义与 startswith('!') 等价(空行得 ''≠'!')。 + if Segments[i][:1] == '!': j: int = i + 1 - while j < LineCount and Segments[j].startswith('!'): + while j < LineCount and Segments[j][:1] == '!': j += 1 Blocks.append((i, j)) i = j