Files
2026-07-13 13:33:03 +08:00

291 lines
12 KiB
Python

import re
import sys
import subprocess
import os
import difflib
import re
import sys
import difflib
SIMILARITY_THRESHOLD = 0.85
SYNONYM_RULES = [
# 规则 1 & 2: 简单的 dup/mov 同义词
(re.compile(r'^(dup)\s+(.*)$'), "mov {1}", "dup -> mov"),
(re.compile(r'^(mov)\s+(z\d+\.q,\s*z\d+\.q\[\d+\])$'), "dup {1}", "mov z.q -> dup z.q"),
# 规则 3 & 4: 仅限 ptrue 的 #N <-> vlN
(re.compile(r'^(ptrue\s+p\d+\.[bshd]\s*,)\s*#(\d+)(?:\.0)?$'), "{0} vl{1}", "ptrue..., #${N} -> ..."),
(re.compile(r'^(ptrue\s+p\d+\.[bshd]\s*,)\s*vl(\d+)$'), "{0} #{1}", "ptrue..., vl${N} -> ..."),
# 规则 5 & 6 (新增): 处理 z<R>.<S>[0] <-> <S><R> 的等价关系
# 例如: z29.h[0] -> h29
(
re.compile(r'^(.*,)\s*z(\d+)\.([bshd])\[0\]$'),
lambda m: f"{m.group(1)} {m.group(3)}{m.group(2)}",
"..., z<R>.<S>[0] -> ..., <S><R>"
),
# # 例如: h29 -> z29.h[0]
(
re.compile(r'^(.*,)\s*([bshd])(\d+)$'),
lambda m: f"{m.group(1)} z{m.group(3)}.{m.group(2)}[0]",
"..., <S><R> -> ..., z<R>.<S>[0]"
),
]
def get_canonical_form(line):
line = line.split('//')[0].split('@')[0]
line = re.sub(r'\s+', ' ', line).strip().lower()
if not line: return ""
def number_replacer(match):
num_str = match.group(1)
try:
num_val = float(num_str)
if '.' not in num_str and 'e' not in num_str.lower(): return f'#{int(num_val)}'
return f'#{str(num_val)}'
except ValueError: return match.group(0)
line = re.sub(r'#([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)', number_replacer, line)
return line
def should_be_converted(instruction_line):
"""
根据用户定义的规则,判断一条指令是否应该被转换为 .inst 格式。
*** 此函数已更新以包含 'cnth' 和 'pn' 寄存器 ***
"""
# 规则1:如果指令助记符是特殊指令之一,则必须转换
parts = instruction_line.split()
if parts and parts[0] in {'addvl', 'cnth', 'cntw', 'smstart', 'smstop'}:
return True
# 规则2:如果包含 p, pn, z, 或 za 寄存器,则必须转换
# 正则表达式已更新以包含 pn<数字>
if re.search(r'\b(p\d+|pn\d+|z\d+|za+|zt0)', instruction_line):
return True
# 如果以上条件都不满足,则不转换
return False
def parse_objdump(objdump_file):
pattern = re.compile(r'^\s*[0-9a-f]+:\s+([0-9a-f]{8})\s+(.+)$')
instruction_map = {}
with open(objdump_file, 'r') as f:
for line in f:
match = pattern.match(line)
if match:
hex_code, asm_instruction = match.group(1), match.group(2).strip()
canonical_key = get_canonical_form(asm_instruction)
if canonical_key: instruction_map[canonical_key] = hex_code
return instruction_map
def generate_equivalent_instructions(canonical_line):
equivalents = {canonical_line} # 使用集合以自动去重
# 迭代处理,因为一个规则的输出可能是另一个规则的输入
items_to_process = [canonical_line]
while items_to_process:
line = items_to_process.pop(0)
for pattern, replacement, _ in SYNONYM_RULES:
match = pattern.match(line)
if match:
if callable(replacement):
# 如果替换规则是函数,则调用它
new_instr = replacement(match)
else:
# 否则,使用字符串格式化
new_instr = replacement.format(*match.groups())
if new_instr not in equivalents:
equivalents.add(new_instr)
items_to_process.append(new_instr)
return list(equivalents)
def expand_register_range(instruction):
"""
查找并展开指令中的寄存器范围,如 {z26.s-z27.s} -> {z26.s,z27.s}。
{z0.s-z3.s} 会被展开为 {z0.s,z1.s,z2.s,z3.s}。
"""
# 正则表达式模式:
# \{ \s* - 匹配 '{' 和可选空格
# ([a-zA-Z])(\d+) - 捕获组1(前缀), 捕获组2(起始编号)
# (\.\w+) - 捕获组3(后缀)
# \s*-\s* - 匹配 '-' 和可选空格
# \1(\d+)\3 - 匹配相同的组1(前缀), 捕获组4(结束编号), 相同的组3(后缀)
# \s* \} - 匹配可选空格和 '}'
pattern = re.compile(r'\{\s*([a-zA-Z])(\d+)(\.\w+)\s*-\s*\1(\d+)\3\s*\}')
# 定义一个替换函数,用于生成展开后的列表
def replacer(match):
prefix, start_num_str, suffix, end_num_str = match.groups()
start_num, end_num = int(start_num_str), int(end_num_str)
# 确保范围是有效的
if start_num >= end_num:
return match.group(0) # 如果范围无效,则不替换
# 使用列表推导生成所有寄存器名
regs = [f"{prefix}{i}{suffix}" for i in range(start_num, end_num + 1)]
# 将列表连接成一个无空格的字符串,并用花括号包裹
# 输出如: {z26.s,z27.s}
return f"{{{','.join(regs)}}}"
# 使用 re.sub 和我们的替换函数来执行替换
return pattern.sub(replacer, instruction)
def normalize_instruction(instruction):
"""
对汇编指令进行语义规范化。
- 步骤 0: 移除分号后的注释。
- 步骤 1: 展开寄存器范围 (例如, z26.s-z27.s)。
- 步骤 2: 规范化数字,统一转为十进制格式。
- 步骤 3: 仅保留助记符后的第一个空格,移除所有其他空格。
"""
# 步骤 0: 移除注释
instruction = instruction.split(';')[0].strip()
# 步骤 1: 展开寄存器范围
instruction = expand_register_range(instruction)
# 步骤 2: 规范化数字
match = re.search(r'#\s*(0x[0-9a-fA-F]+|[0-9]+)', instruction)
if match:
number_str = match.group(1)
try:
decimal_value = int(number_str, 0)
instruction = instruction.replace(match.group(0), f'#{decimal_value}')
except ValueError:
pass
# 步骤 3: 规范化空格
parts = instruction.split(' ', 1)
if len(parts) == 2:
mnemonic = parts[0]
operands = parts[1]
operands_no_space = operands.replace(' ', '')
return f"{mnemonic} {operands_no_space}"
else:
return instruction
def find_best_match(source_line, instruction_map):
matcher = difflib.SequenceMatcher(None, normalize_instruction(source_line))
best_match_key, highest_score = None, 0.0
for key in instruction_map.keys():
keyNormalized = normalize_instruction(key)
matcher.set_seq2(keyNormalized)
score = matcher.ratio()
if score > highest_score: highest_score, best_match_key = score, key
return best_match_key, highest_score
def find_mnemonic_matches(source_line, instruction_map):
"""
新增函数:查找所有指令助记符相同的指令。
"""
source_mnemonic = source_line.split()[0] if source_line else ""
if not source_mnemonic:
return []
matches = []
for key in instruction_map.keys():
if key.split()[0] == source_mnemonic:
matches.append(key)
return matches
def process_assembly_file(s_file_path, instruction_map, output_file_path):
"""
主处理函数,已集成新的过滤逻辑和增强的错误报告。
"""
with open(s_file_path, 'r') as f_in, open(output_file_path, 'w') as f_out:
for line_num, line in enumerate(f_in, 1):
match = re.match(r'^(\s*)(.*)$', line)
indentation, content_with_comment = match.group(1), match.group(2).strip()
if not content_with_comment or content_with_comment.startswith(('.', '//', '#', '@')) or content_with_comment.endswith(':'):
f_out.write(line)
continue
canonical_content = get_canonical_form(content_with_comment)
if not should_be_converted(canonical_content):
f_out.write(line)
continue
found_match = False
hex_code = None
equivalent_candidates = generate_equivalent_instructions(canonical_content)
for candidate in equivalent_candidates:
if candidate in instruction_map:
hex_code = instruction_map[candidate]
found_match = True
if candidate != canonical_content:
print(f"提示 (行 {line_num}): 使用等价匹配 '{canonical_content}' -> '{candidate}'")
break
if found_match:
new_line = f"{indentation}.inst 0x{hex_code} // {content_with_comment}\n"
f_out.write(new_line)
continue
# --- 全新的、增强的错误报告逻辑 ---
print("--------------------------------------------------")
# 报告1: 全局最相似的匹配
best_match_key, score = find_best_match(canonical_content, instruction_map)
# if best_match_key:
# print(f" -> 全局最相似的匹配是 '{best_match_key}' (相似度: {score:.2%})")
# 报告2: 所有助记符相同的匹配
# mnemonic_matches = find_mnemonic_matches(canonical_content, instruction_map)
# if mnemonic_matches:
# source_mnemonic = canonical_content.split()[0]
# print(f" -> 在 Objdump 中找到以下助记符为 '{source_mnemonic}' 的指令:")
# for m_match in mnemonic_matches:
# print(f" - '{m_match}'")
if score > SIMILARITY_THRESHOLD:
print(f"警告 (行 {line_num}): '{content_with_comment}' 与 '{best_match_key}' 的相似度为 {score:.2%},这里同样进行替换。请检查是否正确。")
new_line = f"{indentation}.inst 0x{instruction_map[best_match_key]} // {content_with_comment}\n"
f_out.write(new_line)
else:
print(f"错误 (行 {line_num}): 无法为 '{content_with_comment}' 找到任何直接或等价的匹配项,最相似匹配是 {best_match_key}, 相似分是{score} 请检查指令或手动添加支持。")
f_out.write(line) # 保持原样
def main():
if len(sys.argv) != 2:
print("用法: python arm2binary.py <原始S文件>")
print("例如: python arm2binary.py MNNPackedMatMulRemainFP32_SME2.S")
sys.exit(1)
s_file = sys.argv[1]
output_file = s_file.replace('.S', '_with_inst.S')
# 生成临时objdump文件名
binary_file = 'temp.o'
objdump_file = 'temp_objdump.txt'
current_directory = os.path.dirname(os.path.abspath(__file__))
level1_directory = os.path.dirname(current_directory)
level2_directory = os.path.dirname(level1_directory)
header_directory = os.path.join(level2_directory, 'source/backend/cpu/arm')
build_cmd = f"gcc -c -fno-tree-vectorize -march=armv8.2-a+sve+sve2+sme+sme2+fp16 {s_file} -I{header_directory} -o {binary_file}"
subprocess.check_output(build_cmd, shell=True).decode()
objdump_cmd = f"objdump -d {binary_file} > {objdump_file}"
subprocess.check_output(objdump_cmd, shell=True).decode()
print(f"1. 正在解析机器码...")
instruction_map = parse_objdump(objdump_file)
if not instruction_map:
print("错误:未能从objdump文件中解析出任何指令。请检查文件内容。")
sys.exit(1)
print(f" ...成功解析 {len(instruction_map)} 条指令。")
print(f"2. 正在处理汇编文件...")
process_assembly_file(s_file, instruction_map, output_file)
copy_cmd = f"cp {output_file} {s_file}"
subprocess.check_output(copy_cmd, shell=True).decode()
print(" ...处理完成!")
rm_cmd = f"rm {binary_file} {objdump_file} {output_file}"
subprocess.check_output(rm_cmd, shell=True).decode()
if __name__ == "__main__":
main()