Fuck git
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import argparse
|
||||
|
||||
import logging as log
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
from enum import StrEnum
|
||||
from pathlib import Path
|
||||
|
||||
from autosync.cpptranslator.Configurator import Configurator
|
||||
from autosync.cpptranslator.CppTranslator import Translator
|
||||
from autosync.HeaderPatcher import CompatHeaderBuilder, HeaderPatcher
|
||||
from autosync.Helper import convert_loglevel, fail_exit, get_path
|
||||
|
||||
from autosync.IncGenerator import IncGenerator
|
||||
|
||||
from autosync.MCUpdater import MCUpdater
|
||||
from autosync.Targets import ARCH_LLVM_NAMING, TARGET_TO_DIR_NAME
|
||||
|
||||
|
||||
class USteps(StrEnum):
|
||||
INC_GEN = "IncGen"
|
||||
TRANS = "Translate"
|
||||
DIFF = "Diff"
|
||||
MC = "MCUpdate"
|
||||
PATCH_HEADER = "PatchArchHeader"
|
||||
ALL = "All"
|
||||
|
||||
|
||||
class ASUpdater:
|
||||
"""
|
||||
The auto-sync updater.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
arch: str,
|
||||
write: bool,
|
||||
steps: list[USteps],
|
||||
inc_list: list,
|
||||
no_clean: bool,
|
||||
copy_translated: bool,
|
||||
differ_no_auto_apply: bool,
|
||||
wait_for_user: bool = True,
|
||||
) -> None:
|
||||
self.arch = arch
|
||||
self.arch_dir_name = TARGET_TO_DIR_NAME[self.arch]
|
||||
self.write = write
|
||||
self.no_clean_build = no_clean
|
||||
self.inc_list = inc_list
|
||||
self.wait_for_user = wait_for_user
|
||||
if USteps.ALL in steps:
|
||||
self.steps = [
|
||||
USteps.INC_GEN,
|
||||
USteps.TRANS,
|
||||
USteps.DIFF,
|
||||
USteps.MC,
|
||||
USteps.PATCH_HEADER,
|
||||
]
|
||||
else:
|
||||
self.steps = steps
|
||||
self.copy_translated = copy_translated
|
||||
self.differ_no_auto_apply = differ_no_auto_apply
|
||||
self.arch_dir = get_path("{CS_ARCH_MODULE_DIR}").joinpath(
|
||||
TARGET_TO_DIR_NAME[self.arch]
|
||||
)
|
||||
if not self.no_clean_build:
|
||||
self.clean_build_dir()
|
||||
self.inc_generator = IncGenerator(
|
||||
self.arch,
|
||||
self.inc_list,
|
||||
)
|
||||
with open(get_path("{MCUPDATER_CONFIG_FILE}")) as f:
|
||||
self.mcupdater_conf = json.loads(f.read())
|
||||
|
||||
self.mc_updater = MCUpdater(
|
||||
self.arch,
|
||||
get_path("{LLVM_MC_TEST_DIR}"),
|
||||
None,
|
||||
None,
|
||||
self.arch in self.mcupdater_conf["unify_test_cases"],
|
||||
multi_mode=True,
|
||||
)
|
||||
|
||||
def clean_build_dir(self) -> None:
|
||||
log.info("Clean build directory")
|
||||
path: Path
|
||||
for path in get_path("{BUILD_DIR}").iterdir():
|
||||
log.debug(f"Delete {path}")
|
||||
if path.is_dir():
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.remove(path)
|
||||
|
||||
def patch_main_header(self) -> list:
|
||||
"""
|
||||
Patches the main header of the arch with the .inc files.
|
||||
It returns a list of files it has patched into the main header.
|
||||
"""
|
||||
if not self.write:
|
||||
return []
|
||||
main_header = get_path("{CS_INCLUDE_DIR}").joinpath(f"{self.arch.lower()}.h")
|
||||
# Just try every inc file
|
||||
patched = []
|
||||
for file in get_path("{C_INC_OUT_DIR}").iterdir():
|
||||
patcher = HeaderPatcher(main_header, file)
|
||||
if patcher.patch_header():
|
||||
# Save the path. This file should not be moved.
|
||||
patched.append(file)
|
||||
if self.arch == "AArch64":
|
||||
builder = CompatHeaderBuilder(
|
||||
v6=main_header,
|
||||
v5=get_path("{CS_INCLUDE_DIR}").joinpath(f"arm64.h"),
|
||||
arch="aarch64",
|
||||
)
|
||||
builder.generate_v5_compat_header()
|
||||
elif self.arch == "AArch64":
|
||||
builder = CompatHeaderBuilder(
|
||||
v6=main_header,
|
||||
v5=get_path("{CS_INCLUDE_DIR}").joinpath(f"systemz_compatibility.h"),
|
||||
arch="systemz",
|
||||
)
|
||||
builder.generate_v5_compat_header()
|
||||
return patched
|
||||
|
||||
def copy_files(self, path: Path, dest: Path) -> None:
|
||||
"""
|
||||
Copies files from path to dest.
|
||||
If path is a directory it copies all files in it.
|
||||
If it is a file, it only copies it.
|
||||
"""
|
||||
if not self.write:
|
||||
return
|
||||
if not dest.is_dir():
|
||||
fail_exit(f"{dest} is not a directory.")
|
||||
|
||||
if path.is_file():
|
||||
log.debug(f"Copy {path} to {dest}")
|
||||
shutil.copy(path, dest)
|
||||
return
|
||||
|
||||
for file in path.iterdir():
|
||||
log.debug(f"Copy {path} to {dest}")
|
||||
shutil.copy(file, dest)
|
||||
|
||||
def check_tree_sitter(self) -> None:
|
||||
ts_dir = get_path("{VENDOR_DIR}").joinpath("tree-sitter-cpp")
|
||||
if not ts_dir.exists():
|
||||
log.info("tree-sitter was not fetched. Cloning it now...")
|
||||
subprocess.run(
|
||||
["git", "submodule", "update", "--init", "--recursive"], check=True
|
||||
)
|
||||
|
||||
def translate(self) -> None:
|
||||
self.check_tree_sitter()
|
||||
translator_config = get_path("{CPP_TRANSLATOR_CONFIG}")
|
||||
configurator = Configurator(self.arch, translator_config)
|
||||
translator = Translator(configurator, self.wait_for_user)
|
||||
translator.translate()
|
||||
translator.remark_manual_files()
|
||||
|
||||
def diff(self) -> None:
|
||||
translator_config = get_path("{CPP_TRANSLATOR_CONFIG}")
|
||||
configurator = Configurator(self.arch, translator_config)
|
||||
from autosync.cpptranslator.Differ import Differ
|
||||
|
||||
differ = Differ(configurator, self.differ_no_auto_apply)
|
||||
differ.diff()
|
||||
|
||||
def update(self) -> None:
|
||||
if USteps.INC_GEN in self.steps:
|
||||
self.inc_generator.generate()
|
||||
if USteps.PATCH_HEADER in self.steps:
|
||||
if self.write:
|
||||
patched = self.patch_main_header()
|
||||
log.info(f"Patched {len(patched)} .inc files into the main header.")
|
||||
else:
|
||||
log.info("Patching the main header requires the -w flag.")
|
||||
if USteps.TRANS in self.steps:
|
||||
self.translate()
|
||||
if USteps.DIFF in self.steps:
|
||||
self.diff()
|
||||
if USteps.MC in self.steps:
|
||||
self.mc_updater.gen_all()
|
||||
self.mc_updater.write_to_build_dir()
|
||||
self.mc_updater.write_to_build_dir(fuzzer_tests=True)
|
||||
if not self.write:
|
||||
if self.inc_generator.has_inc_patches():
|
||||
log.warning(
|
||||
f"Patches to inc files are only applied with the -w flag. This wasn't done. Find them in {get_path('{INC_PATCH_DIR}')}"
|
||||
)
|
||||
# Done
|
||||
exit(0)
|
||||
|
||||
# Copy .inc files
|
||||
log.info(f"Copy .inc files to {self.arch_dir}")
|
||||
i = 0
|
||||
arch_header = get_path("{CS_INCLUDE_DIR}").joinpath(f"{self.arch.lower()}.h")
|
||||
for file in get_path("{C_INC_OUT_DIR}").iterdir():
|
||||
if HeaderPatcher.file_in_main_header(arch_header, file.name):
|
||||
continue
|
||||
self.copy_files(file, self.arch_dir)
|
||||
i += 1
|
||||
self.inc_generator.apply_patches()
|
||||
log.info(f"Copied {i} files")
|
||||
|
||||
i = 0
|
||||
if self.copy_translated:
|
||||
# Diffed files
|
||||
log.info(f"Copy translated files to {self.arch_dir}")
|
||||
for file in get_path("{CPP_TRANSLATOR_TRANSLATION_OUT_DIR}").iterdir():
|
||||
self.copy_files(file, self.arch_dir)
|
||||
i += 1
|
||||
else:
|
||||
# Diffed files
|
||||
log.info(f"Copy diffed files to {self.arch_dir}")
|
||||
for file in get_path("{CPP_TRANSLATOR_DIFF_OUT_DIR}").iterdir():
|
||||
self.copy_files(file, self.arch_dir)
|
||||
i += 1
|
||||
log.info(f"Copied {i} files")
|
||||
|
||||
# MC tests
|
||||
i = 0
|
||||
mc_dir = get_path("{MC_DIR}").joinpath(self.arch_dir_name)
|
||||
log.info(f"Copy MC test files to {mc_dir}")
|
||||
for file in get_path("{MCUPDATER_OUT_DIR}").iterdir():
|
||||
self.copy_files(file, mc_dir)
|
||||
i += 1
|
||||
legacy_mc_dir = get_path("{LEGACY_MC_DIR}").joinpath(self.arch_dir_name)
|
||||
for file in get_path("{MCUPDATER_OUT_FUZZ_DIR}").iterdir():
|
||||
self.copy_files(file, legacy_mc_dir)
|
||||
i += 1
|
||||
log.info(f"Copied {i} files")
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="Auto-Sync-Updater",
|
||||
description="Capstones architecture module updater.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
dest="arch",
|
||||
help="Name of target architecture.",
|
||||
choices=ARCH_LLVM_NAMING,
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
dest="no_clean",
|
||||
help="Don't clean build dir before updating.",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-w",
|
||||
dest="write",
|
||||
help="Write generated/translated files to arch/<ARCH>/",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
dest="verbosity",
|
||||
help="Verbosity of the log messages.",
|
||||
choices=["debug", "info", "warning", "fatal"],
|
||||
default="info",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
dest="no_auto_apply",
|
||||
help="Differ: Do not apply saved diff resolutions. Ask for every diff again.",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
dest="steps",
|
||||
help="List of update steps to perform. If omitted, it performs all update steps.",
|
||||
choices=[
|
||||
"All",
|
||||
"IncGen",
|
||||
"Translate",
|
||||
"Diff",
|
||||
"MCUpdate",
|
||||
"PatchArchHeader",
|
||||
],
|
||||
nargs="+",
|
||||
default=["All"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"--inc-list",
|
||||
dest="inc_list",
|
||||
help="Only generate the following inc files.",
|
||||
choices=[
|
||||
"All",
|
||||
"Disassembler",
|
||||
"AsmWriter",
|
||||
"RegisterInfo",
|
||||
"InstrInfo",
|
||||
"SubtargetInfo",
|
||||
"Mapping",
|
||||
"SystemOperand",
|
||||
],
|
||||
nargs="+",
|
||||
type=str,
|
||||
default=["All"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"--copy-translated",
|
||||
dest="copy_translated",
|
||||
help="Copy the translated files and not the files produced by the Differ.",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ci",
|
||||
dest="wait_for_user",
|
||||
help="The translator will not wait for user input when printing important logs.",
|
||||
action="store_false",
|
||||
)
|
||||
arguments = parser.parse_args()
|
||||
return arguments
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
log.basicConfig(
|
||||
level=convert_loglevel(args.verbosity),
|
||||
stream=sys.stdout,
|
||||
format="%(levelname)-5s - %(message)s",
|
||||
force=True,
|
||||
)
|
||||
|
||||
Updater = ASUpdater(
|
||||
args.arch,
|
||||
args.write,
|
||||
args.steps,
|
||||
args.inc_list,
|
||||
args.no_clean,
|
||||
args.copy_translated,
|
||||
args.no_auto_apply,
|
||||
args.wait_for_user,
|
||||
)
|
||||
Updater.update()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,352 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import argparse
|
||||
import logging as log
|
||||
import re
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
AARCH64_CC_MACROS = [
|
||||
"\n",
|
||||
"#define arm64_cc AArch64CC_CondCode\n",
|
||||
"#define ARM64_CC_EQ AArch64CC_EQ\n",
|
||||
"#define ARM64_CC_NE AArch64CC_NE\n",
|
||||
"#define ARM64_CC_HS AArch64CC_HS\n",
|
||||
"#define ARM64_CC_LO AArch64CC_LO\n",
|
||||
"#define ARM64_CC_MI AArch64CC_MI\n",
|
||||
"#define ARM64_CC_PL AArch64CC_PL\n",
|
||||
"#define ARM64_CC_VS AArch64CC_VS\n",
|
||||
"#define ARM64_CC_VC AArch64CC_VC\n",
|
||||
"#define ARM64_CC_HI AArch64CC_HI\n",
|
||||
"#define ARM64_CC_LS AArch64CC_LS\n",
|
||||
"#define ARM64_CC_GE AArch64CC_GE\n",
|
||||
"#define ARM64_CC_LT AArch64CC_LT\n",
|
||||
"#define ARM64_CC_GT AArch64CC_GT\n",
|
||||
"#define ARM64_CC_LE AArch64CC_LE\n",
|
||||
"#define ARM64_CC_AL AArch64CC_AL\n",
|
||||
"#define ARM64_CC_NV AArch64CC_NV\n",
|
||||
"#define ARM64_CC_INVALID AArch64CC_Invalid\n",
|
||||
"#define ARM64_VAS_INVALID AARCH64LAYOUT_INVALID\n",
|
||||
"#define ARM64_VAS_16B AARCH64LAYOUT_VL_16B\n",
|
||||
"#define ARM64_VAS_8B AARCH64LAYOUT_VL_8B\n",
|
||||
"#define ARM64_VAS_4B AARCH64LAYOUT_VL_4B\n",
|
||||
"#define ARM64_VAS_1B AARCH64LAYOUT_VL_1B\n",
|
||||
"#define ARM64_VAS_8H AARCH64LAYOUT_VL_8H\n",
|
||||
"#define ARM64_VAS_4H AARCH64LAYOUT_VL_4H\n",
|
||||
"#define ARM64_VAS_2H AARCH64LAYOUT_VL_2H\n",
|
||||
"#define ARM64_VAS_1H AARCH64LAYOUT_VL_1H\n",
|
||||
"#define ARM64_VAS_4S AARCH64LAYOUT_VL_4S\n",
|
||||
"#define ARM64_VAS_2S AARCH64LAYOUT_VL_2S\n",
|
||||
"#define ARM64_VAS_1S AARCH64LAYOUT_VL_1S\n",
|
||||
"#define ARM64_VAS_2D AARCH64LAYOUT_VL_2D\n",
|
||||
"#define ARM64_VAS_1D AARCH64LAYOUT_VL_1D\n",
|
||||
"#define ARM64_VAS_1Q AARCH64LAYOUT_VL_1Q\n",
|
||||
"#define arm64_vas AArch64Layout_VectorLayout\n",
|
||||
]
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="PatchHeaders",
|
||||
description="Patches generated enums into the main arch header file.",
|
||||
)
|
||||
parser.add_argument("--header", dest="header", help="Path header file.", type=Path)
|
||||
parser.add_argument("--inc", dest="inc", help="Path inc file.", type=Path)
|
||||
parser.add_argument(
|
||||
"--v6", dest="v6", help="aarch64.h/systemz.h header file location", type=Path
|
||||
)
|
||||
parser.add_argument(
|
||||
"--v5", dest="v5", help="arm64.h/systemz_v5.h header file location", type=Path
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", dest="compat", help="Generate compatibility header", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", dest="patch", help="Patch inc file into header", action="store_true"
|
||||
)
|
||||
arguments = parser.parse_args()
|
||||
return arguments
|
||||
|
||||
|
||||
def error_exit(msg: str) -> None:
|
||||
log.fatal(f"{msg}")
|
||||
exit(1)
|
||||
|
||||
|
||||
class HeaderPatcher:
|
||||
def __init__(self, header: Path, inc: Path, write_file: bool = True) -> None:
|
||||
self.header = header
|
||||
self.inc = inc
|
||||
self.inc_content: str = ""
|
||||
self.write_file = write_file
|
||||
# Gets set to the patched file content if writing to the file is disabled.
|
||||
self.patched_header_content: str = ""
|
||||
|
||||
def patch_header(self) -> bool:
|
||||
if not (self.header.exists() or self.header.is_file()):
|
||||
error_exit(f"self.header file {self.header.name} does not exist.")
|
||||
|
||||
if not (self.inc.exists() or self.inc.is_file()):
|
||||
error_exit(f"self.inc file {self.inc.name} does not exist.")
|
||||
|
||||
with open(self.header) as f:
|
||||
header_content = f.read()
|
||||
|
||||
if self.inc.name not in header_content:
|
||||
log.debug(f"{self.inc.name} has no include comments in {self.header.name}")
|
||||
return False
|
||||
|
||||
with open(self.inc) as f:
|
||||
self.inc_content = f.read()
|
||||
|
||||
to_write: dict[str:str] = {}
|
||||
enum_vals_id = ""
|
||||
for line in self.inc_content.splitlines():
|
||||
# No comments and empty lines
|
||||
if "/*" == line[:2] or not line:
|
||||
continue
|
||||
|
||||
if "#ifdef" in line:
|
||||
enum_vals_id = line[7:].strip("\n")
|
||||
to_write[enum_vals_id] = ""
|
||||
elif "#endif" in line and not enum_vals_id == "NOTGIVEN":
|
||||
enum_vals_id = ""
|
||||
elif "#undef" in line:
|
||||
continue
|
||||
else:
|
||||
line = re.sub(r"^(\s+)?", "\t", line)
|
||||
if not enum_vals_id:
|
||||
enum_vals_id = "NOTGIVEN"
|
||||
to_write[enum_vals_id] = line + "\n"
|
||||
continue
|
||||
to_write[enum_vals_id] += line + "\n"
|
||||
for ev_id in to_write.keys():
|
||||
header_enum_id = f":{ev_id}" if ev_id != "NOTGIVEN" else ""
|
||||
regex = (
|
||||
rf"\s*// generated content <{self.inc.name}{header_enum_id}> begin.*(\n)"
|
||||
rf"(.*\n)*"
|
||||
rf"\s*// generated content <{self.inc.name}{header_enum_id}> end.*(\n)"
|
||||
)
|
||||
if not re.search(regex, header_content):
|
||||
error_exit(f"Could not locate include comments for {self.inc.name}")
|
||||
|
||||
new_content = (
|
||||
f"\n\t// generated content <{self.inc.name}{header_enum_id}> begin\n"
|
||||
+ "\t// clang-format off\n\n"
|
||||
+ to_write[ev_id]
|
||||
+ "\n\t// clang-format on\n"
|
||||
+ f"\t// generated content <{self.inc.name}{header_enum_id}> end\n"
|
||||
)
|
||||
|
||||
header_content = re.sub(regex, new_content, header_content)
|
||||
if self.write_file:
|
||||
with open(self.header, "w") as f:
|
||||
f.write(header_content)
|
||||
else:
|
||||
self.patched_header_content = header_content
|
||||
log.info(f"Patched {self.inc.name} into {self.header.name}")
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def file_in_main_header(header: Path, filename: str) -> bool:
|
||||
with open(header) as f:
|
||||
header_content = f.read()
|
||||
return filename in header_content
|
||||
|
||||
|
||||
class CompatHeaderBuilder:
|
||||
def __init__(self, v6: Path, v5: Path, arch: str):
|
||||
self.v6 = v6
|
||||
self.v5 = v5
|
||||
match arch:
|
||||
case "aarch64":
|
||||
self.v6_lower = "aarch64"
|
||||
self.v6_upper = "AARCH64"
|
||||
self.v6_camel = "AArch64"
|
||||
self.v5_lower = "arm64"
|
||||
self.v5_upper = "ARM64"
|
||||
case "systemz":
|
||||
self.v6_lower = "systemz"
|
||||
self.v6_upper = "SYSTEMZ"
|
||||
self.v6_camel = "SystemZ"
|
||||
self.v5_lower = "sysz"
|
||||
self.v5_upper = "SYSZ"
|
||||
case _:
|
||||
raise ValueError(f"{arch} not handled")
|
||||
|
||||
def replace_typedef_struct(self, v6_lines: list[str]) -> list[str]:
|
||||
output = list()
|
||||
typedef = ""
|
||||
for line in v6_lines:
|
||||
if typedef:
|
||||
if not re.search(r"^}\s[\w_]+;", line):
|
||||
# Skip struct content
|
||||
continue
|
||||
type_name = re.findall(r"[\w_]+", line)[0]
|
||||
output.append(
|
||||
f"typedef {type_name} {re.sub(self.v6_lower,self.v5_lower, type_name)};\n"
|
||||
)
|
||||
typedef = ""
|
||||
continue
|
||||
|
||||
if re.search(rf"^typedef\s+(struct|union)", line):
|
||||
typedef = line
|
||||
continue
|
||||
output.append(line)
|
||||
return output
|
||||
|
||||
def replace_typedef_enum(self, v6_lines: list[str]) -> list[str]:
|
||||
output = list()
|
||||
typedef = ""
|
||||
for line in v6_lines:
|
||||
if typedef:
|
||||
if not re.search(r"^}\s[\w_]+;", line):
|
||||
# Replace name
|
||||
if self.v6_camel not in line and self.v6_upper not in line:
|
||||
output.append(line)
|
||||
continue
|
||||
found = re.findall(
|
||||
rf"({self.v6_camel}|{self.v6_upper})([\w_]+)", line
|
||||
)
|
||||
entry_name: str = "".join(found[0])
|
||||
v5_name = entry_name.replace(self.v6_camel, self.v5_upper).replace(
|
||||
self.v6_upper, self.v5_upper
|
||||
)
|
||||
patched_line = re.sub(
|
||||
rf"({self.v6_camel}|{self.v6_upper}).+",
|
||||
f"{v5_name} = {entry_name},",
|
||||
line,
|
||||
)
|
||||
output.append(patched_line)
|
||||
continue
|
||||
# We still have LLVM and CS naming conventions mixed
|
||||
p = re.sub(self.v6_lower, self.v5_lower, line)
|
||||
p = re.sub(rf"({self.v6_camel}|{self.v6_upper})", self.v5_upper, p)
|
||||
output.append(p)
|
||||
typedef = ""
|
||||
continue
|
||||
|
||||
if re.search(rf"^typedef\s+enum", line):
|
||||
typedef = line
|
||||
output.append("typedef enum {\n")
|
||||
continue
|
||||
output.append(line)
|
||||
return output
|
||||
|
||||
def remove_comments(self, v6_lines: list[str]) -> list[str]:
|
||||
output = list()
|
||||
for line in v6_lines:
|
||||
if re.search(r"^\s*//", line) and "// SPDX" not in line:
|
||||
continue
|
||||
output.append(line)
|
||||
return output
|
||||
|
||||
def replace_v6_prefix(self, v6_lines: list[str]) -> list[str]:
|
||||
output = list()
|
||||
in_typedef = False
|
||||
for line in v6_lines:
|
||||
if "CAPSTONE_SYSTEMZ_COMPAT_HEADER" in line:
|
||||
output.append(line)
|
||||
if in_typedef:
|
||||
if re.search(r"^}\s[\w_]+;", line):
|
||||
in_typedef = False
|
||||
output.append(line)
|
||||
continue
|
||||
|
||||
if re.search(f"^typedef", line):
|
||||
in_typedef = True
|
||||
output.append(line)
|
||||
continue
|
||||
output.append(
|
||||
re.sub(rf"({self.v6_camel}|{self.v6_upper})", self.v5_upper, line)
|
||||
)
|
||||
return output
|
||||
|
||||
def replace_include_guards(self, v6_lines: list[str]) -> list[str]:
|
||||
output = list()
|
||||
skip = False
|
||||
for line in v6_lines:
|
||||
if "CAPSTONE_SYSTEMZ_COMPAT_HEADER" in line:
|
||||
# The compat heade is inlcuded in the v6 header.
|
||||
# Because v5 and v6 header share the same name.
|
||||
skip = True
|
||||
continue
|
||||
elif skip and "#endif" in line:
|
||||
skip = False
|
||||
continue
|
||||
elif skip:
|
||||
continue
|
||||
|
||||
if not re.search(r"^#(ifndef|define)", line):
|
||||
output.append(line)
|
||||
continue
|
||||
output.append(re.sub(self.v6_upper, self.v5_upper, line))
|
||||
return output
|
||||
|
||||
def inject_v6_header(self, v6_lines: list[str]) -> list[str]:
|
||||
output = list()
|
||||
header_inserted = False
|
||||
for line in v6_lines:
|
||||
if re.search(r"^#include", line):
|
||||
if not header_inserted:
|
||||
output.append(f'#include "{self.v6_lower}.h"\n')
|
||||
header_inserted = True
|
||||
output.append(line)
|
||||
return output
|
||||
|
||||
def add_cc_macros(self, v6_lines: list[str]) -> list[str]:
|
||||
v6_lines += AARCH64_CC_MACROS
|
||||
return v6_lines
|
||||
|
||||
def generate_v5_compat_header(self) -> bool:
|
||||
"""
|
||||
Translates the aarch64.h header into the arm64.h header and renames all aarch64 occurrences.
|
||||
It does simple regex matching and replacing.
|
||||
Same for systemz.h and SYSTEMZ -> SYSZ. But the output file is systemz_compatibility.h.
|
||||
"""
|
||||
log.info("Generate compatibility header")
|
||||
with open(self.v6) as f:
|
||||
v6_lines = f.readlines()
|
||||
|
||||
patched = self.replace_typedef_struct(v6_lines)
|
||||
patched = self.replace_typedef_enum(patched)
|
||||
patched = self.remove_comments(patched)
|
||||
patched = self.replace_v6_prefix(patched)
|
||||
patched = self.replace_include_guards(patched)
|
||||
patched = self.inject_v6_header(patched)
|
||||
if self.v6_lower == "aarch64":
|
||||
patched = self.add_cc_macros(patched)
|
||||
|
||||
with open(self.v5, "w+") as f:
|
||||
f.writelines(patched)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
if (not args.patch and not args.compat) or (args.patch and args.compat):
|
||||
print("You need to specify either -c or -p")
|
||||
exit(1)
|
||||
if args.compat and not (args.v6 and args.v5):
|
||||
print("Generating the v5 compatibility header requires --v5 and --v6")
|
||||
exit(1)
|
||||
if args.patch and not (args.inc and args.header):
|
||||
print("Patching headers requires --inc and --header")
|
||||
exit(1)
|
||||
|
||||
if args.patch:
|
||||
patcher = HeaderPatcher(args.header, args.inc)
|
||||
patcher.patch_header()
|
||||
exit(0)
|
||||
|
||||
if "aarch64" in args.v6.name:
|
||||
arch = "aarch64"
|
||||
elif "systemz" in args.v6.name:
|
||||
arch = "systemz"
|
||||
else:
|
||||
raise ValueError(f"Does not know the arch for header file: {args.v6.name}")
|
||||
|
||||
builder = CompatHeaderBuilder(args.v6, args.v5, arch)
|
||||
builder.generate_v5_compat_header()
|
||||
@@ -0,0 +1,170 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import hashlib
|
||||
import logging as log
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import termcolor
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.PathVarHandler import PathVarHandler
|
||||
|
||||
|
||||
def convert_loglevel(level: str) -> int:
|
||||
if level == "debug":
|
||||
return log.DEBUG
|
||||
elif level == "info":
|
||||
return log.INFO
|
||||
elif level == "warning":
|
||||
return log.WARNING
|
||||
elif level == "error":
|
||||
return log.ERROR
|
||||
elif level == "fatal":
|
||||
return log.FATAL
|
||||
elif level == "critical":
|
||||
return log.CRITICAL
|
||||
raise ValueError(f'Unknown loglevel "{level}"')
|
||||
|
||||
|
||||
def find_id_by_type(node: Node, node_types: [str], type_must_match: bool) -> bytes:
|
||||
"""
|
||||
Recursively searches for a node sequence with given node types.
|
||||
|
||||
A valid sequence is a path from node_n to node_{(n + |node_types|-1)} where
|
||||
forall i in {0, ..., |node_types|-1}: type(node_{(n + i)}) = node_types_i.
|
||||
|
||||
If a node sequence is found, this functions returns the text associated with the
|
||||
last node in the sequence.
|
||||
|
||||
:param node: Current node.
|
||||
:param node_types: List of node types.
|
||||
:param type_must_match: If true, it is mandatory for the current node that its type matches node_types[0]
|
||||
:return: The nodes text of the last node in a valid sequence of and empty string of no such sequence exists.
|
||||
"""
|
||||
if len(node_types) == 0:
|
||||
# No ids left to compare to: Nothing found
|
||||
return b""
|
||||
|
||||
# Set true if:
|
||||
# current node type matches.
|
||||
# OR
|
||||
# parent dictates that node type match
|
||||
type_must_match = node.type == node_types[0] or type_must_match
|
||||
if type_must_match and node.type != node_types[0]:
|
||||
# This child has no matching type. Return.
|
||||
return b""
|
||||
|
||||
if len(node_types) == 1 and type_must_match:
|
||||
if node.type == node_types[0]:
|
||||
# Found it
|
||||
return node.text
|
||||
else:
|
||||
# Not found. Return to parent
|
||||
return b""
|
||||
|
||||
# If this nodes type matches the first in the list
|
||||
# we remove this one from the list.
|
||||
# Otherwise, give the whole list to the child (since our type does not matter).
|
||||
children_id_types = node_types[1:] if type_must_match else node_types
|
||||
|
||||
# Check if any child has a matching type.
|
||||
for child in node.named_children:
|
||||
res = find_id_by_type(child, children_id_types, type_must_match)
|
||||
if res:
|
||||
# A path from this node matches the id_types!
|
||||
return res
|
||||
|
||||
# None of our children matched the type list.
|
||||
return b""
|
||||
|
||||
|
||||
def print_prominent_warning(msg: str, wait_for_user: bool = True) -> None:
|
||||
print("\n" + separator_line_1("yellow"))
|
||||
print(termcolor.colored("WARNING", "yellow", attrs=["bold"]) + "\n")
|
||||
print(msg)
|
||||
print(separator_line_1("yellow"))
|
||||
if wait_for_user:
|
||||
input("Press enter to continue...\n")
|
||||
|
||||
|
||||
def term_width() -> int:
|
||||
return shutil.get_terminal_size()[0]
|
||||
|
||||
|
||||
def print_prominent_info(msg: str, wait_for_user: bool = True) -> None:
|
||||
print("\n" + separator_line_1("blue"))
|
||||
print(msg)
|
||||
print(separator_line_1("blue"))
|
||||
if wait_for_user:
|
||||
input("Press enter to continue...\n")
|
||||
|
||||
|
||||
def bold(msg: str, color: str = None) -> str:
|
||||
if color:
|
||||
return termcolor.colored(msg, attrs=["bold"], color=color)
|
||||
return termcolor.colored(msg, attrs=["bold"])
|
||||
|
||||
|
||||
def colored(msg: str, color: str) -> str:
|
||||
return termcolor.colored(msg, color=color)
|
||||
|
||||
|
||||
def separator_line_1(color: str = None) -> str:
|
||||
return f"{bold(f'⎼' * int(term_width() / 2), color)}\n"
|
||||
|
||||
|
||||
def separator_line_2(color: str = None) -> str:
|
||||
return f"{bold(f'═' * int(term_width() / 2), color)}\n"
|
||||
|
||||
|
||||
def get_sha256(data: bytes) -> str:
|
||||
h = hashlib.sha256()
|
||||
h.update(data)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def get_header() -> str:
|
||||
return (
|
||||
"/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n"
|
||||
"/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2022, */\n"
|
||||
"/* Rot127 <unisono@quyllur.org> 2022-2023 */\n"
|
||||
"/* Automatically translated source file from LLVM. */\n\n"
|
||||
"/* LLVM-commit: <commit> */\n"
|
||||
"/* LLVM-tag: <tag> */\n\n"
|
||||
"/* Only small edits allowed. */\n"
|
||||
"/* For multiple similar edits, please create a Patch for the translator. */\n\n"
|
||||
"/* Capstone's C++ file translator: */\n"
|
||||
"/* https://github.com/capstone-engine/capstone/tree/next/suite/auto-sync */\n\n"
|
||||
)
|
||||
|
||||
|
||||
def run_clang_format(out_paths: list[Path]):
|
||||
for out_file in out_paths:
|
||||
log.info(f"Format {out_file}")
|
||||
subprocess.run(
|
||||
[
|
||||
"clang-format",
|
||||
f"-style=file:{get_path('{CS_CLANG_FORMAT_FILE}')}",
|
||||
"-i",
|
||||
out_file,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def get_path(config_path: str) -> Path:
|
||||
return PathVarHandler().complete_path(config_path)
|
||||
|
||||
|
||||
def test_only_overwrite_path_var(var_name: str, new_path: Path):
|
||||
"""Don't use outside of testing."""
|
||||
return PathVarHandler().test_only_overwrite_var(var_name, new_path)
|
||||
|
||||
|
||||
def fail_exit(msg: str) -> None:
|
||||
"""Logs a fatal message and exits with error code 1."""
|
||||
log.fatal(msg)
|
||||
exit(1)
|
||||
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from autosync.Helper import fail_exit, get_path
|
||||
|
||||
|
||||
class IncGenerator:
|
||||
def __init__(self, arch: str, inc_list: list) -> None:
|
||||
self.arch: str = arch
|
||||
self.inc_list = inc_list # Names of inc files to generate.
|
||||
self.arch_dir_name: str = "PowerPC" if self.arch == "PPC" else self.arch
|
||||
self.patches_dir_path: Path = get_path("{INC_PATCH_DIR}")
|
||||
self.llvm_include_dir: Path = get_path("{LLVM_INCLUDE_DIR}")
|
||||
self.output_dir: Path = get_path("{BUILD_DIR}")
|
||||
self.llvm_target_dir: Path = get_path("{LLVM_TARGET_DIR}").joinpath(
|
||||
f"{self.arch_dir_name}"
|
||||
)
|
||||
self.llvm_tblgen: Path = get_path("{LLVM_TBLGEN_BIN}")
|
||||
self.output_dir_c_inc = get_path("{C_INC_OUT_DIR}")
|
||||
self.output_dir_cpp_inc = get_path("{CPP_INC_OUT_DIR}")
|
||||
with open(get_path("{INC_GEN_CONF_FILE}")) as f:
|
||||
self.conf = json.loads(f.read())
|
||||
self.check_paths()
|
||||
|
||||
def check_paths(self) -> None:
|
||||
if not self.llvm_include_dir.exists():
|
||||
fail_exit(f"{self.llvm_include_dir} does not exist.")
|
||||
if not self.llvm_target_dir.exists():
|
||||
fail_exit(f"{self.llvm_target_dir} does not exist.")
|
||||
if not self.llvm_tblgen.exists():
|
||||
fail_exit(f"{self.llvm_tblgen} does not exist. Have you build llvm-tblgen?")
|
||||
if not self.output_dir.exists():
|
||||
fail_exit(f"{self.output_dir} does not exist.")
|
||||
if not self.output_dir_c_inc.exists():
|
||||
log.debug(f"{self.output_dir_c_inc} does not exist. Creating it...")
|
||||
os.makedirs(self.output_dir_c_inc)
|
||||
if not self.output_dir_cpp_inc.exists():
|
||||
log.debug(f"{self.output_dir_cpp_inc} does not exist. Creating it...")
|
||||
os.makedirs(self.output_dir_cpp_inc)
|
||||
|
||||
def generate(self) -> None:
|
||||
self.gen_incs()
|
||||
self.move_mapping_files()
|
||||
|
||||
def move_mapping_files(self) -> None:
|
||||
"""
|
||||
Moves the <ARCH>GenCS files. They are written to CWD (I know, not nice).
|
||||
We move them manually to the build dir, as long as llvm-capstone doesn't
|
||||
allow to specify an output dir.
|
||||
"""
|
||||
for file in Path.cwd().iterdir():
|
||||
if re.search(rf"{self.arch}Gen.*\.inc", file.name):
|
||||
log.debug(f"Move {file} to {self.output_dir_c_inc}")
|
||||
if self.output_dir_c_inc.joinpath(file.name).exists():
|
||||
os.remove(self.output_dir_c_inc.joinpath(file.name))
|
||||
shutil.move(file, self.output_dir_c_inc)
|
||||
|
||||
if self.arch == "ARM":
|
||||
# We have to rename the file SystemOperand -> SystemRegister
|
||||
sys_ops_table_file = self.output_dir_c_inc.joinpath(
|
||||
"ARMGenSystemOperands.inc"
|
||||
)
|
||||
new_sys_ops_file = self.output_dir_c_inc.joinpath(
|
||||
"ARMGenSystemRegister.inc"
|
||||
)
|
||||
if "SystemOperand" not in self.inc_list:
|
||||
return
|
||||
elif not sys_ops_table_file.exists():
|
||||
fail_exit(
|
||||
f"{sys_ops_table_file} does not exist. But it should have been generated."
|
||||
)
|
||||
if new_sys_ops_file.exists():
|
||||
os.remove(new_sys_ops_file)
|
||||
shutil.move(sys_ops_table_file, new_sys_ops_file)
|
||||
|
||||
def gen_incs(self) -> None:
|
||||
for table in self.conf["inc_tables"]:
|
||||
if "All" not in self.inc_list and table["name"] not in self.inc_list:
|
||||
log.debug(f"Skip {table['name']} generation")
|
||||
continue
|
||||
|
||||
if table["only_arch"] and self.arch not in table["only_arch"]:
|
||||
continue
|
||||
|
||||
log.info(f"Generating {table['name']} tables...")
|
||||
for lang in table["lang"]:
|
||||
log.debug(f"Generating {lang} tables...")
|
||||
td_file = self.llvm_target_dir.joinpath(f"{self.arch}.td")
|
||||
out_file = f"{self.arch}Gen{table['inc_name']}.inc"
|
||||
if lang == "CCS":
|
||||
out_path = self.output_dir_c_inc.joinpath(out_file)
|
||||
elif lang == "C++":
|
||||
out_path = self.output_dir_cpp_inc.joinpath(out_file)
|
||||
else:
|
||||
raise NotImplementedError(f"{lang} not supported by llvm-tblgen.")
|
||||
|
||||
args = []
|
||||
args.append(str(self.llvm_tblgen))
|
||||
args.append(f"--printerLang={lang}")
|
||||
args.append(table["tblgen_arg"])
|
||||
args.append("-I")
|
||||
args.append(f"{str(self.llvm_include_dir)}")
|
||||
args.append("-I")
|
||||
args.append(f"{str(self.llvm_target_dir)}")
|
||||
if table["inc_name"]:
|
||||
args.append("-o")
|
||||
args.append(f"{str(out_path)}")
|
||||
args.append(str(td_file))
|
||||
|
||||
log.debug(" ".join(args))
|
||||
try:
|
||||
subprocess.run(
|
||||
args,
|
||||
check=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
log.fatal("Generation failed")
|
||||
raise e
|
||||
|
||||
def has_inc_patches(self) -> bool:
|
||||
patch_dir = self.patches_dir_path.joinpath(self.arch)
|
||||
return patch_dir.exists()
|
||||
|
||||
def apply_patches(self) -> None:
|
||||
"""
|
||||
Applies all patches of inc files.
|
||||
Files must be moved to their arch/<ARCH> directory before.
|
||||
"""
|
||||
patch_dir = self.patches_dir_path.joinpath(self.arch)
|
||||
if not patch_dir.exists():
|
||||
return
|
||||
|
||||
# apply patches in alphabetical order
|
||||
for patch in sorted(patch_dir.iterdir()):
|
||||
try:
|
||||
subprocess.run(
|
||||
["git", "apply", "-v", "--recount", str(patch)],
|
||||
check=True,
|
||||
)
|
||||
log.info(f"Applied inc patch {patch.name}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
log.warning(f".inc patch {patch.name} did not apply correctly!")
|
||||
log.warning(f"Error:\n{e.output}")
|
||||
@@ -0,0 +1,641 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright © 2024 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import argparse
|
||||
import logging as log
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import subprocess as sp
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from autosync.Targets import TARGETS_LLVM_NAMING, TARGET_TO_DIR_NAME
|
||||
from autosync.Helper import convert_loglevel, get_path
|
||||
|
||||
|
||||
class LLVM_MC_Command:
|
||||
def __init__(self, cmd_line: str, mattr: str):
|
||||
self.cmd: str = ""
|
||||
self.opts: str = ""
|
||||
self.file: Path | None = None
|
||||
self.additional_mattr: str = mattr
|
||||
|
||||
self.cmd, self.opts, self.file = self.parse_llvm_mc_line(cmd_line)
|
||||
if not (self.cmd and self.opts and self.file):
|
||||
log.warning(f"Could not parse llvm-mc command: {cmd_line}")
|
||||
elif not "--show-encoding" in self.cmd:
|
||||
self.cmd = re.sub("llvm-mc", "llvm-mc --show-encoding", self.cmd)
|
||||
elif not "--disassemble" in self.cmd:
|
||||
self.cmd = re.sub("llvm-mc", "llvm-mc --disassemble", self.cmd)
|
||||
|
||||
def parse_llvm_mc_line(self, line: str) -> tuple[str, str, Path]:
|
||||
test_file_base_dir = str(get_path("{LLVM_LIT_TEST_DIR}").absolute())
|
||||
file = re.findall(rf"{test_file_base_dir}\S+", line)
|
||||
if not file:
|
||||
log.warning(f"llvm-mc command doesn't contain a file: {line}")
|
||||
return None, None, None
|
||||
test_file = file[0]
|
||||
cmd = re.sub(rf"{test_file}", "", line).strip()
|
||||
cmd = re.sub(r"\s+", " ", cmd)
|
||||
arch = re.finditer(r"(triple|arch)[=\s](\S+)", cmd)
|
||||
mattr = re.finditer(r"(mattr|mcpu)[=\s](\S+)", cmd)
|
||||
opts = ",".join([m.group(2) for m in arch]) if arch else ""
|
||||
if mattr:
|
||||
opts += "" if not opts else ","
|
||||
processed_attr = list()
|
||||
for m in mattr:
|
||||
attribute = m.group(2).strip("+")
|
||||
processed_attr.append(attribute)
|
||||
opts += ",".join(processed_attr)
|
||||
return cmd, opts, Path(test_file)
|
||||
|
||||
def exec(self) -> sp.CompletedProcess:
|
||||
with open(self.file, "b+r") as f:
|
||||
content = f.read()
|
||||
if self.additional_mattr:
|
||||
# If mattr exists, patch it into the cmd
|
||||
if "mattr" in self.cmd:
|
||||
self.cmd = re.sub(
|
||||
r"mattr[=\s]+", f"mattr={self.additional_mattr} -mattr=", self.cmd
|
||||
)
|
||||
else:
|
||||
self.cmd = re.sub(
|
||||
r"llvm-mc", f"llvm-mc -mattr={self.additional_mattr}", self.cmd
|
||||
)
|
||||
|
||||
log.debug(f"Run: {self.cmd}")
|
||||
result = sp.run(self.cmd.split(" "), input=content, capture_output=True)
|
||||
return result
|
||||
|
||||
def get_opts_list(self) -> list[str]:
|
||||
opts = self.opts.strip().strip(",")
|
||||
opts = re.sub(r"[, ]+", ",", opts)
|
||||
return opts.split(",")
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.cmd} < {str(self.file.absolute())}"
|
||||
|
||||
|
||||
class MCTest:
|
||||
"""
|
||||
A single test. It can contain multiple decoded instruction for a given byte sequence.
|
||||
In general a MCTest always tests a sequence of instructions in a single .text segment.
|
||||
"""
|
||||
|
||||
def __init__(self, arch: str, opts: list[str], encoding: str, asm_text: str):
|
||||
self.arch = arch
|
||||
self.opts = opts
|
||||
self.encoding: list[str] = [encoding]
|
||||
self.asm_text: list[str] = [asm_text]
|
||||
|
||||
def extend(self, encoding: str, asm_text: str):
|
||||
self.encoding.append(encoding)
|
||||
self.asm_text.append(asm_text)
|
||||
|
||||
def get_legacy_mc_test_triple(self):
|
||||
"""
|
||||
Returns the legacy triple for the old MC test files:
|
||||
<ARCH>, <MODE>, None
|
||||
Should only be used to generate fuzzing tests.
|
||||
"""
|
||||
triple = "# "
|
||||
if self.arch.startswith("CS_ARCH"):
|
||||
triple += self.arch
|
||||
else:
|
||||
triple += f"CS_ARCH_{self.arch.upper()}"
|
||||
opts = "|".join([f'"{o}"' for o in self.opts if o.startswith("CS_MODE_")])
|
||||
if not opts:
|
||||
opts = "0"
|
||||
triple += f", {opts}, None"
|
||||
return triple
|
||||
|
||||
def fuzz_test_str(self):
|
||||
old_mc_tcase = ""
|
||||
for enc, asm_text in zip(self.encoding, self.asm_text):
|
||||
if old_mc_tcase:
|
||||
old_mc_tcase += "\n"
|
||||
encoding = re.sub(r"[\[\]]", "", enc)
|
||||
encoding = encoding.strip()
|
||||
encoding = re.sub(r"[\s,]+", ",", encoding)
|
||||
old_mc_tcase += f"{encoding} == {asm_text}"
|
||||
return old_mc_tcase
|
||||
|
||||
def __str__(self):
|
||||
encoding = ",".join(self.encoding)
|
||||
encoding = re.sub(r"[\[\]]", "", encoding)
|
||||
encoding = encoding.strip()
|
||||
encoding = re.sub(r"[\s,]+", ", ", encoding)
|
||||
yaml_tc = (
|
||||
" -\n"
|
||||
" input:\n"
|
||||
" bytes: [ <ENCODING> ]\n"
|
||||
' arch: "<ARCH>"\n'
|
||||
" options: [ <OPTIONS> ]\n"
|
||||
" expected:\n"
|
||||
" insns:\n"
|
||||
)
|
||||
template = " -\n asm_text: <ASM_TEXT>\n"
|
||||
insn_cases = ""
|
||||
for text in self.asm_text:
|
||||
insn_cases += template.replace("<ASM_TEXT>", f'"{text}"')
|
||||
|
||||
yaml_tc = yaml_tc.replace("<ENCODING>", encoding)
|
||||
yaml_tc = yaml_tc.replace("<ARCH>", f"CS_ARCH_{self.arch.upper()}")
|
||||
yaml_tc = yaml_tc.replace("<OPTIONS>", ", ".join([f'"{o}"' for o in self.opts]))
|
||||
yaml_tc += insn_cases
|
||||
return yaml_tc
|
||||
|
||||
|
||||
class TestFile:
|
||||
def __init__(
|
||||
self,
|
||||
arch: str,
|
||||
file_path: Path,
|
||||
opts: list[str] | None,
|
||||
mc_cmd: LLVM_MC_Command,
|
||||
unified_test_cases: bool,
|
||||
):
|
||||
self.arch: str = arch
|
||||
self.file_path: Path = file_path
|
||||
self.opts: list[str] = list() if not opts else opts
|
||||
self.mc_cmd: LLVM_MC_Command = mc_cmd
|
||||
# Indexed by .text section count
|
||||
self.tests: dict[int : list[MCTest]] = dict()
|
||||
|
||||
self.init_tests(unified_test_cases)
|
||||
|
||||
def init_tests(self, unified_test_cases: bool):
|
||||
mc_output = self.mc_cmd.exec()
|
||||
if mc_output.stderr and not mc_output.stdout:
|
||||
# We can still continue. We just ignore the failed cases.
|
||||
log.debug(f"llvm-mc cmd stderr: {mc_output.stderr}")
|
||||
log.debug(f"llvm-mc result: {mc_output}")
|
||||
text_section = 0 # Counts the .text sections
|
||||
asm_pat = f"(?P<asm_text>.+)"
|
||||
enc_pat = r"(\[?(?P<full_enc_string>(?P<enc_bytes>((0x[a-fA-F0-9]{1,2}[, ]{0,2}))+)[^, ]?)\]?)"
|
||||
|
||||
dups = []
|
||||
for line in mc_output.stdout.splitlines():
|
||||
line = line.decode("utf8")
|
||||
if ".text" in line:
|
||||
text_section += 1
|
||||
continue
|
||||
match = re.search(
|
||||
rf"^\s*{asm_pat}\s*(#|//|@|!|;)\s*encoding:\s*{enc_pat}", line
|
||||
)
|
||||
if not match:
|
||||
continue
|
||||
full_enc_string = match.group("full_enc_string")
|
||||
if not re.search(r"0x[a-fA-F0-9]{1,2}$", full_enc_string[:-1]):
|
||||
log.debug(f"Ignore because symbol injection is needed: {line}")
|
||||
# The encoding string contains symbol information of the form:
|
||||
# [0xc0,0xe0,A,A,A... or similar. We ignore these for now.
|
||||
continue
|
||||
enc_bytes = match.group("enc_bytes").strip()
|
||||
asm_text = match.group("asm_text").strip()
|
||||
asm_text = re.sub(r"\t+", " ", asm_text)
|
||||
asm_text = asm_text.strip()
|
||||
if not self.valid_byte_seq(enc_bytes):
|
||||
continue
|
||||
|
||||
if (enc_bytes + asm_text) in dups:
|
||||
continue
|
||||
|
||||
dups.append(enc_bytes + asm_text)
|
||||
if text_section in self.tests:
|
||||
if unified_test_cases:
|
||||
self.tests[text_section][0].extend(enc_bytes, asm_text)
|
||||
else:
|
||||
self.tests[text_section].append(
|
||||
MCTest(self.arch, self.opts, enc_bytes, asm_text)
|
||||
)
|
||||
else:
|
||||
self.tests[text_section] = [
|
||||
MCTest(self.arch, self.opts, enc_bytes, asm_text)
|
||||
]
|
||||
|
||||
def has_tests(self) -> bool:
|
||||
return len(self.tests) != 0
|
||||
|
||||
def get_cs_testfile_content(self, only_tests: bool) -> str:
|
||||
content = "\n" if only_tests else "test_cases:\n"
|
||||
for tl in self.tests.values():
|
||||
content += "\n".join([str(t) for t in tl])
|
||||
return content
|
||||
|
||||
def get_fuzz_test_file_content(self, only_tests: bool) -> str:
|
||||
content = ""
|
||||
for tl in self.tests.values():
|
||||
if not content:
|
||||
content = (
|
||||
"\n" if only_tests else tl[0].get_legacy_mc_test_triple() + "\n"
|
||||
)
|
||||
content += "\n".join([t.fuzz_test_str() for t in tl])
|
||||
return content
|
||||
|
||||
def num_test_cases(self) -> int:
|
||||
return len(self.tests)
|
||||
|
||||
def valid_byte_seq(self, enc_bytes):
|
||||
match self.arch:
|
||||
case "AArch64":
|
||||
# It always needs 4 bytes.
|
||||
# Otherwise it is likely a reloc or symbol test
|
||||
return enc_bytes.count("0x") == 4
|
||||
case _:
|
||||
return True
|
||||
|
||||
def get_multi_mode_filename(self) -> Path:
|
||||
filename = self.file_path.stem
|
||||
parent = self.file_path.parent
|
||||
prefix_less_opts = [re.sub(r"CS_(OPT|MODE)_", "", o).lower() for o in self.opts]
|
||||
detailed_name = f"{filename}_{'_'.join(prefix_less_opts)}.txt"
|
||||
detailed_name = re.sub(r"[+-]", "_", detailed_name)
|
||||
out_path = parent.joinpath(detailed_name)
|
||||
return Path(out_path)
|
||||
|
||||
def get_simple_filename(self) -> Path:
|
||||
return self.file_path
|
||||
|
||||
def __lt__(self, other) -> bool:
|
||||
return str(self.file_path) < str(other.file_path)
|
||||
|
||||
|
||||
def exists_and_is_dir(x):
|
||||
return x.exists() and x.is_dir()
|
||||
|
||||
|
||||
class MCUpdater:
|
||||
"""
|
||||
The MCUpdater parses all test files of the LLVM MC regression tests.
|
||||
Each of those LLVM files can contain several llvm-mc commands to run on the same file.
|
||||
Mostly this is done to test the same file with different CPU features enabled.
|
||||
So it can test different flavors of assembly etc.
|
||||
|
||||
In Capstone all modules enable always all CPU features (even if this is not
|
||||
possible in reality).
|
||||
Due to this we always parse all llvm-mc commands run on a test file, generate a TestFile
|
||||
object for each of it, but only write the last one of them to disk.
|
||||
Once https://github.com/capstone-engine/capstone/issues/1992 is resolved, we can
|
||||
write all variants of a test file to disk.
|
||||
|
||||
This is already implemented and tested with multi_mode = True.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
arch: str,
|
||||
mc_dir: Path,
|
||||
excluded: list[str] | None,
|
||||
included: list[str] | None,
|
||||
unified_test_cases: bool,
|
||||
multi_mode: bool = False,
|
||||
):
|
||||
self.symbolic_links = list()
|
||||
self.arch = arch
|
||||
self.arch_dir_name = TARGET_TO_DIR_NAME[self.arch]
|
||||
self.test_dir_link_prefix = f"test_dir_{arch}_"
|
||||
self.mc_dir = mc_dir
|
||||
self.excluded = excluded if excluded else list()
|
||||
self.included = included if included else list()
|
||||
self.test_files: list[TestFile] = list()
|
||||
self.unified_test_cases = unified_test_cases
|
||||
with open(get_path("{MCUPDATER_CONFIG_FILE}")) as f:
|
||||
self.conf = json.loads(f.read())
|
||||
# Additional mattr passed to llvm-mc
|
||||
self.mattr: str = (
|
||||
",".join(self.conf["additional_mattr"][self.arch])
|
||||
if self.arch in self.conf["additional_mattr"]
|
||||
else ""
|
||||
)
|
||||
# A list of options which are always added.
|
||||
self.mandatory_options: list[str] = (
|
||||
self.conf["mandatory_options"][self.arch]
|
||||
if self.arch in self.conf["mandatory_options"]
|
||||
else list()
|
||||
)
|
||||
self.default_endianess: str = (
|
||||
self.conf["default_endianess"][self.arch]
|
||||
if self.arch in self.conf["default_endianess"]
|
||||
else ""
|
||||
)
|
||||
self.remove_options: str = (
|
||||
self.conf["remove_options"][self.arch]
|
||||
if self.arch in self.conf["remove_options"]
|
||||
else list()
|
||||
)
|
||||
self.remove_options = [x.lower() for x in self.remove_options]
|
||||
self.replace_option_map: dict = (
|
||||
self.conf["replace_option_map"][self.arch]
|
||||
if self.arch in self.conf["replace_option_map"]
|
||||
else {}
|
||||
)
|
||||
self.replace_option_map = {
|
||||
k.lower(): v
|
||||
for k, v in self.replace_option_map.items()
|
||||
if k.lower not in self.remove_options
|
||||
}
|
||||
self.multi_mode = multi_mode
|
||||
|
||||
def check_prerequisites(self, paths):
|
||||
if all(not exists_and_is_dir(path) for path in paths):
|
||||
raise ValueError(
|
||||
f"'{paths}' does not exits or is not a directory. Cannot generate tests from there."
|
||||
)
|
||||
llvm_lit_cfg = get_path("{LLVM_LIT_TEST_DIR}")
|
||||
if not llvm_lit_cfg.exists():
|
||||
raise ValueError(
|
||||
f"Could not find '{llvm_lit_cfg}'. Check {{LLVM_LIT_TEST_DIR}} in path_vars.json."
|
||||
)
|
||||
|
||||
def write_to_build_dir(self, fuzzer_tests: bool = False):
|
||||
no_tests_file = 0
|
||||
file_cnt = 0
|
||||
test_cnt = 0
|
||||
overwritten = 0
|
||||
files_written = set()
|
||||
for test in sorted(self.test_files):
|
||||
if not test.has_tests():
|
||||
no_tests_file += 1
|
||||
continue
|
||||
file_cnt += 1
|
||||
test_cnt += test.num_test_cases()
|
||||
|
||||
if self.multi_mode:
|
||||
rel_path = str(
|
||||
test.get_multi_mode_filename().relative_to(
|
||||
get_path("{LLVM_LIT_TEST_DIR}")
|
||||
)
|
||||
)
|
||||
else:
|
||||
rel_path = str(
|
||||
test.get_simple_filename().relative_to(
|
||||
get_path("{LLVM_LIT_TEST_DIR}")
|
||||
)
|
||||
)
|
||||
|
||||
filename = re.sub(rf"{self.test_dir_link_prefix}\d+", ".", rel_path)
|
||||
if fuzzer_tests:
|
||||
filename = get_path("{MCUPDATER_OUT_FUZZ_DIR}").joinpath(
|
||||
f"{filename}.cs"
|
||||
)
|
||||
else:
|
||||
filename = get_path("{MCUPDATER_OUT_DIR}").joinpath(f"{filename}.yaml")
|
||||
|
||||
if filename in files_written:
|
||||
write_mode = "a"
|
||||
else:
|
||||
write_mode = "w+"
|
||||
filename.parent.mkdir(parents=True, exist_ok=True)
|
||||
if self.multi_mode and filename.exists():
|
||||
log.warning(
|
||||
f"The following file exists already: {filename}. This indicates a blind spot in testing."
|
||||
)
|
||||
overwritten += 1
|
||||
elif not self.multi_mode and filename.exists():
|
||||
log.debug(f"Overwrite: {filename}")
|
||||
overwritten += 1
|
||||
with open(filename, write_mode) as f:
|
||||
if fuzzer_tests:
|
||||
content = test.get_fuzz_test_file_content(
|
||||
only_tests=(write_mode == "a")
|
||||
)
|
||||
else:
|
||||
content = test.get_cs_testfile_content(
|
||||
only_tests=(write_mode == "a")
|
||||
)
|
||||
f.write(content)
|
||||
log.debug(f"Write {filename}")
|
||||
files_written.add(filename)
|
||||
print()
|
||||
log.info(
|
||||
f"Got {len(self.test_files)} {'fuzzing ' if fuzzer_tests else ''}test files.\n"
|
||||
f"\t\tProcessed {file_cnt} files with {test_cnt} test cases.\n"
|
||||
f"\t\tIgnored {no_tests_file} without tests.\n"
|
||||
f"\t\tGenerated {len(files_written)} files"
|
||||
)
|
||||
if overwritten > 0:
|
||||
log.warning(
|
||||
f"Overwrote {overwritten} test files with the same name.\n"
|
||||
f"These files contain instructions of several different cpu features.\n"
|
||||
f"You have to use multi-mode to write them into distinct files.\n"
|
||||
f"The current setting will only keep the last one written.\n"
|
||||
f"See also: https://github.com/capstone-engine/capstone/issues/1992\n"
|
||||
"If you already used multi-mode (default = yes), there might be a blind spot in testing."
|
||||
)
|
||||
|
||||
def build_test_options(self, options):
|
||||
new_options = [] + self.mandatory_options
|
||||
for opt in options:
|
||||
opt = opt.lower()
|
||||
if opt in self.remove_options:
|
||||
continue
|
||||
elif opt in self.replace_option_map:
|
||||
new_options.extend(self.replace_option_map[opt])
|
||||
else:
|
||||
new_options.append(opt)
|
||||
if (
|
||||
not any(
|
||||
[
|
||||
True
|
||||
for x in new_options
|
||||
if x in ["CS_MODE_BIG_ENDIAN", "CS_MODE_LITTLE_ENDIAN"]
|
||||
]
|
||||
)
|
||||
and self.default_endianess
|
||||
):
|
||||
new_options.append(self.default_endianess)
|
||||
return new_options
|
||||
|
||||
def build_test_files(self, mc_cmds: list[LLVM_MC_Command]) -> list[TestFile]:
|
||||
log.info("Build TestFile objects")
|
||||
test_files = list()
|
||||
n_all = len(mc_cmds)
|
||||
for i, mcc in enumerate(mc_cmds):
|
||||
print(f"{i + 1}/{n_all} {mcc.file.name}", flush=True, end="\r")
|
||||
opts = self.build_test_options(mcc.get_opts_list())
|
||||
test_files.append(
|
||||
TestFile(
|
||||
self.arch,
|
||||
mcc.file,
|
||||
opts,
|
||||
mcc,
|
||||
self.unified_test_cases,
|
||||
)
|
||||
)
|
||||
return test_files
|
||||
|
||||
def run_llvm_lit(self, paths: list[Path]) -> list[LLVM_MC_Command]:
|
||||
"""
|
||||
Calls llvm-lit with the given paths to the tests.
|
||||
It parses the llvm-lit commands to LLVM_MC_Commands.
|
||||
"""
|
||||
lit_cfg_dir = get_path("{LLVM_LIT_TEST_DIR}")
|
||||
llvm_lit_cfg = str(lit_cfg_dir.absolute())
|
||||
args = ["lit", "-v", "-a", llvm_lit_cfg]
|
||||
for i, p in enumerate(paths):
|
||||
slink = lit_cfg_dir.joinpath(f"{self.test_dir_link_prefix}{i}")
|
||||
self.symbolic_links.append(slink)
|
||||
log.debug(f"Create link: {slink} -> {p}")
|
||||
try:
|
||||
slink.symlink_to(p, target_is_directory=True)
|
||||
except FileExistsError as e:
|
||||
print("Failed: Link existed. Please delete it")
|
||||
raise e
|
||||
|
||||
log.info(f"Run lit: {' '.join(args)}")
|
||||
cmds = sp.run(args, capture_output=True)
|
||||
if cmds.stderr:
|
||||
raise ValueError(f"llvm-lit failed with {cmds.stderr}")
|
||||
return self.extract_llvm_mc_cmds(cmds.stdout.decode("utf8"))
|
||||
|
||||
def extract_llvm_mc_cmds(self, cmds: str) -> list[LLVM_MC_Command]:
|
||||
log.debug("Parsing llvm-mc commands")
|
||||
# Get only the RUN lines which have a show-encoding set.
|
||||
cmd_lines = cmds.splitlines()
|
||||
log.debug(f"NO FILTER: {cmd_lines}")
|
||||
matches = list(
|
||||
filter(
|
||||
lambda l: (
|
||||
l
|
||||
if re.search(r"^RUN.+(show-encoding|disassemble)[^|]+", l)
|
||||
else None
|
||||
),
|
||||
cmd_lines,
|
||||
)
|
||||
)
|
||||
log.debug(f"FILTER RUN: {' '.join(matches)}")
|
||||
# Don't add tests which are allowed to fail
|
||||
matches = list(
|
||||
filter(lambda m: None if re.search(r"not\s+llvm-mc", m) else m, matches)
|
||||
)
|
||||
log.debug(f"FILTER not llvm-mc: {' '.join(matches)}")
|
||||
# Skip object file tests
|
||||
matches = list(
|
||||
filter(lambda m: None if re.search(r"filetype=obj", m) else m, matches)
|
||||
)
|
||||
log.debug(f"FILTER filetype=obj-mc: {' '.join(matches)}")
|
||||
# Skip any relocation related tests.
|
||||
matches = filter(lambda m: None if re.search(r"reloc", m) else m, matches)
|
||||
# Remove 'RUN: at ...' prefix
|
||||
matches = map(lambda m: re.sub(r"^RUN: at line \d+: ", "", m), matches)
|
||||
# Remove redirection
|
||||
matches = map(lambda m: re.sub(r"\d>&\d", "", m), matches)
|
||||
# Remove unused arguments
|
||||
matches = map(lambda m: re.sub(r"-o\s?-", "", m), matches)
|
||||
# Remove redirection of stderr to a file
|
||||
matches = map(lambda m: re.sub(r"2>\s?\S+", "", m), matches)
|
||||
# Remove piping to FileCheck
|
||||
matches = map(lambda m: re.sub(r"\|\s*FileCheck\s+.+", "", m), matches)
|
||||
# Remove input stream
|
||||
matches = map(lambda m: re.sub(r"\s+<", "", m), matches)
|
||||
|
||||
all_cmds = list()
|
||||
for match in matches:
|
||||
if self.included and not any(
|
||||
re.search(x, match) is not None for x in self.included
|
||||
):
|
||||
continue
|
||||
if any(re.search(x, match) is not None for x in self.excluded):
|
||||
continue
|
||||
llvm_mc_cmd = LLVM_MC_Command(match, self.mattr)
|
||||
if not llvm_mc_cmd.cmd:
|
||||
# Invalid
|
||||
continue
|
||||
all_cmds.append(llvm_mc_cmd)
|
||||
log.debug(f"Added: {llvm_mc_cmd}")
|
||||
log.debug(f"Extracted {len(all_cmds)} llvm-mc commands")
|
||||
return all_cmds
|
||||
|
||||
def gen_all(self):
|
||||
log.info("Check prerequisites")
|
||||
test_paths = list()
|
||||
if self.arch in self.conf["use_assembly_tests"]:
|
||||
log.info(f"Add assembly tests for {self.arch}")
|
||||
test_paths.append(self.mc_dir.joinpath(self.arch))
|
||||
if self.arch not in self.conf["exclude_disassembly_tests"]:
|
||||
log.info(f"Add disassembly tests for {self.arch}")
|
||||
disas_tests = self.mc_dir.joinpath(f"Disassembler/{self.arch_dir_name}")
|
||||
test_paths.append(disas_tests)
|
||||
self.check_prerequisites(test_paths)
|
||||
log.info("Generate MC regression tests")
|
||||
llvm_mc_cmds = self.run_llvm_lit(
|
||||
[path for path in test_paths if exists_and_is_dir(path)]
|
||||
)
|
||||
log.info(f"Got {len(llvm_mc_cmds)} llvm-mc commands to run")
|
||||
self.test_files = self.build_test_files(llvm_mc_cmds)
|
||||
for slink in self.symbolic_links:
|
||||
log.debug(f"Unlink {slink}")
|
||||
slink.unlink()
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="Test file updater",
|
||||
description="Synchronizes test files with LLVM",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
dest="mc_dir",
|
||||
help=f"Path to the LLVM MC test files. Default: {get_path('{LLVM_MC_TEST_DIR}')}",
|
||||
default=get_path("{LLVM_MC_TEST_DIR}"),
|
||||
type=Path,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
dest="arch",
|
||||
help="Name of architecture to update.",
|
||||
choices=TARGETS_LLVM_NAMING,
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
dest="excluded_files",
|
||||
metavar="filename",
|
||||
nargs="+",
|
||||
help="File names to exclude from update (can be a regex pattern).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
dest="included_files",
|
||||
metavar="filename",
|
||||
nargs="+",
|
||||
help="Specific list of file names to update (can be a regex pattern).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-u",
|
||||
dest="unified_tests",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="If set, all instructions of a text segment will decoded and tested at once. Should be set, if instructions depend on each other.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
dest="verbosity",
|
||||
help="Verbosity of the log messages.",
|
||||
choices=["debug", "info", "warning", "fatal"],
|
||||
default="info",
|
||||
)
|
||||
arguments = parser.parse_args()
|
||||
return arguments
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
log.basicConfig(
|
||||
level=convert_loglevel(args.verbosity),
|
||||
stream=sys.stdout,
|
||||
format="%(levelname)-5s - %(message)s",
|
||||
force=True,
|
||||
)
|
||||
|
||||
MCUpdater(
|
||||
args.arch,
|
||||
args.mc_dir,
|
||||
args.excluded_files,
|
||||
args.included_files,
|
||||
args.unified_tests,
|
||||
True,
|
||||
).gen_all()
|
||||
@@ -0,0 +1,113 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import json
|
||||
import logging as log
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
|
||||
|
||||
class PathVarHandler(metaclass=Singleton):
|
||||
def __init__(self) -> None:
|
||||
try:
|
||||
res = subprocess.run(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
log.fatal("Could not get repository top level directory.")
|
||||
exit(1)
|
||||
repo_root = res.stdout.decode("utf8").strip("\n")
|
||||
# The main directories
|
||||
self.paths: dict[str:Path] = dict()
|
||||
self.paths["{CS_ROOT}"] = Path(repo_root)
|
||||
self.paths["{AUTO_SYNC_ROOT}"] = Path(repo_root).joinpath("suite/auto-sync/")
|
||||
self.paths["{AUTO_SYNC_SRC}"] = self.paths["{AUTO_SYNC_ROOT}"].joinpath(
|
||||
"src/autosync/"
|
||||
)
|
||||
path_config_file = self.paths["{AUTO_SYNC_SRC}"].joinpath("path_vars.json")
|
||||
|
||||
# Load variables
|
||||
with open(path_config_file) as f:
|
||||
vars = json.load(f)
|
||||
|
||||
paths = vars["paths"]
|
||||
self.create_during_runtime = vars["create_during_runtime"]
|
||||
|
||||
missing = list()
|
||||
for p_name, path in paths.items():
|
||||
resolved = path
|
||||
for var_id in re.findall(r"\{.+}", resolved):
|
||||
if var_id not in self.paths:
|
||||
log.fatal(
|
||||
f"{var_id} hasn't been added to the PathVarsHandler, yet. The var must be defined in a previous entry."
|
||||
)
|
||||
exit(1)
|
||||
resolved: str = re.sub(var_id, str(self.paths[var_id]), resolved)
|
||||
log.debug(f"Set {p_name} = {resolved}")
|
||||
if not Path(resolved).exists() and (
|
||||
p_name not in self.create_during_runtime
|
||||
and p_name not in vars["ignore_missing"]
|
||||
):
|
||||
missing.append(resolved)
|
||||
elif var_id in self.create_during_runtime:
|
||||
self.create_path(var_id, resolved)
|
||||
self.paths[p_name] = Path(resolved)
|
||||
if len(missing) > 0:
|
||||
log.fatal(f"Some paths from config file are missing!")
|
||||
for m in missing:
|
||||
log.fatal(f"\t{m}")
|
||||
exit(1)
|
||||
|
||||
def test_only_overwrite_var(self, var_name: str, new_path: Path):
|
||||
if var_name not in self.paths:
|
||||
raise ValueError(f"PathVarHandler doesn't have a path for '{var_name}'")
|
||||
if not new_path.exists():
|
||||
raise ValueError(f"New path doesn't exists: '{new_path}")
|
||||
self.paths[var_name] = new_path
|
||||
|
||||
def get_path(self, name: str) -> Path:
|
||||
if name not in self.paths:
|
||||
raise ValueError(f"Path variable {name} has no path saved.")
|
||||
if name in self.create_during_runtime:
|
||||
self.create_path(name, self.paths[name])
|
||||
return self.paths[name]
|
||||
|
||||
def complete_path(self, path_str: str) -> Path:
|
||||
resolved = path_str
|
||||
for p_name in re.findall(r"\{.+}", path_str):
|
||||
resolved = re.sub(p_name, str(self.get_path(p_name)), resolved)
|
||||
return Path(resolved)
|
||||
|
||||
@staticmethod
|
||||
def create_path(var_id: str, path: str):
|
||||
pp = Path(path)
|
||||
if pp.exists():
|
||||
return
|
||||
|
||||
log.debug(f"Create path {var_id} @ {path}")
|
||||
postfix = var_id.strip("}").split("_")[-1]
|
||||
if postfix == "FILE":
|
||||
if not pp.parent.exists():
|
||||
pp.parent.mkdir(parents=True)
|
||||
pp.touch()
|
||||
elif postfix == "DIR":
|
||||
pp.mkdir(parents=True)
|
||||
else:
|
||||
from autosync.Helper import fail_exit
|
||||
|
||||
fail_exit(
|
||||
f"The var_id: {var_id} must end in _FILE or _DIR. It ends in '{postfix}'"
|
||||
)
|
||||
@@ -0,0 +1,64 @@
|
||||
# Copyright © 2024 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
# Names of the target architectures as they are listed under llvm/lib/Target/
|
||||
TARGETS_LLVM_NAMING = [
|
||||
"ARM",
|
||||
"PowerPC",
|
||||
"Alpha",
|
||||
"AArch64",
|
||||
"LoongArch",
|
||||
"SystemZ",
|
||||
"Mips",
|
||||
"Xtensa",
|
||||
"TriCore",
|
||||
"ARC",
|
||||
"Sparc",
|
||||
]
|
||||
|
||||
# Names of the target architecture as they are used in code and pretty much everywhere else.
|
||||
ARCH_LLVM_NAMING = [
|
||||
"ARM",
|
||||
"PPC",
|
||||
"Alpha",
|
||||
"AArch64",
|
||||
"LoongArch",
|
||||
"SystemZ",
|
||||
"Mips",
|
||||
"Xtensa",
|
||||
"TriCore",
|
||||
"ARC",
|
||||
"Sparc",
|
||||
]
|
||||
|
||||
# Maps the target full name to the name used in code (and pretty much everywhere else).
|
||||
TARGET_TO_IN_CODE_NAME = {
|
||||
"ARM": "ARM",
|
||||
"PowerPC": "PPC",
|
||||
"Alpha": "Alpha",
|
||||
"AArch64": "AArch64",
|
||||
"LoongArch": "LoongArch",
|
||||
"SystemZ": "SystemZ",
|
||||
"Mips": "Mips",
|
||||
"Xtensa": "Xtensa",
|
||||
"TriCore": "TriCore",
|
||||
"ARC": "ARC",
|
||||
"Sparc": "Sparc",
|
||||
"ARCH": "ARCH", # For testing
|
||||
}
|
||||
|
||||
# Maps the name from ARCH_LLVM_NAMING to the directory name in LLVM
|
||||
TARGET_TO_DIR_NAME = {
|
||||
"ARM": "ARM",
|
||||
"PPC": "PowerPC",
|
||||
"Alpha": "Alpha",
|
||||
"AArch64": "AArch64",
|
||||
"LoongArch": "LoongArch",
|
||||
"SystemZ": "SystemZ",
|
||||
"Mips": "Mips",
|
||||
"Xtensa": "Xtensa",
|
||||
"TriCore": "TriCore",
|
||||
"ARC": "ARC",
|
||||
"Sparc": "Sparc",
|
||||
"ARCH": "ARCH", # For testing
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import json
|
||||
import logging as log
|
||||
from pathlib import Path
|
||||
|
||||
import tree_sitter_cpp as ts_cpp
|
||||
from tree_sitter import Language, Parser
|
||||
|
||||
from autosync.Helper import fail_exit
|
||||
|
||||
|
||||
class Configurator:
|
||||
"""
|
||||
Holds common setup procedures for the configuration.
|
||||
It reads the configuration file, compiles languages and initializes the Parser.
|
||||
"""
|
||||
|
||||
arch: str
|
||||
config_path: Path
|
||||
config: dict = None
|
||||
ts_cpp_lang: Language = None
|
||||
parser: Parser = None
|
||||
|
||||
def __init__(self, arch: str, config_path: Path) -> None:
|
||||
self.arch = arch
|
||||
self.config_path = config_path
|
||||
self.load_config()
|
||||
self.ts_set_cpp_language()
|
||||
self.init_parser()
|
||||
|
||||
def get_arch(self) -> str:
|
||||
return self.arch
|
||||
|
||||
def get_cpp_lang(self) -> Language:
|
||||
if self.ts_cpp_lang:
|
||||
return self.ts_cpp_lang
|
||||
self.ts_set_cpp_language()
|
||||
return self.ts_cpp_lang
|
||||
|
||||
def get_parser(self) -> Parser:
|
||||
if self.parser:
|
||||
return self.parser
|
||||
self.init_parser()
|
||||
return self.parser
|
||||
|
||||
def get_arch_config(self) -> dict:
|
||||
if self.config:
|
||||
return self.config[self.arch]
|
||||
self.load_config()
|
||||
return self.config[self.arch]
|
||||
|
||||
def get_general_config(self) -> dict:
|
||||
if self.config:
|
||||
return self.config["General"]
|
||||
self.load_config()
|
||||
return self.config["General"]
|
||||
|
||||
def get_patch_config(self) -> dict:
|
||||
if self.config:
|
||||
return self.config["General"]["patching"]
|
||||
self.load_config()
|
||||
return self.config["General"]["patching"]
|
||||
|
||||
def load_config(self) -> None:
|
||||
if not Path.exists(self.config_path):
|
||||
fail_exit(f"Could not load arch config file at '{self.config_path}'")
|
||||
with open(self.config_path) as f:
|
||||
conf = json.loads(f.read())
|
||||
if self.arch not in conf:
|
||||
fail_exit(
|
||||
f"{self.arch} has no configuration. Please add them in {self.config_path}!"
|
||||
)
|
||||
self.config = conf
|
||||
|
||||
def ts_set_cpp_language(self) -> None:
|
||||
self.ts_cpp_lang = Language(ts_cpp.language())
|
||||
|
||||
def init_parser(self) -> None:
|
||||
log.debug("Init parser")
|
||||
self.parser = Parser(self.ts_cpp_lang)
|
||||
+551
@@ -0,0 +1,551 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import argparse
|
||||
import logging as log
|
||||
import sys
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import termcolor
|
||||
from tree_sitter import Language, Node, Parser, Query, Tree
|
||||
|
||||
from autosync.cpptranslator.Configurator import Configurator
|
||||
from autosync.cpptranslator.patches.AddCSDetail import AddCSDetail
|
||||
from autosync.cpptranslator.patches.AddOperand import AddOperand
|
||||
from autosync.cpptranslator.patches.Assert import Assert
|
||||
from autosync.cpptranslator.patches.BitCastStdArray import BitCastStdArray
|
||||
from autosync.cpptranslator.patches.CheckDecoderStatus import CheckDecoderStatus
|
||||
from autosync.cpptranslator.patches.ClassConstructorDef import ClassConstructorDef
|
||||
from autosync.cpptranslator.patches.ClassesDef import ClassesDef
|
||||
from autosync.cpptranslator.patches.ConstMCInstParameter import ConstMCInstParameter
|
||||
from autosync.cpptranslator.patches.ConstMCOperand import ConstMCOperand
|
||||
from autosync.cpptranslator.patches.CppInitCast import CppInitCast
|
||||
from autosync.cpptranslator.patches.CreateOperand0 import CreateOperand0
|
||||
from autosync.cpptranslator.patches.CreateOperand1 import CreateOperand1
|
||||
from autosync.cpptranslator.patches.Data import Data
|
||||
from autosync.cpptranslator.patches.DeclarationInConditionClause import (
|
||||
DeclarationInConditionalClause,
|
||||
)
|
||||
from autosync.cpptranslator.patches.DecodeInstruction import DecodeInstruction
|
||||
from autosync.cpptranslator.patches.DecoderCast import DecoderCast
|
||||
from autosync.cpptranslator.patches.DecoderParameter import DecoderParameter
|
||||
from autosync.cpptranslator.patches.FallThrough import FallThrough
|
||||
from autosync.cpptranslator.patches.FeatureBits import FeatureBits
|
||||
from autosync.cpptranslator.patches.FeatureBitsDecl import FeatureBitsDecl
|
||||
from autosync.cpptranslator.patches.FieldFromInstr import FieldFromInstr
|
||||
from autosync.cpptranslator.patches.GetNumOperands import GetNumOperands
|
||||
from autosync.cpptranslator.patches.GetOpcode import GetOpcode
|
||||
from autosync.cpptranslator.patches.GetOperand import GetOperand
|
||||
from autosync.cpptranslator.patches.GetOperandRegImm import GetOperandRegImm
|
||||
from autosync.cpptranslator.patches.GetRegClass import GetRegClass
|
||||
from autosync.cpptranslator.patches.GetRegFromClass import GetRegFromClass
|
||||
from autosync.cpptranslator.patches.GetSubReg import GetSubReg
|
||||
from autosync.cpptranslator.patches.Includes import Includes
|
||||
from autosync.cpptranslator.patches.InlineToStaticInline import InlineToStaticInline
|
||||
from autosync.cpptranslator.patches.IsOptionalDef import IsOptionalDef
|
||||
from autosync.cpptranslator.patches.IsPredicate import IsPredicate
|
||||
from autosync.cpptranslator.patches.IsRegImm import IsOperandRegImm
|
||||
from autosync.cpptranslator.patches.LLVMFallThrough import LLVMFallThrough
|
||||
from autosync.cpptranslator.patches.LLVMunreachable import LLVMUnreachable
|
||||
from autosync.cpptranslator.patches.BadConditionCode import BadConditionCode
|
||||
from autosync.cpptranslator.patches.LLVM_DEBUG import LLVM_DEBUG
|
||||
from autosync.cpptranslator.patches.MethodToFunctions import MethodToFunction
|
||||
from autosync.cpptranslator.patches.MethodTypeQualifier import MethodTypeQualifier
|
||||
from autosync.cpptranslator.patches.NamespaceAnon import NamespaceAnon
|
||||
from autosync.cpptranslator.patches.NamespaceArch import NamespaceArch
|
||||
from autosync.cpptranslator.patches.NamespaceLLVM import NamespaceLLVM
|
||||
from autosync.cpptranslator.patches.OutStreamParam import OutStreamParam
|
||||
from autosync.cpptranslator.patches.Override import Override
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
from autosync.cpptranslator.patches.PredicateBlockFunctions import (
|
||||
PredicateBlockFunctions,
|
||||
)
|
||||
from autosync.cpptranslator.patches.PrintAnnotation import PrintAnnotation
|
||||
from autosync.cpptranslator.patches.PrintRegImmShift import PrintRegImmShift
|
||||
from autosync.cpptranslator.patches.QualifiedIdentifier import QualifiedIdentifier
|
||||
from autosync.cpptranslator.patches.ReferencesDecl import ReferencesDecl
|
||||
from autosync.cpptranslator.patches.RegClassContains import RegClassContains
|
||||
from autosync.cpptranslator.patches.SetOpcode import SetOpcode
|
||||
from autosync.cpptranslator.patches.SignExtend import SignExtend
|
||||
from autosync.cpptranslator.patches.Size import Size
|
||||
from autosync.cpptranslator.patches.SizeAssignments import SizeAssignment
|
||||
from autosync.cpptranslator.patches.STIArgument import STIArgument
|
||||
from autosync.cpptranslator.patches.STIFeatureBits import STIFeatureBits
|
||||
from autosync.cpptranslator.patches.STParameter import SubtargetInfoParam
|
||||
from autosync.cpptranslator.patches.StreamOperation import StreamOperations
|
||||
from autosync.cpptranslator.patches.TemplateDeclaration import TemplateDeclaration
|
||||
from autosync.cpptranslator.patches.TemplateDefinition import TemplateDefinition
|
||||
from autosync.cpptranslator.patches.TemplateParamDecl import TemplateParamDecl
|
||||
from autosync.cpptranslator.patches.TemplateRefs import TemplateRefs
|
||||
from autosync.cpptranslator.patches.UseMarkup import UseMarkup
|
||||
from autosync.cpptranslator.patches.UsingDeclaration import UsingDeclaration
|
||||
from autosync.cpptranslator.TemplateCollector import TemplateCollector
|
||||
from autosync.Helper import (
|
||||
convert_loglevel,
|
||||
get_header,
|
||||
get_path,
|
||||
print_prominent_warning,
|
||||
run_clang_format,
|
||||
)
|
||||
from autosync.cpptranslator.patches.isUInt import IsUInt
|
||||
from autosync.cpptranslator.tree_sitter_compatibility import query_captures_22_3
|
||||
|
||||
|
||||
class Translator:
|
||||
ts_cpp_lang: Language = None
|
||||
parser: Parser = None
|
||||
template_collector: TemplateCollector = None
|
||||
src_paths: [Path]
|
||||
out_paths: [Path]
|
||||
conf: dict
|
||||
src = b""
|
||||
current_src_path_in: Path = None
|
||||
current_src_path_out: Path = None
|
||||
tree: Tree = None
|
||||
|
||||
# Patch priorities: The bigger the number the later the patch will be applied.
|
||||
# Patches which create templates must always be executed last. Since syntax
|
||||
# in macros is no longer parsed as such (but is only recognized as macro body).
|
||||
#
|
||||
# If a patch must be executed before another patch (because the matching rules depend on it)
|
||||
# mark this dependency as you see below.
|
||||
patches: [Patch] = list()
|
||||
|
||||
patch_priorities: {str: int} = {
|
||||
RegClassContains.__name__: 0,
|
||||
GetRegClass.__name__: 0,
|
||||
GetRegFromClass.__name__: 0,
|
||||
CppInitCast.__name__: 0,
|
||||
BitCastStdArray.__name__: 0,
|
||||
PrintRegImmShift.__name__: 0,
|
||||
InlineToStaticInline.__name__: 0,
|
||||
GetSubReg.__name__: 0,
|
||||
UseMarkup.__name__: 0,
|
||||
ConstMCOperand.__name__: 0,
|
||||
ClassConstructorDef.__name__: 0,
|
||||
ConstMCInstParameter.__name__: 0,
|
||||
PrintAnnotation.__name__: 0,
|
||||
GetNumOperands.__name__: 0,
|
||||
STIArgument.__name__: 0,
|
||||
DecodeInstruction.__name__: 0,
|
||||
FallThrough.__name__: 0,
|
||||
SizeAssignment.__name__: 0,
|
||||
FieldFromInstr.__name__: 0,
|
||||
FeatureBitsDecl.__name__: 0,
|
||||
FeatureBits.__name__: 0,
|
||||
STIFeatureBits.__name__: 0,
|
||||
Includes.__name__: 0,
|
||||
CreateOperand0.__name__: 0, # ◁───┐ `CreateOperand0` removes most calls to MI.addOperand().
|
||||
AddOperand.__name__: 1, # ────────┘ The ones left are fixed with the `AddOperand` patch.
|
||||
CreateOperand1.__name__: 0,
|
||||
IsUInt.__name__: 0,
|
||||
GetOpcode.__name__: 0,
|
||||
SetOpcode.__name__: 0,
|
||||
GetOperand.__name__: 0,
|
||||
GetOperandRegImm.__name__: 0,
|
||||
IsOperandRegImm.__name__: 0,
|
||||
SignExtend.__name__: 0,
|
||||
DecoderParameter.__name__: 0,
|
||||
UsingDeclaration.__name__: 0,
|
||||
DecoderCast.__name__: 0,
|
||||
IsPredicate.__name__: 0,
|
||||
IsOptionalDef.__name__: 0,
|
||||
Assert.__name__: 0, # ◁─────────┐ The llvm_unreachable calls are replaced with asserts.
|
||||
LLVMUnreachable.__name__: 1, # ─┘ Those assert should stay.
|
||||
LLVMFallThrough.__name__: 0,
|
||||
BadConditionCode.__name__: 0,
|
||||
LLVM_DEBUG.__name__: 0,
|
||||
DeclarationInConditionalClause.__name__: 0,
|
||||
StreamOperations.__name__: 0,
|
||||
OutStreamParam.__name__: 0, # ◁──────┐ add_cs_detail() is added to printOperand functions with a certain
|
||||
SubtargetInfoParam.__name__: 0, # ◁──┤ signature. This signature depends on those patches.
|
||||
MethodToFunction.__name__: 0, # ◁────┤
|
||||
AddCSDetail.__name__: 1, # ──────────┘
|
||||
NamespaceAnon.__name__: 0, # ◁─────┐ "llvm" and anonymous namespaces must be removed first,
|
||||
NamespaceLLVM.__name__: 0, # ◁─────┤ so they don't match in NamespaceArch.
|
||||
NamespaceArch.__name__: 1, # ──────┘
|
||||
PredicateBlockFunctions.__name__: 0,
|
||||
Override.__name__: 0,
|
||||
Size.__name__: 0,
|
||||
Data.__name__: 0,
|
||||
ClassesDef.__name__: 0, # ◁────────┐ Declarations must be extracted first from the classes.
|
||||
MethodTypeQualifier.__name__: 1, # ┘
|
||||
# All previous patches can contain qualified identifiers (Ids with the "::" operator) in their search patterns.
|
||||
# After this patch they are removed.
|
||||
QualifiedIdentifier.__name__: 2,
|
||||
ReferencesDecl.__name__: 3, # ◁────┐
|
||||
CheckDecoderStatus.__name__: 4, # ─┘ Reference declarations must be removed first.
|
||||
TemplateParamDecl.__name__: 5,
|
||||
TemplateRefs.__name__: 5,
|
||||
# Template declarations are replaced with macros.
|
||||
# Those declarations are parsed as macro afterwards
|
||||
TemplateDeclaration.__name__: 5,
|
||||
# Template definitions are replaced with macros.
|
||||
# Those template functions are parsed as macro afterwards.
|
||||
TemplateDefinition.__name__: 6,
|
||||
}
|
||||
|
||||
def __init__(self, configure: Configurator, wait_for_user: bool = False):
|
||||
self.configurator = configure
|
||||
self.wait_for_user = wait_for_user
|
||||
self.arch = self.configurator.get_arch()
|
||||
self.conf = self.configurator.get_arch_config()
|
||||
self.conf_general = self.configurator.get_general_config()
|
||||
self.ts_cpp_lang = self.configurator.get_cpp_lang()
|
||||
self.parser = self.configurator.get_parser()
|
||||
|
||||
self.src_paths: [Path] = [
|
||||
get_path(sp["in"]) for sp in self.conf["files_to_translate"]
|
||||
]
|
||||
t_out_dir: Path = get_path("{CPP_TRANSLATOR_TRANSLATION_OUT_DIR}")
|
||||
self.out_paths: [Path] = [
|
||||
t_out_dir.joinpath(sp["out"]) for sp in self.conf["files_to_translate"]
|
||||
]
|
||||
|
||||
self.collect_template_instances()
|
||||
self.init_patches()
|
||||
|
||||
def read_src_file(self, src_path: Path) -> None:
|
||||
"""Reads the file at src_path into self.src"""
|
||||
log.debug(f"Read {src_path}")
|
||||
if not Path.exists(src_path):
|
||||
log.fatal(f"Could not open the source file '{src_path}'")
|
||||
exit(1)
|
||||
with open(src_path) as f:
|
||||
self.src = bytes(f.read(), "utf8")
|
||||
|
||||
def init_patches(self):
|
||||
log.debug("Init patches")
|
||||
priorities = dict(
|
||||
sorted(self.patch_priorities.items(), key=lambda item: item[1])
|
||||
)
|
||||
for ptype, p in priorities.items():
|
||||
match ptype:
|
||||
case RegClassContains.__name__:
|
||||
patch = RegClassContains(p)
|
||||
case GetRegClass.__name__:
|
||||
patch = GetRegClass(p)
|
||||
case GetRegFromClass.__name__:
|
||||
patch = GetRegFromClass(p)
|
||||
case CppInitCast.__name__:
|
||||
patch = CppInitCast(p)
|
||||
case BitCastStdArray.__name__:
|
||||
patch = BitCastStdArray(p)
|
||||
case CheckDecoderStatus.__name__:
|
||||
patch = CheckDecoderStatus(p)
|
||||
case ReferencesDecl.__name__:
|
||||
patch = ReferencesDecl(p)
|
||||
case FieldFromInstr.__name__:
|
||||
patch = FieldFromInstr(p)
|
||||
case FeatureBitsDecl.__name__:
|
||||
patch = FeatureBitsDecl(p)
|
||||
case FeatureBits.__name__:
|
||||
patch = FeatureBits(p, bytes(self.arch, "utf8"))
|
||||
case STIFeatureBits.__name__:
|
||||
patch = STIFeatureBits(p, bytes(self.arch, "utf8"))
|
||||
case QualifiedIdentifier.__name__:
|
||||
patch = QualifiedIdentifier(p)
|
||||
case Includes.__name__:
|
||||
patch = Includes(p, self.arch)
|
||||
case ClassesDef.__name__:
|
||||
patch = ClassesDef(p)
|
||||
case CreateOperand0.__name__:
|
||||
patch = CreateOperand0(p)
|
||||
case CreateOperand1.__name__:
|
||||
patch = CreateOperand1(p)
|
||||
case IsUInt.__name__:
|
||||
patch = IsUInt(p)
|
||||
case GetOpcode.__name__:
|
||||
patch = GetOpcode(p)
|
||||
case SetOpcode.__name__:
|
||||
patch = SetOpcode(p)
|
||||
case GetOperand.__name__:
|
||||
patch = GetOperand(p)
|
||||
case SignExtend.__name__:
|
||||
patch = SignExtend(p)
|
||||
case TemplateDeclaration.__name__:
|
||||
patch = TemplateDeclaration(p, self.template_collector)
|
||||
case TemplateDefinition.__name__:
|
||||
patch = TemplateDefinition(p, self.template_collector)
|
||||
case DecoderParameter.__name__:
|
||||
patch = DecoderParameter(p)
|
||||
case TemplateRefs.__name__:
|
||||
patch = TemplateRefs(p)
|
||||
case TemplateParamDecl.__name__:
|
||||
patch = TemplateParamDecl(p)
|
||||
case MethodTypeQualifier.__name__:
|
||||
patch = MethodTypeQualifier(p)
|
||||
case UsingDeclaration.__name__:
|
||||
patch = UsingDeclaration(p)
|
||||
case NamespaceLLVM.__name__:
|
||||
patch = NamespaceLLVM(p)
|
||||
case DecoderCast.__name__:
|
||||
patch = DecoderCast(p)
|
||||
case IsPredicate.__name__:
|
||||
patch = IsPredicate(p)
|
||||
case IsOptionalDef.__name__:
|
||||
patch = IsOptionalDef(p)
|
||||
case Assert.__name__:
|
||||
patch = Assert(p)
|
||||
case LLVMFallThrough.__name__:
|
||||
patch = LLVMFallThrough(p)
|
||||
case BadConditionCode.__name__:
|
||||
patch = BadConditionCode(p)
|
||||
case DeclarationInConditionalClause.__name__:
|
||||
patch = DeclarationInConditionalClause(p)
|
||||
case OutStreamParam.__name__:
|
||||
patch = OutStreamParam(p)
|
||||
case MethodToFunction.__name__:
|
||||
patch = MethodToFunction(p)
|
||||
case GetOperandRegImm.__name__:
|
||||
patch = GetOperandRegImm(p)
|
||||
case StreamOperations.__name__:
|
||||
patch = StreamOperations(p)
|
||||
case SubtargetInfoParam.__name__:
|
||||
patch = SubtargetInfoParam(p)
|
||||
case SizeAssignment.__name__:
|
||||
patch = SizeAssignment(p)
|
||||
case NamespaceArch.__name__:
|
||||
patch = NamespaceArch(p)
|
||||
case NamespaceAnon.__name__:
|
||||
patch = NamespaceAnon(p)
|
||||
case PredicateBlockFunctions.__name__:
|
||||
patch = PredicateBlockFunctions(p)
|
||||
case FallThrough.__name__:
|
||||
patch = FallThrough(p)
|
||||
case DecodeInstruction.__name__:
|
||||
patch = DecodeInstruction(p)
|
||||
case STIArgument.__name__:
|
||||
patch = STIArgument(p)
|
||||
case GetNumOperands.__name__:
|
||||
patch = GetNumOperands(p)
|
||||
case AddOperand.__name__:
|
||||
patch = AddOperand(p)
|
||||
case PrintAnnotation.__name__:
|
||||
patch = PrintAnnotation(p)
|
||||
case ConstMCInstParameter.__name__:
|
||||
patch = ConstMCInstParameter(p)
|
||||
case LLVMUnreachable.__name__:
|
||||
patch = LLVMUnreachable(p)
|
||||
case LLVM_DEBUG.__name__:
|
||||
patch = LLVM_DEBUG(p)
|
||||
case ClassConstructorDef.__name__:
|
||||
patch = ClassConstructorDef(p)
|
||||
case ConstMCOperand.__name__:
|
||||
patch = ConstMCOperand(p)
|
||||
case UseMarkup.__name__:
|
||||
patch = UseMarkup(p)
|
||||
case GetSubReg.__name__:
|
||||
patch = GetSubReg(p)
|
||||
case InlineToStaticInline.__name__:
|
||||
patch = InlineToStaticInline(p)
|
||||
case AddCSDetail.__name__:
|
||||
patch = AddCSDetail(p, self.arch)
|
||||
case PrintRegImmShift.__name__:
|
||||
patch = PrintRegImmShift(p)
|
||||
case IsOperandRegImm.__name__:
|
||||
patch = IsOperandRegImm(p)
|
||||
case Override.__name__:
|
||||
patch = Override(p)
|
||||
case Size.__name__:
|
||||
patch = Size(p)
|
||||
case Data.__name__:
|
||||
patch = Data(p)
|
||||
case _:
|
||||
log.fatal(f"Patch type {ptype} not in Patch init routine.")
|
||||
exit(1)
|
||||
self.patches.append(patch)
|
||||
|
||||
def parse(self, src_path: Path) -> None:
|
||||
self.read_src_file(src_path)
|
||||
log.debug("Parse source code")
|
||||
self.tree = self.parser.parse(self.src)
|
||||
|
||||
def patch_src(self, p_list: [(bytes, Node)]) -> None:
|
||||
if len(p_list) == 0:
|
||||
return
|
||||
# Sort list of patches descending so the patches which are last in the file
|
||||
# get patched first. This way the indices of the code snippets before
|
||||
# don't change.
|
||||
patches = sorted(p_list, key=lambda x: x[1].start_byte, reverse=True)
|
||||
|
||||
new_src = b""
|
||||
patch: bytes
|
||||
node: Node
|
||||
for patch, node in patches:
|
||||
start_byte: int = node.start_byte
|
||||
old_end_byte: int = node.end_byte
|
||||
start_point: (int, int) = node.start_point
|
||||
old_end_point: (int, int) = node.end_point
|
||||
|
||||
new_src = self.src[:start_byte] + patch + self.src[old_end_byte:]
|
||||
self.src = new_src
|
||||
d = len(patch) - (old_end_byte - start_byte)
|
||||
self.tree.edit(
|
||||
start_byte=start_byte,
|
||||
old_end_byte=old_end_byte,
|
||||
new_end_byte=old_end_byte + d,
|
||||
start_point=start_point,
|
||||
old_end_point=old_end_point,
|
||||
new_end_point=(old_end_point[0], old_end_point[1] + d),
|
||||
)
|
||||
self.tree = self.parser.parse(new_src, self.tree)
|
||||
|
||||
def apply_patch(self, patch: Patch) -> bool:
|
||||
"""Tests if the given patch should be applied for the current architecture or file."""
|
||||
apply_only_to = self.configurator.get_patch_config()["apply_patch_only_to"]
|
||||
patch_name = patch.__class__.__name__
|
||||
if patch_name not in apply_only_to:
|
||||
# No constraints
|
||||
return True
|
||||
|
||||
file_constraints = apply_only_to[patch_name]
|
||||
if self.current_src_path_in.name in file_constraints["files"]:
|
||||
return True
|
||||
elif (
|
||||
re.search("InstPrinter.cpp", self.current_src_path_in.name)
|
||||
and patch_name == AddCSDetail.__name__
|
||||
):
|
||||
print_prominent_warning(
|
||||
(
|
||||
f"The AddCSDetail patch is not applied to {self.current_src_path_in.name}. "
|
||||
"Have you forgotten to add it to arch_config.json?"
|
||||
),
|
||||
False,
|
||||
)
|
||||
return False
|
||||
|
||||
def translate(self) -> None:
|
||||
for self.current_src_path_in, self.current_src_path_out in zip(
|
||||
self.src_paths, self.out_paths
|
||||
):
|
||||
log.info(f"Translate '{self.current_src_path_in}'")
|
||||
self.parse(self.current_src_path_in)
|
||||
patch: Patch
|
||||
for patch in self.patches:
|
||||
if not self.apply_patch(patch):
|
||||
log.debug(f"Skip patch {patch.__class__.__name__}")
|
||||
continue
|
||||
pattern: str = patch.get_search_pattern()
|
||||
|
||||
# Each patch has a capture which includes the whole subtree searched for.
|
||||
# Additionally, it can include captures within this subtree.
|
||||
# Here we bundle these captures together.
|
||||
query: Query = self.ts_cpp_lang.query(pattern)
|
||||
captures_bundle: [[(Node, str)]] = list()
|
||||
for q in query_captures_22_3(query, self.tree.root_node):
|
||||
if q[1] == patch.get_main_capture_name():
|
||||
# The main capture the patch is looking for.
|
||||
captures_bundle.append([q])
|
||||
else:
|
||||
# A capture which is part of the main capture.
|
||||
# Add it to the bundle.
|
||||
if len(captures_bundle) > 0:
|
||||
captures_bundle[-1].append(q)
|
||||
|
||||
log.debug(
|
||||
f"Patch {patch.__class__.__name__} (to patch: {len(captures_bundle)})."
|
||||
)
|
||||
|
||||
p_list: (bytes, Node) = list()
|
||||
cb: [(Node, str)]
|
||||
for cb in captures_bundle:
|
||||
patch_kwargs = self.get_patch_kwargs(patch)
|
||||
bytes_patch: bytes = patch.get_patch(cb, self.src, **patch_kwargs)
|
||||
p_list.append((bytes_patch, cb[0][0]))
|
||||
self.patch_src(p_list)
|
||||
if self.tree.root_node.type == "ERROR":
|
||||
log.fatal(
|
||||
f"Patch {patch.__class__.__name__} corrupts the tree for {self.current_src_path_in.name}!"
|
||||
)
|
||||
exit(1)
|
||||
log.info(f"Patched file at '{self.current_src_path_out}'")
|
||||
with open(self.current_src_path_out, "w") as f:
|
||||
f.write(get_header())
|
||||
f.write(self.src.decode("utf8"))
|
||||
run_clang_format(self.out_paths)
|
||||
|
||||
def collect_template_instances(self):
|
||||
search_paths = [get_path(p) for p in self.conf["files_for_template_search"]]
|
||||
temp_arg_deduction = [
|
||||
p.encode("utf8") for p in self.conf["templates_with_arg_deduction"]
|
||||
]
|
||||
self.template_collector = TemplateCollector(
|
||||
self.parser, self.ts_cpp_lang, search_paths, temp_arg_deduction
|
||||
)
|
||||
self.template_collector.collect()
|
||||
|
||||
def get_patch_kwargs(self, patch):
|
||||
default_kwargs = dict()
|
||||
default_kwargs["tree"] = self.tree
|
||||
default_kwargs["ts_cpp_lang"] = self.ts_cpp_lang
|
||||
if isinstance(patch, Includes) and self.current_src_path_in:
|
||||
default_kwargs["filename"] = self.current_src_path_in.name
|
||||
return default_kwargs
|
||||
|
||||
def remark_manual_files(self) -> None:
|
||||
manual_edited = self.conf["manually_edited_files"]
|
||||
msg = ""
|
||||
if len(manual_edited) > 0:
|
||||
msg += (
|
||||
termcolor.colored(
|
||||
"The following files are too complex to translate! Please check them by hand.",
|
||||
attrs=["bold"],
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
else:
|
||||
return
|
||||
for f in manual_edited:
|
||||
msg += get_path(f).name + "\n"
|
||||
print_prominent_warning(msg, self.wait_for_user)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="CppTranslator",
|
||||
description="Capstones C++ to C translator for LLVM source files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
dest="arch",
|
||||
help="Name of target architecture.",
|
||||
choices=["ARM", "PPC", "AArch64", "Alpha"],
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
dest="verbosity",
|
||||
help="Verbosity of the log messages.",
|
||||
choices=["debug", "info", "warning", "fatal"],
|
||||
default="info",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
dest="config_path",
|
||||
help="Config file for architectures.",
|
||||
default="arch_config.json",
|
||||
type=Path,
|
||||
)
|
||||
arguments = parser.parse_args()
|
||||
return arguments
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not sys.hexversion >= 0x030B00F0:
|
||||
log.fatal("Python >= v3.11 required.")
|
||||
exit(1)
|
||||
|
||||
args = parse_args()
|
||||
log.basicConfig(
|
||||
level=convert_loglevel(args.verbosity),
|
||||
stream=sys.stdout,
|
||||
format="%(levelname)-5s - %(message)s",
|
||||
)
|
||||
configurator = Configurator(args.arch, args.config_path)
|
||||
translator = Translator(configurator)
|
||||
translator.translate()
|
||||
translator.remark_manual_files()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
<!--
|
||||
Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
SPDX-License-Identifier: BSD-3
|
||||
-->
|
||||
|
||||
# C++ Translator
|
||||
|
||||
Capstone uses source files from LLVM to disassemble opcodes.
|
||||
Because LLVM is written in C++ we must translate those files to C.
|
||||
|
||||
The task of the `CppTranslator` is to do just that.
|
||||
The translation will not result in a completely correct C file! But it takes away most of the manual work.
|
||||
|
||||
## The configuration file
|
||||
|
||||
The configuration for each architecture is set in `arch_config.json`.
|
||||
|
||||
The config values have the following meaning:
|
||||
|
||||
- `General`: Settings valid for all architectures.
|
||||
- `diff_color_new`: Color in the `Differ` for translated content.
|
||||
- `diff_color_old`: Color in the `Differ` for old/current Capstone content.
|
||||
- `diff_color_saved`: Color in the `Differ` for saved content.
|
||||
- `diff_color_edited`: Color in the `Differ` for edited content.
|
||||
- `patch_editor`: Editor to open for patch editing.
|
||||
- `nodes_to_diff`: List of parse tree nodes which get diffed - *Mind the note below*.
|
||||
- `node_type`: The `type` of the node to be diffed.
|
||||
- `identifier_node_type`: Types of child nodes which identify the node during diffing (the identifier must be the same in the translated and the old file!). Types can be of the form `<parent-type>/<child type>`.
|
||||
- `<ARCH>`: Settings valid for a specific architecture
|
||||
- `files_to_translate`: A list of file paths to translate.
|
||||
- `in`: *Path* to a specific source file.
|
||||
- `out`: The *filename* of the translated file.
|
||||
- `files_for_template_search`: List of file paths to search for calls to template functions.
|
||||
- `manually_edite_files`: List of files which are too complicated to translate. The user will be warned about them.
|
||||
- `templates_with_arg_deduction`: Template functions which uses [argument deduction](https://en.cppreference.com/w/cpp/language/template_argument_deduction). Those templates are translated to normal functions, not macro definition.
|
||||
|
||||
_Note_:
|
||||
- To understand the `nodes_to_diff` setting, check out `Differ.py`.
|
||||
- Paths can contain `{AUTO_SYNC_ROOT}`, `{CS_ROOT}` and `{CPP_TRANSLATOR_ROOT}`.
|
||||
They are replaced with the absolute paths to those directories.
|
||||
|
||||
## Translation process
|
||||
|
||||
The translation process simply searches for certain syntax and patches it.
|
||||
|
||||
To allow searches for complicated patterns we parse the C++ file with Tree-sitter.
|
||||
Afterward we can use [pattern queries](https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries)
|
||||
to find our syntax we would like to patch.
|
||||
|
||||
Here is an overview of the procedure:
|
||||
|
||||
- First the source file is parsed with Tree-Sitter.
|
||||
- Afterward the translator iterates of a number of patches.
|
||||
|
||||
For each patch we do the following.
|
||||
|
||||
```
|
||||
|
||||
Translator Patch
|
||||
+---+
|
||||
| | +----+
|
||||
| | Request pattern to search for | |
|
||||
| | ----------------------------------> | |
|
||||
| | | |
|
||||
| | Return pattern | |
|
||||
| | <--------------------------------- | |
|
||||
| | | |
|
||||
| | ---+ | |
|
||||
| | | Find | |
|
||||
| | | captures | |
|
||||
| | | in src | |
|
||||
| | <--+ | |
|
||||
| | | |
|
||||
| | Return captures found | |
|
||||
| | ----------------------------------> | |
|
||||
| | | |
|
||||
| | +-- | |
|
||||
| | Use capture | | |
|
||||
| | info to | | |
|
||||
| | build new | | |
|
||||
| | syntax str | | |
|
||||
| | +-> | |
|
||||
| | | |
|
||||
| | Return new syntax string to patch | |
|
||||
| | <---------------------------------- | |
|
||||
| | | |
|
||||
| | ---+ | |
|
||||
| | | Replace old | |
|
||||
| | | with new syntax | |
|
||||
| | | at all occurrences | |
|
||||
| | | in the file. | |
|
||||
| | <--+ | |
|
||||
| | | |
|
||||
+---+ +----+
|
||||
```
|
||||
|
||||
## C++ Template translation
|
||||
|
||||
Most of the C++ syntax is simple to translate. But unfortunately the one exception are C++ templates.
|
||||
|
||||
Translating template functions and calls from C++ to C is tricky.
|
||||
Since each template has a number of actual implementations we do the following.
|
||||
|
||||
- A template function definition is translated into a C macro.
|
||||
- The template parameters get translated to the macro parameters.
|
||||
- To differentiate the C implementations, the functions follow the naming pattern `fcn_[template_param_0]_[template_param_1]()`
|
||||
|
||||
<hr>
|
||||
|
||||
**Example**
|
||||
|
||||
This C++ template function
|
||||
|
||||
```cpp
|
||||
template<unsigned X>
|
||||
void fcn() {
|
||||
unsigned a = X * 8;
|
||||
}
|
||||
```
|
||||
becomes
|
||||
```
|
||||
#define DEFINE_FCN(X) \
|
||||
void fcn ## _ ## X() { \
|
||||
unsigned a = X * 8; \
|
||||
}
|
||||
```
|
||||
To define an implementation where `X = 0` we do
|
||||
```
|
||||
DEFINE_FCN(0)
|
||||
```
|
||||
To call this implementation we call `fcn_0()`.
|
||||
|
||||
_(There is a special case when a template parameter is passed on to a template call. But this is explained in the code.)_
|
||||
<hr>
|
||||
|
||||
### Enumerate template instances
|
||||
|
||||
In our C++ code a template function can be called with different template parameters.
|
||||
For each of those calls we need to define a template implementation in C.
|
||||
|
||||
To do that we first scan source files for calls to template functions (`TemplateCollector.py` does this).
|
||||
For each unique call we check the parameter list.
|
||||
Knowing the parameter list we can now define a C function which uses exactly those parameters.
|
||||
For the definition we use a macro as above.
|
||||
|
||||
<hr>
|
||||
|
||||
**Example**
|
||||
|
||||
Within this C++ code we see two template function calls:
|
||||
|
||||
```cpp
|
||||
void main() {
|
||||
fcn<0>();
|
||||
fcn<4>();
|
||||
}
|
||||
```
|
||||
With the knowledge that once parameter `1` and once parameter `4` was passed to the template,
|
||||
we can define the implementations with the help of our `DEFINE_FCN` macro.
|
||||
```c
|
||||
DEFINE_FCN(0)
|
||||
DEFINE_FCN(4)
|
||||
```
|
||||
|
||||
Within the C code we can now call those with `fcn_0()` and `fcn_4()`.
|
||||
<hr>
|
||||
+346
@@ -0,0 +1,346 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from tree_sitter import Language, Node, Parser, Query
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.tree_sitter_compatibility import query_captures_22_3
|
||||
|
||||
|
||||
class TemplateRefInstance:
|
||||
"""
|
||||
Represents a concrete instance of a template function reference.
|
||||
E.g. DecodeT2Imm7<shift, 2>
|
||||
"""
|
||||
|
||||
name: bytes
|
||||
args: bytes
|
||||
args_list: list
|
||||
dependent_calls = list()
|
||||
|
||||
# Holds the indices of the caller template parameters which set the templ. parameters
|
||||
# of this TemplateCallInstance.
|
||||
# Structure: {caller_name: i, "self_i": k}
|
||||
#
|
||||
# Only used if this is an incomplete TemplateInstance
|
||||
# (parameters are set by the template parameters of the calling function).
|
||||
caller_param_indices: [{str: int}] = list()
|
||||
|
||||
def __init__(
|
||||
self, name: bytes, args: bytes, start_point, start_byte, end_point, end_byte
|
||||
):
|
||||
self.name = name
|
||||
self.args = args
|
||||
self.start_point = start_point
|
||||
self.start_byte = start_byte
|
||||
self.end_point = end_point
|
||||
self.end_byte = end_byte
|
||||
self.args_list = TemplateCollector.templ_params_to_list(args)
|
||||
self.templ_name = name + args
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
self.name == other.name
|
||||
and self.args == other.args
|
||||
and any(
|
||||
[
|
||||
a == b
|
||||
for a, b in zip(
|
||||
self.caller_param_indices, other.caller_param_indices
|
||||
)
|
||||
]
|
||||
)
|
||||
and self.start_byte == other.start_byte
|
||||
and self.start_point == other.start_point
|
||||
and self.end_byte == other.end_byte
|
||||
and self.end_point == other.end_point
|
||||
)
|
||||
|
||||
def set_dep_calls(self, deps: list):
|
||||
self.dependent_calls = deps
|
||||
|
||||
def get_c_name(self):
|
||||
return b"_".join([self.name] + self.args_list)
|
||||
|
||||
def get_args_for_decl(self) -> list[bytes]:
|
||||
"""Returns the list of arguments, but replaces all characters which
|
||||
can not be part of a C identifier with _
|
||||
"""
|
||||
args_list = [re.sub(b"'", b"", a) for a in self.args_list]
|
||||
return args_list
|
||||
|
||||
|
||||
class TemplateCollector:
|
||||
"""
|
||||
Searches through the given files for calls to template functions.
|
||||
And creates a list with concrete template instances.
|
||||
"""
|
||||
|
||||
# List of completed template instances indexed by their name.
|
||||
# One function can have multiple template instances. Depending on the template arguments
|
||||
template_refs: {bytes: [TemplateRefInstance]} = dict()
|
||||
# List of incomplete template instances indexed by the **function name they depend on**!
|
||||
incomplete_template_refs: {bytes: [TemplateRefInstance]} = dict()
|
||||
sources: [{str: bytes}] = list()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ts_parser: Parser,
|
||||
ts_cpp: Language,
|
||||
searchable_files: [Path],
|
||||
temp_arg_deduction: [bytes],
|
||||
):
|
||||
self.parser = ts_parser
|
||||
self.lang_cpp = ts_cpp
|
||||
self.searchable_files = searchable_files
|
||||
self.templates_with_arg_deduction = temp_arg_deduction
|
||||
|
||||
def collect(self):
|
||||
self.read_files()
|
||||
for x in self.sources:
|
||||
path = x["path"]
|
||||
src = x["content"]
|
||||
log.debug(f"Search for template references in {path}")
|
||||
|
||||
tree = self.parser.parse(src)
|
||||
query: Query = self.lang_cpp.query(self.get_template_pattern())
|
||||
capture_bundles = self.get_capture_bundles(query, tree)
|
||||
|
||||
for cb in capture_bundles:
|
||||
templ_name: Node = cb[1][0]
|
||||
templ_args: Node = cb[2][0]
|
||||
name = get_text(src, templ_name.start_byte, templ_name.end_byte)
|
||||
args = get_text(src, templ_args.start_byte, templ_args.end_byte)
|
||||
|
||||
ti = TemplateRefInstance(
|
||||
name,
|
||||
args,
|
||||
cb[0][0].start_point,
|
||||
cb[0][0].start_byte,
|
||||
cb[0][0].end_point,
|
||||
cb[0][0].end_byte,
|
||||
)
|
||||
|
||||
log.debug(
|
||||
f"Found new template ref: {name.decode('utf8')}{args.decode('utf8')}"
|
||||
)
|
||||
|
||||
if not self.contains_template_dependent_param(src, ti, cb[0]):
|
||||
if name not in self.template_refs:
|
||||
self.template_refs[name] = list()
|
||||
# The template function has no parameter which is part of a previous
|
||||
# template definition. So all template parameters are well-defined.
|
||||
# Add it to the well-defined list.
|
||||
if ti not in self.template_refs[name]:
|
||||
self.template_refs[name].append(ti)
|
||||
self.resolve_dependencies()
|
||||
|
||||
def resolve_dependencies(self):
|
||||
# Resolve dependencies of templates until nothing new was resolved.
|
||||
prev_len = 0
|
||||
while (
|
||||
len(self.incomplete_template_refs) > 0
|
||||
and len(self.incomplete_template_refs) != prev_len
|
||||
):
|
||||
# Dict with new template calls which were previously incomplete
|
||||
# because one or more parameters were unknown.
|
||||
new_completed_tcs: {str: list} = dict()
|
||||
tc_instance_list: [TemplateRefInstance]
|
||||
for caller_name, tc_instance_list in self.template_refs.items():
|
||||
# Check if this caller has a dependent template call.
|
||||
# In other words: If a template parameter of this caller is given
|
||||
# to another template call in the callers body.
|
||||
if caller_name not in self.incomplete_template_refs:
|
||||
# Not in the dependency list. Skip it.
|
||||
continue
|
||||
# For each configuration of template parameters we complete a template reference.
|
||||
for caller_template in tc_instance_list:
|
||||
incomplete_tc: TemplateRefInstance
|
||||
for incomplete_tc in self.incomplete_template_refs[caller_name]:
|
||||
new_tc: TemplateRefInstance = self.get_completed_tc(
|
||||
caller_template, incomplete_tc
|
||||
)
|
||||
callee_name = new_tc.name
|
||||
if callee_name not in new_completed_tcs:
|
||||
new_completed_tcs[callee_name] = list()
|
||||
if new_tc not in new_completed_tcs[callee_name]:
|
||||
new_completed_tcs[callee_name].append(new_tc)
|
||||
del self.incomplete_template_refs[caller_name]
|
||||
|
||||
for templ_name, tc_list in new_completed_tcs.items():
|
||||
if templ_name in self.template_refs:
|
||||
self.template_refs[templ_name] += tc_list
|
||||
else:
|
||||
self.template_refs[templ_name] = tc_list
|
||||
prev_len = len(self.incomplete_template_refs)
|
||||
if prev_len > 0:
|
||||
log.info(
|
||||
f"Unresolved template calls: {self.incomplete_template_refs.keys()}. Patch them by hand!"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_completed_tc(
|
||||
tc: TemplateRefInstance, itc: TemplateRefInstance
|
||||
) -> TemplateRefInstance:
|
||||
new_tc = TemplateRefInstance(
|
||||
itc.name,
|
||||
itc.args,
|
||||
itc.start_byte,
|
||||
itc.start_byte,
|
||||
itc.end_point,
|
||||
itc.end_byte,
|
||||
)
|
||||
for indices in itc.caller_param_indices:
|
||||
if tc.name not in indices:
|
||||
# Index of other caller function. Skip.
|
||||
continue
|
||||
caller_i = indices[tc.name]
|
||||
self_i = indices["self_i"]
|
||||
new_tc.args_list[self_i] = tc.args_list[caller_i]
|
||||
new_tc.args = TemplateCollector.list_to_templ_params(new_tc.args_list)
|
||||
new_tc.templ_name = new_tc.name + new_tc.args
|
||||
return new_tc
|
||||
|
||||
def contains_template_dependent_param(
|
||||
self, src, ti: TemplateRefInstance, parse_tree: (Node, str)
|
||||
) -> bool:
|
||||
"""Here we check if one of the template parameters of the given template call,
|
||||
is a parameter of the callers template definition.
|
||||
|
||||
Let's assume we find the template call `func_B<X>()`.
|
||||
Now look at the context `func_B<X>` is in:
|
||||
|
||||
template<X>
|
||||
void func_A() {
|
||||
func_B<X>(a)
|
||||
}
|
||||
|
||||
Since `X` is a template parameter of `func_A` we have to wait until we see a call
|
||||
to `func_A<X>` where `X` gets properly defined.
|
||||
|
||||
Until then we save the TemplateInstance of `func_B<X>` in a list of incomplete
|
||||
template calls and note that it depends on `func_A`.
|
||||
If later a call to function `func_A` is found (with a concrete value for `X`) we can add
|
||||
a concrete TemplateInstance of `func_B`.
|
||||
|
||||
:param: src The current source code to operate on.
|
||||
:param: ti The TemplateInstance for which to check dependencies.
|
||||
:param: parse_tree The parse tree of the template call.
|
||||
:return: True if a dependency was found. False otherwise.
|
||||
"""
|
||||
|
||||
# Search up to the function definition this call belongs to
|
||||
node: Node = parse_tree[0]
|
||||
while node.type != "function_definition":
|
||||
node = node.parent
|
||||
if not node.prev_named_sibling.type == "template_parameter_list":
|
||||
# Caller is a normal function definition.
|
||||
# Nothing to do here.
|
||||
return False
|
||||
|
||||
caller_fcn_id = node.named_children[2].named_children[0]
|
||||
caller_fcn_name = get_text(
|
||||
src, caller_fcn_id.start_byte, caller_fcn_id.end_byte
|
||||
)
|
||||
caller_templ_params = get_text(
|
||||
src, node.prev_sibling.start_byte, node.prev_sibling.end_byte
|
||||
)
|
||||
pl = TemplateCollector.templ_params_to_list(caller_templ_params)
|
||||
has_parameter_dependency = False
|
||||
for i, param in enumerate(pl):
|
||||
if param in ti.args_list:
|
||||
has_parameter_dependency = True
|
||||
ti.caller_param_indices.append(
|
||||
{caller_fcn_name: i, "self_i": ti.args_list.index(param)}
|
||||
)
|
||||
|
||||
if not has_parameter_dependency:
|
||||
return False
|
||||
|
||||
if caller_fcn_name not in self.incomplete_template_refs:
|
||||
self.incomplete_template_refs[caller_fcn_name] = list()
|
||||
if ti not in self.incomplete_template_refs[caller_fcn_name]:
|
||||
self.incomplete_template_refs[caller_fcn_name].append(ti)
|
||||
return True
|
||||
|
||||
def read_files(self):
|
||||
for sf in self.searchable_files:
|
||||
if not Path.exists(sf):
|
||||
log.fatal(f"TemplateCollector: Could not find '{sf}' for search.")
|
||||
exit(1)
|
||||
log.debug(f"TemplateCollector: Read {sf}")
|
||||
with open(sf) as f:
|
||||
file = {"path": sf, "content": bytes(f.read(), "utf8")}
|
||||
self.sources.append(file)
|
||||
|
||||
@staticmethod
|
||||
def get_capture_bundles(query, tree):
|
||||
captures_bundle: list[list[tuple[Node, str]]] = list()
|
||||
for q in query_captures_22_3(query, tree.root_node):
|
||||
if q[1] == "templ_ref":
|
||||
captures_bundle.append([q])
|
||||
else:
|
||||
captures_bundle[-1].append(q)
|
||||
return captures_bundle
|
||||
|
||||
@staticmethod
|
||||
def get_template_pattern():
|
||||
"""
|
||||
:return: A pattern which finds either a template function calls or references.
|
||||
"""
|
||||
return (
|
||||
"(template_function"
|
||||
" ((identifier) @name)"
|
||||
" ((template_argument_list) @templ_args)"
|
||||
") @templ_ref"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def templ_params_to_list(templ_params: bytes) -> list[bytes]:
|
||||
if not templ_params:
|
||||
return list()
|
||||
|
||||
params = templ_params.strip(b"<>").split(b",")
|
||||
params = [p.strip() for p in params]
|
||||
res = list()
|
||||
for p in params:
|
||||
if len(p.split(b" ")) == 2:
|
||||
# Typename specified for parameter. Remove it.
|
||||
# If it was more than one space, it is likely an operation like `size + 1`
|
||||
p = p.split(b" ")[1]
|
||||
# true and false get resolved to 1 and 0
|
||||
if p == "true":
|
||||
p = "1"
|
||||
elif p == "false":
|
||||
p = "0"
|
||||
res.append(p)
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def list_to_templ_params(temp_param_list: list) -> bytes:
|
||||
return b"<" + b", ".join(temp_param_list) + b">"
|
||||
|
||||
@staticmethod
|
||||
def get_macro_c_call(name: bytes, arg_param_list: [bytes], fcn_args: bytes = b""):
|
||||
res = b""
|
||||
fa = [name] + arg_param_list
|
||||
for x in fa[:-1]:
|
||||
res += b"CONCAT(" + x + b", "
|
||||
res += fa[-1]
|
||||
return res + (b")" * (len(fa) - 1)) + fcn_args
|
||||
|
||||
@staticmethod
|
||||
def log_missing_ref_and_exit(func_ref: bytes) -> None:
|
||||
log.fatal(
|
||||
f"Template collector has no reference for {func_ref}.\n\n"
|
||||
f"The possible reasons are:\n"
|
||||
"\t\t\t- Not all C++ source files which call this function are listed in the config.\n"
|
||||
"\t\t\t- You removed the C++ template syntax from the .td file for this function.\n"
|
||||
"\t\t\t- The function is a template with argument deduction and has no `template<...>` preamble. "
|
||||
"Add it in the config as exception in this case."
|
||||
)
|
||||
exit(1)
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
{
|
||||
"General": {
|
||||
"diff_color_new": "green",
|
||||
"diff_color_old": "light_blue",
|
||||
"diff_color_saved": "yellow",
|
||||
"diff_color_edited": "light_magenta",
|
||||
"patch_editor": "vim",
|
||||
"patching": {
|
||||
"apply_patch_only_to": {
|
||||
"AddCSDetail": {
|
||||
"files": [
|
||||
"ARMInstPrinter.cpp",
|
||||
"PPCInstPrinter.cpp",
|
||||
"AArch64InstPrinter.cpp",
|
||||
"LoongArchInstPrinter.cpp",
|
||||
"MipsInstPrinter.cpp",
|
||||
"SystemZInstPrinter.cpp",
|
||||
"XtensaInstPrinter.cpp",
|
||||
"SparcInstPrinter.cpp"
|
||||
]
|
||||
},
|
||||
"InlineToStaticInline": {
|
||||
"files": [
|
||||
"ARMAddressingModes.h"
|
||||
]
|
||||
},
|
||||
"PrintRegImmShift": {
|
||||
"files": [
|
||||
"ARMInstPrinter.cpp"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodes_to_diff": [
|
||||
{
|
||||
"node_type": "function_definition",
|
||||
"identifier_node_type": ["function_declarator/identifier"]
|
||||
},{
|
||||
"node_type": "preproc_function_def",
|
||||
"identifier_node_type": ["identifier"]
|
||||
},{
|
||||
"node_type": "preproc_include",
|
||||
"identifier_node_type": ["string_literal", "system_lib_string"]
|
||||
},{
|
||||
"node_type": "preproc_define",
|
||||
"identifier_node_type": ["identifier"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ARM": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp",
|
||||
"out": "ARMDisassembler.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp",
|
||||
"out": "ARMInstPrinter.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.h",
|
||||
"out": "ARMInstPrinter.h"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/MCTargetDesc/ARMAddressingModes.h",
|
||||
"out": "ARMAddressingModes.h"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARM/Utils/ARMBaseInfo.cpp",
|
||||
"out": "ARMBaseInfo.c"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/ARMGenDisassemblerTables.inc",
|
||||
"{CPP_INC_OUT_DIR}/ARMGenAsmWriter.inc",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp"
|
||||
],
|
||||
"templates_with_arg_deduction": [],
|
||||
"manually_edited_files": [
|
||||
"{LLVM_ROOT}/llvm/lib/Target/ARM/Utils/ARMBaseInfo.h"
|
||||
]
|
||||
},
|
||||
"PPC": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/PowerPC/Disassembler/PPCDisassembler.cpp",
|
||||
"out": "PPCDisassembler.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/PowerPC/MCTargetDesc/PPCInstPrinter.cpp",
|
||||
"out": "PPCInstPrinter.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.h",
|
||||
"out": "PPCMCTargetDesc.h"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/PowerPC/MCTargetDesc/PPCPredicates.h",
|
||||
"out": "PPCPredicates.h"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/PPCGenDisassemblerTables.inc",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/PowerPC/Disassembler/PPCDisassembler.cpp"
|
||||
],
|
||||
"templates_with_arg_deduction": [
|
||||
"decodeRegisterClass"
|
||||
],
|
||||
"manually_edited_files": [
|
||||
"{LLVM_ROOT}/llvm/lib/Target/PowerPC/PPCInstrInfo.h",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/PowerPC/PPCRegisterInfo.h"
|
||||
]
|
||||
},
|
||||
"AArch64": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp",
|
||||
"out": "AArch64Disassembler.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp",
|
||||
"out": "AArch64InstPrinter.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.h",
|
||||
"out": "AArch64InstPrinter.h"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AddressingModes.h",
|
||||
"out": "AArch64AddressingModes.h"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp",
|
||||
"out": "AArch64BaseInfo.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h",
|
||||
"out": "AArch64BaseInfo.h"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/AArch64GenDisassemblerTables.inc",
|
||||
"{CPP_INC_OUT_DIR}/AArch64GenAsmWriter.inc",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp"
|
||||
],
|
||||
"templates_with_arg_deduction": [
|
||||
"printImmSVE",
|
||||
"printAMIndexedWB",
|
||||
"isSVECpyImm",
|
||||
"isSVEAddSubImm",
|
||||
"printVectorIndex"
|
||||
],
|
||||
"manually_edited_files": []
|
||||
},
|
||||
"Alpha": {
|
||||
"files_to_translate": [],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/AlphaGenDisassemblerTables.inc",
|
||||
"{CPP_INC_OUT_DIR}/AlphaGenAsmWriter.inc"
|
||||
],
|
||||
"templates_with_arg_deduction": [],
|
||||
"manually_edited_files": []
|
||||
},
|
||||
"LoongArch": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/LoongArch/Disassembler/LoongArchDisassembler.cpp",
|
||||
"out": "LoongArchDisassembler.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp",
|
||||
"out": "LoongArchInstPrinter.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.h",
|
||||
"out": "LoongArchInstPrinter.h"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/LoongArchGenDisassemblerTables.inc",
|
||||
"{CPP_INC_OUT_DIR}/LoongArchGenAsmWriter.inc",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/LoongArch/Disassembler/LoongArchDisassembler.cpp",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp"
|
||||
],
|
||||
"templates_with_arg_deduction": [],
|
||||
"manually_edited_files": []
|
||||
},
|
||||
"Mips": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Mips/Disassembler/MipsDisassembler.cpp",
|
||||
"out": "MipsDisassembler.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Mips/MCTargetDesc/MipsInstPrinter.cpp",
|
||||
"out": "MipsInstPrinter.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Mips/MCTargetDesc/MipsInstPrinter.h",
|
||||
"out": "MipsInstPrinter.h"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/MipsGenDisassemblerTables.inc",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/Mips/MCTargetDesc/MipsInstPrinter.cpp",
|
||||
"{CPP_INC_OUT_DIR}/MipsGenAsmWriter.inc"
|
||||
],
|
||||
"templates_with_arg_deduction": [
|
||||
"DecodeINSVE_DF",
|
||||
"DecodeDAHIDATIMMR6",
|
||||
"DecodeDAHIDATI",
|
||||
"DecodeAddiGroupBranch",
|
||||
"DecodePOP35GroupBranchMMR6",
|
||||
"DecodeDaddiGroupBranch",
|
||||
"DecodePOP37GroupBranchMMR6",
|
||||
"DecodePOP65GroupBranchMMR6",
|
||||
"DecodePOP75GroupBranchMMR6",
|
||||
"DecodeBlezlGroupBranch",
|
||||
"DecodeBgtzlGroupBranch",
|
||||
"DecodeBgtzGroupBranch",
|
||||
"DecodeBlezGroupBranch",
|
||||
"DecodeBgtzGroupBranchMMR6",
|
||||
"DecodeBlezGroupBranchMMR6",
|
||||
"DecodeDINS",
|
||||
"DecodeDEXT",
|
||||
"DecodeCRC",
|
||||
"isReg"
|
||||
],
|
||||
"manually_edited_files": []
|
||||
},
|
||||
"SystemZ": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/Disassembler/SystemZDisassembler.cpp",
|
||||
"out": "SystemZDisassembler.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZInstPrinter.cpp",
|
||||
"out": "SystemZInstPrinter.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZInstPrinter.h",
|
||||
"out": "SystemZInstPrinter.h"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp",
|
||||
"out": "SystemZMCTargetDesc.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.h",
|
||||
"out": "SystemZMCTargetDesc.h"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/SystemZGenDisassemblerTables.inc",
|
||||
"{CPP_INC_OUT_DIR}/SystemZGenAsmWriter.inc",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/SystemZ/Disassembler/SystemZDisassembler.cpp",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZInstPrinter.cpp"
|
||||
],
|
||||
"templates_with_arg_deduction": [],
|
||||
"manually_edited_files": []
|
||||
},
|
||||
"Xtensa": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp",
|
||||
"out": "XtensaDisassembler.c"
|
||||
},
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp",
|
||||
"out": "XtensaInstPrinter.c"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/XtensaGenDisassemblerTables.inc",
|
||||
"{CPP_INC_OUT_DIR}/XtensaGenAsmWriter.inc"
|
||||
],
|
||||
"templates_with_arg_deduction": [],
|
||||
"manually_edited_files": []
|
||||
},
|
||||
"TriCore": {
|
||||
"files_to_translate": [],
|
||||
"files_for_template_search": [],
|
||||
"templates_with_arg_deduction": [],
|
||||
"manually_edited_files": []
|
||||
},
|
||||
"ARC": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARC/Disassembler/ARCDisassembler.cpp",
|
||||
"out": "ARCDisassembler.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARC/MCTargetDesc/ARCInstPrinter.cpp",
|
||||
"out": "ARCInstPrinter.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARC/MCTargetDesc/ARCInstPrinter.h",
|
||||
"out": "ARCInstPrinter.h"
|
||||
},
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/ARC/MCTargetDesc/ARCInfo.h",
|
||||
"out": "ARCInfo.h"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/ARCGenDisassemblerTables.inc",
|
||||
"{CPP_INC_OUT_DIR}/ARCGenAsmWriter.inc",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/ARC/Disassembler/ARCDisassembler.cpp",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/ARC/MCTargetDesc/ARCInstPrinter.cpp"
|
||||
],
|
||||
"templates_with_arg_deduction": [],
|
||||
"manually_edited_files": []
|
||||
},
|
||||
"Sparc": {
|
||||
"files_to_translate": [
|
||||
{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Sparc/Disassembler/SparcDisassembler.cpp",
|
||||
"out": "SparcDisassembler.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Sparc/MCTargetDesc/SparcInstPrinter.cpp",
|
||||
"out": "SparcInstPrinter.c"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Sparc/MCTargetDesc/SparcInstPrinter.h",
|
||||
"out": "SparcInstPrinter.h"
|
||||
},{
|
||||
"in": "{LLVM_ROOT}/llvm/lib/Target/Sparc/MCTargetDesc/SparcMCTargetDesc.h",
|
||||
"out": "SparcMCTargetDesc.h"
|
||||
}
|
||||
],
|
||||
"files_for_template_search": [
|
||||
"{CPP_INC_OUT_DIR}/SparcGenDisassemblerTables.inc",
|
||||
"{LLVM_ROOT}/llvm/lib/Target/Sparc/Disassembler/SparcDisassembler.cpp"
|
||||
],
|
||||
"templates_with_arg_deduction": [],
|
||||
"manually_edited_files": []
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import (
|
||||
get_MCInst_var_name,
|
||||
get_text,
|
||||
template_param_list_to_dict,
|
||||
)
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class AddCSDetail(Patch):
|
||||
"""
|
||||
Adds calls to `<ARCH>_add_cs_detail_<n>()` for printOperand functions in <ARCH>InstPrinter.c
|
||||
|
||||
Patch void printThumbLdrLabelOperand(MCInst *MI, unsigned OpNo, SStream *O) {...}
|
||||
to void printThumbLdrLabelOperand(MCInst *MI, unsigned OpNo, SStream *O) {
|
||||
<ARCH>_add_cs_detail_<n>(MI, ARM_OP_GROUP_ThumbLdrLabelOperand, ...);
|
||||
...
|
||||
}
|
||||
"""
|
||||
|
||||
# TODO Simply checking for the passed types would be so much nicer.
|
||||
# Parameter lists of printOperand() functions we need to add `<ARCH>_add_cs_detail_<n>()` to.
|
||||
# Spaces are removed, so we only need to check the letters.
|
||||
valid_param_lists = [
|
||||
b"(MCInst*MI,unsignedOpNum,SStream*O)", # Default printOperand parameters.
|
||||
b"(MCInst*MI,unsignedOpNo,SStream*O)", # ARM - printComplexRotationOp / PPC default
|
||||
b"(MCInst*MI,intopNum,SStream *O)", # Mips - printMemOperandEA and others
|
||||
b"(SStream*O,ARM_AM::ShiftOpcShOpc,unsignedShImm,boolgetUseMarkup())", # ARM - printRegImmShift
|
||||
b"(MCInst*MI,unsignedOpNo,SStream*O,constchar*Modifier)", # PPC - printPredicateOperand
|
||||
b"(MCInst*MI,uint64_tAddress,unsignedOpNo,SStream*O)", # PPC - printBranchOperand
|
||||
b"(MCInst*MI,intOpNum,SStream*O)", # SystemZ
|
||||
b"(MCInst*MI,intOpNum,SStream*O)", # Xtensa printOperand parameters.
|
||||
b"(MCInst*MI,intOpNum,SStream*OS)", # Xtensa printOperand parameters.
|
||||
b"(MCInst*MI,intopNum,SStream*O)", # Sparc printOperand parameters.
|
||||
]
|
||||
|
||||
def __init__(self, priority: int, arch: str):
|
||||
super().__init__(priority)
|
||||
self.arch = arch
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(function_definition"
|
||||
" (_)+"
|
||||
" (function_declarator"
|
||||
' ((identifier) @fcn_id (#match? @fcn_id "print.*"))'
|
||||
" ((parameter_list) @p_list)"
|
||||
" )"
|
||||
" (compound_statement) @comp_stmt"
|
||||
") @print_op"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "print_op"
|
||||
|
||||
def get_patch(
|
||||
self, captures: list[tuple[Node, str]], src: bytes, **kwargs
|
||||
) -> bytes:
|
||||
fcn_def: Node = captures[0][0]
|
||||
params = captures[2][0]
|
||||
params = get_text(src, params.start_byte, params.end_byte)
|
||||
if re.sub(b"[\n \t]", b"", params) not in self.valid_param_lists:
|
||||
return get_text(src, fcn_def.start_byte, fcn_def.end_byte)
|
||||
|
||||
fcn_id = captures[1][0]
|
||||
fcn_id = get_text(src, fcn_id.start_byte, fcn_id.end_byte)
|
||||
|
||||
add_cs_detail = self.get_add_cs_detail(src, fcn_def, fcn_id, params)
|
||||
|
||||
comp = captures[3][0]
|
||||
comp = get_text(src, comp.start_byte, comp.end_byte)
|
||||
return (
|
||||
b"static inline void "
|
||||
+ fcn_id
|
||||
+ params
|
||||
+ b"{ "
|
||||
+ add_cs_detail
|
||||
+ comp.strip(b"{")
|
||||
)
|
||||
|
||||
def get_add_cs_detail(
|
||||
self, src: bytes, fcn_def: Node, fcn_id: bytes, params: bytes
|
||||
) -> bytes:
|
||||
op_group_enum = (
|
||||
self.arch.encode("utf8") + b"_OP_GROUP_" + fcn_id[5:]
|
||||
) # Remove "print" from function id
|
||||
|
||||
is_template = fcn_def.prev_sibling.type == "template_parameter_list"
|
||||
if b"OpNum" in params:
|
||||
op_num_var_name = b"OpNum"
|
||||
elif b"OpNo" in params:
|
||||
op_num_var_name = b"OpNo"
|
||||
elif b"opNum" in params:
|
||||
op_num_var_name = b"opNum"
|
||||
else:
|
||||
raise ValueError("OpNum parameter could not be identified.")
|
||||
|
||||
if not is_template and op_num_var_name in params:
|
||||
# Standard printOperand() parameters
|
||||
mcinst_var = get_MCInst_var_name(src, fcn_def)
|
||||
return (
|
||||
f"{self.arch}_add_cs_detail_0(".encode()
|
||||
+ mcinst_var
|
||||
+ b", "
|
||||
+ op_group_enum
|
||||
+ b", "
|
||||
+ op_num_var_name
|
||||
+ b");"
|
||||
)
|
||||
elif op_group_enum == b"ARM_OP_GROUP_RegImmShift":
|
||||
return (
|
||||
f"{self.arch}_add_cs_detail_1(MI, ".encode()
|
||||
+ op_group_enum
|
||||
+ b", ShOpc, ShImm);"
|
||||
)
|
||||
elif is_template and op_num_var_name in params:
|
||||
mcinst_var = get_MCInst_var_name(src, fcn_def)
|
||||
templ_p = template_param_list_to_dict(fcn_def.prev_sibling)
|
||||
cs_args = b""
|
||||
for tp in templ_p:
|
||||
op_group_enum = (
|
||||
b"CONCAT(" + op_group_enum + b", " + tp["identifier"] + b")"
|
||||
)
|
||||
cs_args += b", " + tp["identifier"]
|
||||
return (
|
||||
f"{self.arch}_add_cs_detail_{len(templ_p)}(".encode()
|
||||
+ mcinst_var
|
||||
+ b", "
|
||||
+ op_group_enum
|
||||
+ b", "
|
||||
+ op_num_var_name
|
||||
+ b" "
|
||||
+ cs_args
|
||||
+ b");"
|
||||
)
|
||||
log.fatal(f"Case {op_group_enum} not handled.")
|
||||
exit(1)
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class AddOperand(Patch):
|
||||
"""
|
||||
Patch MI.addOperand(...)
|
||||
to MCInst_addOperand(MI, ...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression "
|
||||
" (field_expression"
|
||||
" ((identifier) @inst_var)"
|
||||
' ((field_identifier) @field_id_op (#eq? @field_id_op "addOperand"))'
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @add_operand"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "add_operand"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get instruction variable name (MI, Inst)
|
||||
inst_var: Node = captures[1][0]
|
||||
# Arguments of getOperand(...)
|
||||
get_op_args = captures[3][0]
|
||||
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
|
||||
args = get_text(src, get_op_args.start_byte, get_op_args.end_byte)
|
||||
return b"MCInst_addOperand2(" + inst + b", " + args + b")"
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
from autosync.cpptranslator.patches.Helper import get_text_from_node
|
||||
|
||||
|
||||
class Assert(Patch):
|
||||
"""
|
||||
Patch replace `assert`|`report_fatal_error` with `CS_ASSERT`
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(expression_statement"
|
||||
" (call_expression"
|
||||
' ((identifier) @id (#match? @id "assert|report_fatal_error"))'
|
||||
" ((argument_list) @arg_list)"
|
||||
" )"
|
||||
") @assert"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "assert"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
args = get_text_from_node(src, captures[2][0])
|
||||
return b"CS_ASSERT" + args + b";"
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# Copyright © 2024 Dmitry Sibitsev <sibirtsevdl@gmail.com>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class BadConditionCode(Patch):
|
||||
"""
|
||||
Patch return BadConditionCode
|
||||
to CS_ASSERT(0 && "Unknown condition code passed")
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(return_statement "
|
||||
" (call_expression "
|
||||
' (identifier) @fcn_name (#eq? @fcn_name "BadConditionCode")'
|
||||
" (argument_list)"
|
||||
" )"
|
||||
") @bad_condition_code"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "bad_condition_code"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b'CS_ASSERT(0 && "Unknown condition code passed");'
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class BitCastStdArray(Patch):
|
||||
"""
|
||||
Patch auto S = bit_cast<std::array<int32_t, 2>>(Imm);
|
||||
to union {
|
||||
typeof(Imm) In;
|
||||
int32_t Out[2];
|
||||
} U_S;
|
||||
U_S.In = Imm;
|
||||
int32_t *S = U_S.Out;
|
||||
|
||||
MSVC doesn't support typeof so it has to be resolved manually.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(declaration"
|
||||
" (placeholder_type_specifier)"
|
||||
" (init_declarator"
|
||||
" (identifier) @arr_name"
|
||||
" (call_expression"
|
||||
" (template_function"
|
||||
' ((identifier) @tfid (#eq @tfid "bit_cast"))'
|
||||
" (template_argument_list"
|
||||
' ((type_descriptor) @td (#match @td "std::array<.*>"))'
|
||||
" )"
|
||||
" )"
|
||||
" (argument_list) @cast_target"
|
||||
" )"
|
||||
" )"
|
||||
") @array_bit_cast"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "array_bit_cast"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
c1 = captures[1][0]
|
||||
c4 = captures[4][0]
|
||||
arr_name: bytes = get_text(src, c1.start_byte, c1.end_byte)
|
||||
array_type: Node = captures[3][0]
|
||||
cast_target: bytes = get_text(src, c4.start_byte, c4.end_byte).strip(b"()")
|
||||
named_child = array_type.named_children[0].named_children[1].named_children[1]
|
||||
array_templ_args: bytes = get_text(
|
||||
src, named_child.start_byte, named_child.end_byte
|
||||
).strip(b"<>")
|
||||
arr_type = array_templ_args.split(b",")[0]
|
||||
arr_len = array_templ_args.split(b",")[1]
|
||||
return (
|
||||
b"union {\n"
|
||||
+ b" typeof("
|
||||
+ cast_target
|
||||
+ b") In;\n"
|
||||
+ b" "
|
||||
+ arr_type
|
||||
+ b" Out["
|
||||
+ arr_len
|
||||
+ b"];\n"
|
||||
+ b"} U_"
|
||||
+ arr_name
|
||||
+ b";\n"
|
||||
+ b"U_"
|
||||
+ arr_name
|
||||
+ b".In = "
|
||||
+ cast_target
|
||||
+ b";\n"
|
||||
+ arr_type
|
||||
+ b" *"
|
||||
+ arr_name
|
||||
+ b" = U_"
|
||||
+ arr_name
|
||||
+ b".Out;"
|
||||
)
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class CheckDecoderStatus(Patch):
|
||||
"""
|
||||
Patch "Check(S, ..."
|
||||
to "Check(&S, ..."
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
' ((identifier) @fcn_name (#eq? @fcn_name "Check"))'
|
||||
" ((argument_list) @arg_list)"
|
||||
") @check_call"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "check_call"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
call_expr: Node = captures[0][0]
|
||||
first_arg: Node = captures[2][0].named_children[0]
|
||||
|
||||
call_text = get_text(src, call_expr.start_byte, call_expr.end_byte)
|
||||
first_arg_text = get_text(src, first_arg.start_byte, first_arg.end_byte)
|
||||
|
||||
return call_text.replace(first_arg_text + b",", b"&" + first_arg_text + b",")
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class ClassConstructorDef(Patch):
|
||||
"""
|
||||
Removes Class constructor definitions with a field initializer list.
|
||||
Removes Class::Class(...) : ... {}
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(function_definition"
|
||||
" (function_declarator)"
|
||||
" (field_initializer_list)"
|
||||
" (compound_statement)"
|
||||
") @class_constructor"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "class_constructor"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b""
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class ClassesDef(Patch):
|
||||
"""
|
||||
Patch Class definitions
|
||||
to Removes class but extracts method declarations.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return "(class_specifier (_)* ((field_declaration_list) @decl_list)*) @class_specifier"
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "class_specifier"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
if len(captures) < 2:
|
||||
# Forward class definition. Ignore it.
|
||||
return b""
|
||||
field_decl_list = captures[1][0]
|
||||
functions = list()
|
||||
for field_decl in field_decl_list.named_children:
|
||||
if (
|
||||
field_decl.type in "field_declaration"
|
||||
and (
|
||||
"function_declarator" in [t.type for t in field_decl.named_children]
|
||||
)
|
||||
) or field_decl.type == "template_declaration":
|
||||
# Keep comments
|
||||
sibling = field_decl.prev_named_sibling
|
||||
while sibling.type == "comment":
|
||||
functions.append(sibling)
|
||||
sibling = sibling.prev_named_sibling
|
||||
functions.append(field_decl)
|
||||
fcn_decl_text = b""
|
||||
for f in functions:
|
||||
fcn_decl_text += get_text(src, f.start_byte, f.end_byte) + b"\n"
|
||||
return fcn_decl_text
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class ConstMCInstParameter(Patch):
|
||||
"""
|
||||
Patch const MCInst *MI
|
||||
to MCInst *MI
|
||||
|
||||
Removes the const qualifier from MCInst parameters because functions like MCInst_getOperand() ignore them anyway.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(parameter_declaration"
|
||||
" ((type_qualifier) @type_qualifier)"
|
||||
' ((type_identifier) @type_id (#eq? @type_id "MCInst"))'
|
||||
" (pointer_declarator) @ptr_decl"
|
||||
") @mcinst_param"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "mcinst_param"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
inst = captures[3][0]
|
||||
inst = get_text(src, inst.start_byte, inst.end_byte)
|
||||
return b"MCInst " + inst
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class ConstMCOperand(Patch):
|
||||
"""
|
||||
Patch const MCOperand ...
|
||||
to MCOperand
|
||||
|
||||
Removes the const qualifier from MCOperand declarations. They are ignored by the following functions.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(declaration"
|
||||
" (type_qualifier)"
|
||||
' ((type_identifier) @tid (#eq? @tid "MCOperand"))'
|
||||
" (init_declarator) @init_decl"
|
||||
") @const_mcoperand"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "const_mcoperand"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
init_decl = captures[2][0]
|
||||
init_decl = get_text(src, init_decl.start_byte, init_decl.end_byte)
|
||||
return b"MCOperand " + init_decl + b";"
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class CppInitCast(Patch):
|
||||
"""
|
||||
Patch int(...)
|
||||
to (int)(...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
" (primitive_type) @cast_type"
|
||||
" (argument_list) @cast_target"
|
||||
") @cast"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "cast"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
cast_type: Node = captures[1][0]
|
||||
cast_target: Node = captures[2][0]
|
||||
|
||||
ctype = get_text(src, cast_type.start_byte, cast_type.end_byte)
|
||||
ctarget = get_text(src, cast_target.start_byte, cast_target.end_byte)
|
||||
return b"((" + ctype + b")" + ctarget + b")"
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class CreateOperand0(Patch):
|
||||
"""
|
||||
Patch Inst.addOperand(MCOperand::createReg(...));
|
||||
to MCOperand_CreateReg0(...)
|
||||
(and equivalent for CreateImm)
|
||||
|
||||
This is the `0` variant of the CS `CreateReg`/`CreateImm` functions. It is used if the
|
||||
operand is added via `addOperand()`.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression "
|
||||
" (field_expression ((identifier) @inst_var"
|
||||
' (field_identifier) @field_id (#eq? @field_id "addOperand")))'
|
||||
" (argument_list (call_expression "
|
||||
" (qualified_identifier ((_) (identifier) @create_fcn))"
|
||||
" (argument_list) @arg_list"
|
||||
" )"
|
||||
" )"
|
||||
") @create_operand0"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "create_operand0"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get name of instruction variable
|
||||
inst_var: Node = captures[1][0]
|
||||
# Get 'create[Reg/Imm]'
|
||||
op_create_fcn: Node = captures[3][0]
|
||||
# Get arg list
|
||||
op_create_args: Node = captures[4][0]
|
||||
|
||||
# Capstone spells the function with capital letter 'C' for whatever reason.
|
||||
fcn = re.sub(
|
||||
b"create",
|
||||
b"Create",
|
||||
get_text(src, op_create_fcn.start_byte, op_create_fcn.end_byte),
|
||||
)
|
||||
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
|
||||
args = get_text(src, op_create_args.start_byte, op_create_args.end_byte)
|
||||
if args[0] == b"(" and args[-1] == b")":
|
||||
args = args
|
||||
return b"MCOperand_" + fcn + b"0(" + inst + b", " + args + b")"
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class CreateOperand1(Patch):
|
||||
"""
|
||||
Patch MI.insert(..., MCOperand::createReg(...));
|
||||
to MCInst_insert0(..., MCOperand_createReg1(...));
|
||||
(and equivalent for CreateImm)
|
||||
|
||||
This is the `1` variant of the CS `CreateReg`/`CreateImm` functions. It is used if the
|
||||
operand is added via `insert()`.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression "
|
||||
" (field_expression ((identifier) @MC_id"
|
||||
' ((field_identifier) @field_id (#match? @field_id "insert")))'
|
||||
" )"
|
||||
" (argument_list"
|
||||
" ((identifier) @inst_var"
|
||||
" (call_expression"
|
||||
" (qualified_identifier ((_) (identifier) @create_fcn))"
|
||||
" (argument_list) @arg_list)"
|
||||
" )"
|
||||
" )"
|
||||
") @create_operand1"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "create_operand1"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get instruction variable
|
||||
inst_var: Node = captures[1][0]
|
||||
# Get argument of .insert() call
|
||||
insert_arg: Node = captures[3][0]
|
||||
# Get 'create[Reg/Imm]'
|
||||
op_create_fcn: Node = captures[4][0]
|
||||
# CreateReg/Imm args
|
||||
op_create_args: Node = captures[5][0]
|
||||
|
||||
insert_arg_t = get_text(src, insert_arg.start_byte, insert_arg.end_byte)
|
||||
# Capstone spells the function with capital letter 'C' for whatever reason.
|
||||
fcn = re.sub(
|
||||
b"create",
|
||||
b"Create",
|
||||
get_text(src, op_create_fcn.start_byte, op_create_fcn.end_byte),
|
||||
)
|
||||
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
|
||||
args = get_text(src, op_create_args.start_byte, op_create_args.end_byte)
|
||||
return (
|
||||
b"MCInst_insert0("
|
||||
+ inst
|
||||
+ b", "
|
||||
+ insert_arg_t
|
||||
+ b", "
|
||||
+ b"MCOperand_"
|
||||
+ fcn
|
||||
+ b"1("
|
||||
+ inst
|
||||
+ b", "
|
||||
+ args
|
||||
+ b"))"
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class Data(Patch):
|
||||
"""
|
||||
Patch Bytes.data()
|
||||
to Bytes
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression "
|
||||
" (field_expression"
|
||||
" ((identifier) @data_var)"
|
||||
' ((field_identifier) @field_id_op (#eq? @field_id_op "data"))'
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @data"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "data"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get operand variable name (Bytes, ArrayRef)
|
||||
op_var: Node = captures[1][0]
|
||||
op = get_text(src, op_var.start_byte, op_var.end_byte)
|
||||
return op
|
||||
external/capstone/suite/auto-sync/src/autosync/cpptranslator/patches/DeclarationInConditionClause.py
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class DeclarationInConditionalClause(Patch):
|
||||
"""
|
||||
Patch if (DECLARATION) ...
|
||||
to DECLARATION
|
||||
if (VAR) ...
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(if_statement"
|
||||
" (condition_clause"
|
||||
" (declaration"
|
||||
" (_)"
|
||||
" ((identifier) @id)"
|
||||
" (_)"
|
||||
" ) @decl"
|
||||
" )"
|
||||
" (_) @if_body"
|
||||
") @condition_clause"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "condition_clause"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
cond = get_capture_node(captures, "condition_clause")
|
||||
for nc in cond.named_children:
|
||||
if nc.type == "if_statement":
|
||||
# Skip if statements with else if
|
||||
return get_text(src, cond.start_byte, cond.end_byte)
|
||||
declaration = get_capture_node(captures, "decl")
|
||||
identifier = get_capture_node(captures, "id")
|
||||
if_body = get_capture_node(captures, "if_body")
|
||||
identifier = get_text(src, identifier.start_byte, identifier.end_byte)
|
||||
declaration = get_text(src, declaration.start_byte, declaration.end_byte)
|
||||
if_body = get_text(src, if_body.start_byte, if_body.end_byte)
|
||||
res = declaration + b";\nif (" + identifier + b")\n" + if_body
|
||||
return res
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class DecodeInstruction(Patch):
|
||||
"""
|
||||
Patch decodeInstruction(..., this, STI)
|
||||
to decodeInstruction_<instr_width>(..., NULL)
|
||||
|
||||
It also removes the arguments `this, STI`.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression ("
|
||||
' (identifier) @fcn_name (#eq? @fcn_name "decodeInstruction")'
|
||||
" ((argument_list) @arg_list)"
|
||||
")) @decode_instr"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "decode_instr"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
arg_list = captures[2][0]
|
||||
args_text = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()")
|
||||
|
||||
table, mi_inst, opcode_var, address, this, sti = args_text.split(b",")
|
||||
is_32bit = (
|
||||
table[-2:].decode("utf8") == "32" or opcode_var[-2:].decode("utf8") == "32"
|
||||
)
|
||||
is_16bit = (
|
||||
table[-2:].decode("utf8") == "16" or opcode_var[-2:].decode("utf8") == "16"
|
||||
)
|
||||
args = (
|
||||
table + b", " + mi_inst + b", " + opcode_var + b", " + address + b", NULL"
|
||||
)
|
||||
|
||||
if is_16bit and not is_32bit:
|
||||
return b"decodeInstruction_2(" + args + b")"
|
||||
elif is_32bit and not is_16bit:
|
||||
return b"decodeInstruction_4(" + args + b")"
|
||||
else:
|
||||
# Cannot determine instruction width easily. Only update the calls arguments.
|
||||
return b"decodeInstruction(" + args + b")"
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class DecoderCast(Patch):
|
||||
"""
|
||||
Patch Removes casts like `const MCDisassembler *Dis = static_cast<const MCDisassembler*>(Decoder);`
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(declaration"
|
||||
" (type_qualifier)*"
|
||||
' ((type_identifier) @tid (#eq? @tid "MCDisassembler"))'
|
||||
" (init_declarator"
|
||||
" (pointer_declarator)"
|
||||
" (call_expression"
|
||||
" (template_function)" # static_cast<const MCDisassembler>
|
||||
" (argument_list"
|
||||
' ((identifier) @id (#eq? @id "Decoder"))'
|
||||
" )"
|
||||
" )"
|
||||
" )"
|
||||
") @decoder_cast"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "decoder_cast"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b""
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class DecoderParameter(Patch):
|
||||
"""
|
||||
Patch const MCDisassembler *Decoder
|
||||
to const void *Decoder
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(parameter_declaration"
|
||||
" ((type_qualifier) @type_qualifier)"
|
||||
' ((type_identifier) @type_id (#eq? @type_id "MCDisassembler"))'
|
||||
" (pointer_declarator) @ptr_decl"
|
||||
") @decoder_param"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "decoder_param"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b"const void *Decoder"
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class FallThrough(Patch):
|
||||
"""
|
||||
Patch [[fallthrough]]
|
||||
to // fall through
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return '(attributed_statement) @attr (#match? @attr "fallthrough")'
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "attr"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b"// fall through"
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class FeatureBits(Patch):
|
||||
"""
|
||||
Patch featureBits[FLAG]
|
||||
to ARCH_getFeatureBits(Inst->csh->mode, FLAG)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int, arch: bytes):
|
||||
self.arch = arch
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
# Search for featureBits usage.
|
||||
return (
|
||||
"(subscript_expression "
|
||||
' ((identifier) @id (#match? @id "[fF]eatureBits"))'
|
||||
" (subscript_argument_list ((qualified_identifier) @qid))"
|
||||
") @feature_bits"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "feature_bits"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get flag name of feature bit.
|
||||
qualified_id: Node = captures[2][0]
|
||||
flag = get_text(src, qualified_id.start_byte, qualified_id.end_byte)
|
||||
mcinst_var_name = get_MCInst_var_name(src, qualified_id)
|
||||
return (
|
||||
self.arch
|
||||
+ b"_getFeatureBits("
|
||||
+ mcinst_var_name
|
||||
+ b"->csh->mode, "
|
||||
+ flag
|
||||
+ b")"
|
||||
)
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class FeatureBitsDecl(Patch):
|
||||
"""
|
||||
Patch ... featureBits = ...
|
||||
to REMOVED
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
# Search for featureBits declarations.
|
||||
return (
|
||||
"(declaration (init_declarator (reference_declarator "
|
||||
'((identifier) @id (#match? @id "[fF]eatureBits"))))) @feature_bits_decl'
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "feature_bits_decl"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Remove declaration
|
||||
return b""
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_function_params_of_node, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class FieldFromInstr(Patch):
|
||||
"""
|
||||
Patch fieldFromInstruction(...)
|
||||
to fieldFromInstruction_<instr_width>(...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
# Search for fieldFromInstruction() calls.
|
||||
return (
|
||||
"(call_expression"
|
||||
' ((identifier) @fcn_name (#eq? @fcn_name "fieldFromInstruction"))'
|
||||
" (argument_list ((identifier) @first_arg) (_) (_))"
|
||||
") @field_from_instr"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "field_from_instr"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
ffi_call: Node = captures[0][0]
|
||||
ffi_first_arg: Node = captures[2][0]
|
||||
param_list_caller = get_function_params_of_node(ffi_call)
|
||||
ffi_first_arg_text = get_text(
|
||||
src, ffi_first_arg.start_byte, ffi_first_arg.end_byte
|
||||
).decode("utf8")
|
||||
|
||||
# Determine width of instruction by the variable name.
|
||||
if ffi_first_arg_text[-2:] == "32":
|
||||
inst_width = b"4"
|
||||
elif ffi_first_arg_text[-2:] == "16":
|
||||
inst_width = b"2"
|
||||
else:
|
||||
# Get the Val/Inst parameter.
|
||||
# Its type determines the instruction width.
|
||||
if len(param_list_caller.named_children) == 1:
|
||||
# If function just return fieldFromInstruction(...)
|
||||
inst_param: Node = param_list_caller.named_children[0]
|
||||
else:
|
||||
inst_param = param_list_caller.named_children[1]
|
||||
inst_param_text = get_text(src, inst_param.start_byte, inst_param.end_byte)
|
||||
|
||||
# Search for the 'Inst' parameter and determine its type
|
||||
# and with it the width of the instruction.
|
||||
inst_type = inst_param_text.split(b" ")[0]
|
||||
if inst_type:
|
||||
if inst_type in [b"uint64_t"]:
|
||||
inst_width = b"8"
|
||||
elif inst_type in [b"unsigned", b"uint32_t"]:
|
||||
inst_width = b"4"
|
||||
elif inst_type in [b"uint16_t"]:
|
||||
inst_width = b"2"
|
||||
elif inst_type in [b"InsnType"]:
|
||||
# Case means the decode function inherits the type from
|
||||
# a template argument InsnType. The InsnType template argument
|
||||
# is the type of integer holding the instruction bytes.
|
||||
# This type is defined in ARCHDisassembler on calling the right macro.
|
||||
# Hence, we do not know at this point of patching which type it might be.
|
||||
# It needs to call fieldOfInstruction_X() which detects dynamically which
|
||||
# integer type might hold the bytes (e.g. a uint32_t or uint16_t).
|
||||
# You can check it manually in ARCHDisassembler.c, but the script can't.
|
||||
#
|
||||
# Here we just create a function with the postfix fieldFromInstruction_w (for width).
|
||||
# This function must be implemented by hand, and check MCInst for the actual bit width.
|
||||
# The bit width must be set in the ARCHDisassembler.c. Just add the code there by hand.
|
||||
# Then call fieldFromInstruction_4, fieldFromInstruction_2 appropriately.
|
||||
log.warning(
|
||||
"Variable fieldFromInstruction width detected.\n"
|
||||
"Please implement fieldFromInstruction_w() and call "
|
||||
"fieldFromInstruction_4, fieldFromInstruction_2 appropriately.\n"
|
||||
"In fieldFromInstruction_w() check MCInst for the actual bit width.\n"
|
||||
"The bit width must be set in the ARCHDisassembler.c. Just add the code there by hand."
|
||||
)
|
||||
inst_width = b"w"
|
||||
else:
|
||||
raise ValueError(f"Type {inst_type} not handled.")
|
||||
else:
|
||||
# Needs manual fix
|
||||
return get_text(src, ffi_call.start_byte, ffi_call.end_byte)
|
||||
return re.sub(
|
||||
rb"fieldFromInstruction",
|
||||
b"fieldFromInstruction_%s" % inst_width,
|
||||
get_text(src, ffi_call.start_byte, ffi_call.end_byte),
|
||||
)
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class GetNumOperands(Patch):
|
||||
"""
|
||||
Patch MI.getNumOperands()
|
||||
to MCInst_getNumOperands(MI)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression "
|
||||
" (field_expression"
|
||||
" ((identifier) @inst_var)"
|
||||
' ((field_identifier) @field_id_op (#eq? @field_id_op "getNumOperands"))'
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @get_num_operands"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "get_num_operands"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get instruction variable name: MI, Inst etc.
|
||||
inst_var: Node = captures[1][0]
|
||||
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
|
||||
return b"MCInst_getNumOperands(" + inst + b")"
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class GetOpcode(Patch):
|
||||
"""
|
||||
Patch Inst.getOpcode()
|
||||
to MCInst_getOpcode(Inst)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
" (field_expression ("
|
||||
" ((identifier) @inst_var)"
|
||||
' ((field_identifier) @field_id (#eq? @field_id "getOpcode")))'
|
||||
" )"
|
||||
" (argument_list) @arg_list"
|
||||
") @get_opcode"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "get_opcode"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Instruction variable
|
||||
inst_var: Node = captures[1][0]
|
||||
arg_list: Node = captures[3][0]
|
||||
|
||||
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
|
||||
args = get_text(src, arg_list.start_byte, arg_list.end_byte)
|
||||
if args != b"()":
|
||||
args = b", " + args
|
||||
else:
|
||||
args = b""
|
||||
return b"MCInst_getOpcode(" + inst + args + b")"
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class GetOperand(Patch):
|
||||
"""
|
||||
Patch MI.getOperand(...)
|
||||
to MCInst_getOperand(MI, ...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression "
|
||||
" (field_expression"
|
||||
" ((identifier) @inst_var)"
|
||||
' ((field_identifier) @field_id_op (#eq? @field_id_op "getOperand"))'
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @get_operand"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "get_operand"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get instruction variable name (MI, Inst)
|
||||
inst_var: Node = captures[1][0]
|
||||
# Arguments of getOperand(...)
|
||||
get_op_args = captures[3][0]
|
||||
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
|
||||
args = get_text(src, get_op_args.start_byte, get_op_args.end_byte)
|
||||
return b"MCInst_getOperand(" + inst + b", " + args + b")"
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class GetOperandRegImm(Patch):
|
||||
"""
|
||||
Patch OPERAND.getReg()
|
||||
to MCOperand_getReg(OPERAND)
|
||||
|
||||
Same for getImm()|getExpr
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression"
|
||||
" (field_expression"
|
||||
" ((_) @operand)"
|
||||
' ((field_identifier) @field_id (#match? @field_id "get(Reg|Imm|Expr)"))'
|
||||
" )"
|
||||
' ((argument_list) @arg_list (#eq? @arg_list "()"))'
|
||||
") @get_operand"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "get_operand"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# The operand
|
||||
operand: Node = get_capture_node(captures, "operand")
|
||||
# 'getReg()/getImm()\getExpr'
|
||||
get_reg_imm = get_capture_node(captures, "field_id")
|
||||
|
||||
fcn = get_text(src, get_reg_imm.start_byte, get_reg_imm.end_byte)
|
||||
op = get_text(src, operand.start_byte, operand.end_byte)
|
||||
return b"MCOperand_" + fcn + b"(" + op + b")"
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import (
|
||||
get_capture_node,
|
||||
get_MCInst_var_name,
|
||||
get_text,
|
||||
)
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class GetRegClass(Patch):
|
||||
"""
|
||||
Patch MRI.getRegClass(...)
|
||||
to MCRegisterInfo_getRegClass(Inst->MRI, ...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression"
|
||||
" (field_expression"
|
||||
" (_)"
|
||||
' ((field_identifier) @field_id (#eq? @field_id "getRegClass"))'
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @get_reg_class"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "get_reg_class"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
arg_list: Node = get_capture_node(captures, "arg_list")
|
||||
args = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()")
|
||||
mcinst_var = get_MCInst_var_name(
|
||||
src, get_capture_node(captures, "get_reg_class")
|
||||
)
|
||||
res = b"MCRegisterInfo_getRegClass(" + mcinst_var + b"->MRI, " + args + b")"
|
||||
return res
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class GetRegFromClass(Patch):
|
||||
"""
|
||||
Patch <ARCH>MCRegisterClasses[<ARCH>::FPR128RegClassID].getRegister(RegNo);
|
||||
to <ARCH>MCRegisterClasses[<ARCH>::FPR128RegClassID].RegsBegin[RegNo];
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression"
|
||||
" (field_expression"
|
||||
' ((_) @operand (#match? @operand ".+MCRegisterClasses.*"))'
|
||||
' ((field_identifier) @field_id (#eq? @field_id "getRegister"))'
|
||||
" )"
|
||||
" (argument_list) @arg_list"
|
||||
") @get_reg_from_class"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "get_reg_from_class"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Table
|
||||
table: Node = get_capture_node(captures, "operand")
|
||||
# args
|
||||
getter_args = get_capture_node(captures, "arg_list")
|
||||
|
||||
tbl = get_text(src, table.start_byte, table.end_byte)
|
||||
args = get_text(src, getter_args.start_byte, getter_args.end_byte)
|
||||
|
||||
res = tbl + b".RegsBegin" + args.replace(b"(", b"[").replace(b")", b"]")
|
||||
return res
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class GetSubReg(Patch):
|
||||
"""
|
||||
Patch MRI.getSubReg(...);
|
||||
to MCRegisterInfo_getSubReg(MI->MRI, ...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
" (field_expression ("
|
||||
" (identifier)"
|
||||
' ((field_identifier) @field_id (#eq? @field_id "getSubReg")))'
|
||||
" )"
|
||||
" (argument_list) @arg_list"
|
||||
") @get_sub_reg"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "get_sub_reg"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get arg list
|
||||
op_create_args: Node = captures[2][0]
|
||||
|
||||
args = get_text(src, op_create_args.start_byte, op_create_args.end_byte).strip(
|
||||
b"()"
|
||||
)
|
||||
mcinst_var_name = get_MCInst_var_name(src, op_create_args)
|
||||
return b"MCRegisterInfo_getSubReg(" + mcinst_var_name + b"->MRI, " + args + b")"
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.Helper import fail_exit
|
||||
|
||||
|
||||
def get_function_params_of_node(n: Node) -> Node:
|
||||
"""
|
||||
Returns for a given node the parameters of the function this node is a children from.
|
||||
Or None if the node is not part of a function definition.
|
||||
"""
|
||||
fcn_def: Node = n
|
||||
while fcn_def.type != "function_definition":
|
||||
if fcn_def.parent == None:
|
||||
# root node reached
|
||||
return None
|
||||
fcn_def = fcn_def.parent
|
||||
|
||||
# Get parameter list of the function definition
|
||||
param_list: Node = None
|
||||
for child in fcn_def.children:
|
||||
if child.type == "function_declarator":
|
||||
param_list = child.children[1]
|
||||
break
|
||||
if not param_list:
|
||||
log.warning(f"Could not find the functions parameter list for {n.text}")
|
||||
return param_list
|
||||
|
||||
|
||||
def get_MCInst_var_name(src: bytes, n: Node) -> bytes:
|
||||
"""Searches for the name of the parameter of type MCInst and returns it."""
|
||||
params = get_function_params_of_node(n)
|
||||
mcinst_var_name = b""
|
||||
|
||||
if params:
|
||||
for p in params.named_children:
|
||||
p_text = get_text(src, p.start_byte, p.end_byte)
|
||||
if b"MCInst" not in p_text:
|
||||
continue
|
||||
mcinst_var_name = p_text.split((b"&" if b"&" in p_text else b"*"))[1]
|
||||
break
|
||||
if mcinst_var_name == b"":
|
||||
log.debug("Could not find `MCInst` variable name. Defaulting to `Inst`.")
|
||||
mcinst_var_name = b"Inst"
|
||||
return mcinst_var_name
|
||||
|
||||
|
||||
def template_param_list_to_dict(param_list: Node) -> [dict]:
|
||||
if param_list.type != "template_parameter_list":
|
||||
log.fatal(
|
||||
f"Wrong node type '{param_list.type}'. Not 'template_parameter_list'."
|
||||
)
|
||||
exit(1)
|
||||
pl = list()
|
||||
for c in param_list.named_children:
|
||||
if c.type == "type_parameter_declaration":
|
||||
type_decl = {
|
||||
"prim_type": False,
|
||||
"type": "",
|
||||
"identifier": c.children[1].text,
|
||||
}
|
||||
pl.append(type_decl)
|
||||
else:
|
||||
pl.append(parameter_declaration_to_dict(c))
|
||||
return pl
|
||||
|
||||
|
||||
def parameter_declaration_to_dict(param_decl: Node) -> dict:
|
||||
if param_decl.type != "parameter_declaration":
|
||||
log.fatal(
|
||||
f"Wrong node type '{param_decl.type}'. Should be 'parameter_declaration'."
|
||||
)
|
||||
exit(1)
|
||||
return {
|
||||
"prim_type": param_decl.children[0].type == "primitive_type",
|
||||
"type": param_decl.children[0].text,
|
||||
"identifier": param_decl.children[1].text,
|
||||
}
|
||||
|
||||
|
||||
def get_text(src: bytes, start_byte: int, end_byte: int) -> bytes:
|
||||
"""Workaround for https://github.com/tree-sitter/py-tree-sitter/issues/122"""
|
||||
return src[start_byte:end_byte]
|
||||
|
||||
|
||||
def get_text_from_node(src: bytes, node: Node) -> bytes:
|
||||
return src[node.start_byte : node.end_byte]
|
||||
|
||||
|
||||
def namespace_enum(src: bytes, ns_id: bytes, enum: Node) -> bytes:
|
||||
"""
|
||||
Alters an enum in the way that it prepends the namespace id to every enum member.
|
||||
And defines it as a type.
|
||||
Example: naemspace_id = "ARM"
|
||||
enum { X } -> typedef enum { ARM_X } ARM_enum
|
||||
"""
|
||||
enumerator_list: Node = None
|
||||
type_id: Node = None
|
||||
primary_tid_set = False
|
||||
for c in enum.named_children:
|
||||
if c.type == "enumerator_list":
|
||||
enumerator_list = c
|
||||
elif c.type == "type_identifier" and not primary_tid_set:
|
||||
type_id = c
|
||||
primary_tid_set = True
|
||||
|
||||
if not enumerator_list and not type_id:
|
||||
log.fatal("Could not find enumerator_list or enum type_identifier.")
|
||||
exit(1)
|
||||
|
||||
tid = get_text(src, type_id.start_byte, type_id.end_byte) if type_id else None
|
||||
elist = get_text(src, enumerator_list.start_byte, enumerator_list.end_byte)
|
||||
for e in enumerator_list.named_children:
|
||||
if e.type == "enumerator":
|
||||
enum_entry_text = get_text(src, e.start_byte, e.end_byte)
|
||||
elist = elist.replace(enum_entry_text, ns_id + b"_" + enum_entry_text)
|
||||
|
||||
if tid:
|
||||
new_enum = b"typedef enum " + tid + b" " + elist + b"\n " + ns_id + b"_" + tid
|
||||
else:
|
||||
new_enum = b"enum " + b" " + elist + b"\n"
|
||||
return new_enum
|
||||
|
||||
|
||||
def namespace_fcn_def(src: bytes, ns_id: bytes, fcn_def: Node) -> bytes:
|
||||
fcn_id: Node = None
|
||||
for c in fcn_def.named_children:
|
||||
if c.type == "function_declarator":
|
||||
fcn_id = c.named_children[0]
|
||||
break
|
||||
elif c.named_children and c.named_children[0].type == "function_declarator":
|
||||
fcn_id = c.named_children[0].named_children[0]
|
||||
break
|
||||
if not fcn_id:
|
||||
# Not a function declaration
|
||||
return get_text(src, fcn_def.start_byte, fcn_def.end_byte)
|
||||
fcn_id_text = get_text(src, fcn_id.start_byte, fcn_id.end_byte)
|
||||
fcn_def_text = get_text(src, fcn_def.start_byte, fcn_def.end_byte)
|
||||
res = re.sub(fcn_id_text, ns_id + b"_" + fcn_id_text, fcn_def_text)
|
||||
return res
|
||||
|
||||
|
||||
def namespace_struct(src: bytes, ns_id: bytes, struct: Node) -> bytes:
|
||||
"""
|
||||
Defines a struct as a type.
|
||||
Example: naemspace_id = "ARM"
|
||||
struct id { X } -> typedef struct { } ARM_id
|
||||
"""
|
||||
type_id: Node = None
|
||||
field_list: Node = None
|
||||
for c in struct.named_children:
|
||||
if c.type == "type_identifier":
|
||||
type_id = c
|
||||
elif c.type == "base_class_clause":
|
||||
# Inheritances should be fixed manually.
|
||||
return get_text(src, struct.start_byte, struct.end_byte)
|
||||
elif c.type == "field_declaration_list":
|
||||
field_list = c
|
||||
|
||||
if not (type_id and field_list):
|
||||
log.fatal("Could not find struct type_identifier or field declaration list.")
|
||||
exit(1)
|
||||
|
||||
tid = get_text(src, type_id.start_byte, type_id.end_byte)
|
||||
fields = get_text(src, field_list.start_byte, field_list.end_byte)
|
||||
|
||||
typed_struct = (
|
||||
b"typedef struct " + tid + b" " + fields + b"\n " + ns_id + b"_" + tid
|
||||
)
|
||||
return typed_struct
|
||||
|
||||
|
||||
def parse_function_capture(
|
||||
capture: list[tuple[Node, str]], src: bytes
|
||||
) -> tuple[list[bytes], bytes, bytes, bytes, bytes, bytes]:
|
||||
"""
|
||||
Parses the capture of a (template) function definition or declaration and returns the byte strings
|
||||
for each node in the following order:
|
||||
|
||||
list[template_args], storage_class_identifiers, return_type_id, function_name, function_params, compound_stmt
|
||||
|
||||
If any of those is not present it returns an empty byte string for this position.
|
||||
"""
|
||||
temp_args = b""
|
||||
st_class_ids = b""
|
||||
ret_type = b""
|
||||
func_name = b""
|
||||
func_params = b""
|
||||
comp_stmt = b""
|
||||
for node, node_name in capture:
|
||||
t = get_text(src, node.start_byte, node.end_byte)
|
||||
match node.type:
|
||||
case "template_declaration":
|
||||
continue
|
||||
case "template_parameter_list":
|
||||
temp_args += t if not temp_args else b" " + t
|
||||
case "storage_class_specifier":
|
||||
st_class_ids += b" " + t
|
||||
case "type_identifier" | "primitive_type":
|
||||
ret_type += b" " + t
|
||||
case "identifier":
|
||||
func_name += t if not func_name else b" " + t
|
||||
case "parameter_list":
|
||||
func_params += t if not func_params else b" " + t
|
||||
case "compound_statement":
|
||||
comp_stmt += t if not comp_stmt else b" " + t
|
||||
case _:
|
||||
raise NotImplementedError(f"Node type {node.type} not handled.")
|
||||
|
||||
from autosync.cpptranslator.TemplateCollector import TemplateCollector
|
||||
|
||||
return (
|
||||
TemplateCollector.templ_params_to_list(temp_args),
|
||||
st_class_ids,
|
||||
ret_type,
|
||||
func_name,
|
||||
func_params,
|
||||
comp_stmt,
|
||||
)
|
||||
|
||||
|
||||
def get_capture_node(captures: [(Node, str)], name: str) -> Node:
|
||||
"""
|
||||
Returns the captured node with the given name.
|
||||
"""
|
||||
for c in captures:
|
||||
if c[1] == name:
|
||||
return c[0]
|
||||
fail_exit(f'Capture "{name}" is not in captures:\n{captures}')
|
||||
+487
@@ -0,0 +1,487 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class Includes(Patch):
|
||||
"""
|
||||
Patch LLVM includes
|
||||
to Capstone includes
|
||||
"""
|
||||
|
||||
include_count = dict()
|
||||
|
||||
def __init__(self, priority: int, arch: str):
|
||||
self.arch = arch
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return "(preproc_include) @preproc_include"
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "preproc_include"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
filename = kwargs["filename"]
|
||||
if filename not in self.include_count:
|
||||
self.include_count[filename] = 1
|
||||
else:
|
||||
self.include_count[filename] += 1
|
||||
|
||||
include_text = get_text(src, captures[0][0].start_byte, captures[0][0].end_byte)
|
||||
# Special cases, which appear somewhere in the code.
|
||||
if b"GenDisassemblerTables.inc" in include_text:
|
||||
return (
|
||||
b'#include "'
|
||||
+ bytes(self.arch, "utf8")
|
||||
+ b'GenDisassemblerTables.inc"\n\n'
|
||||
)
|
||||
elif b"GenAsmWriter.inc" in include_text:
|
||||
return b'#include "' + bytes(self.arch, "utf8") + b'GenAsmWriter.inc"\n\n'
|
||||
elif b"GenSystemOperands.inc" in include_text:
|
||||
return (
|
||||
b'#include "' + bytes(self.arch, "utf8") + b'GenSystemOperands.inc"\n\n'
|
||||
)
|
||||
|
||||
if self.include_count[filename] > 1:
|
||||
# Only the first include is replaced with all CS includes.
|
||||
return b""
|
||||
|
||||
# All includes which belong to the source files top.
|
||||
res = get_general_inc()
|
||||
match self.arch:
|
||||
case "ARM":
|
||||
return res + get_ARM_includes(filename) + get_general_macros()
|
||||
case "PPC":
|
||||
return res + get_PPC_includes(filename) + get_general_macros()
|
||||
case "AArch64":
|
||||
return res + get_AArch64_includes(filename) + get_general_macros()
|
||||
case "LoongArch":
|
||||
return res + get_LoongArch_includes(filename) + get_general_macros()
|
||||
case "Mips":
|
||||
return res + get_Mips_includes(filename) + get_general_macros()
|
||||
case "SystemZ":
|
||||
return res + get_SystemZ_includes(filename) + get_general_macros()
|
||||
case "Xtensa":
|
||||
return res + get_Xtensa_includes(filename) + get_general_macros()
|
||||
case "ARC":
|
||||
return res + get_ARC_includes(filename) + get_general_macros()
|
||||
case "Sparc":
|
||||
return res + get_sparc_includes(filename) + get_general_macros()
|
||||
case "TEST_ARCH":
|
||||
return res + b"test_output"
|
||||
case _:
|
||||
log.fatal(f"Includes of {self.arch} not handled.")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_general_inc() -> bytes:
|
||||
return (
|
||||
b"#include <stdio.h>\n"
|
||||
+ b"#include <string.h>\n"
|
||||
+ b"#include <stdlib.h>\n"
|
||||
+ b"#include <capstone/platform.h>\n\n"
|
||||
)
|
||||
|
||||
|
||||
def get_PPC_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "PPCDisassembler.cpp":
|
||||
return (
|
||||
b'#include "../../LEB128.h"\n'
|
||||
+ b'#include "../../MCDisassembler.h"\n'
|
||||
+ b'#include "../../MCFixedLenDisassembler.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstrDesc.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "../../SStream.h"\n'
|
||||
+ b'#include "../../utils.h"\n'
|
||||
+ b'#include "PPCLinkage.h"\n'
|
||||
+ b'#include "PPCMapping.h"\n'
|
||||
+ b'#include "PPCMCTargetDesc.h"\n'
|
||||
+ b'#include "PPCPredicates.h"\n\n'
|
||||
)
|
||||
case "PPCInstPrinter.cpp":
|
||||
return (
|
||||
b'#include "../../LEB128.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstrDesc.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "PPCInstrInfo.h"\n'
|
||||
+ b'#include "PPCInstPrinter.h"\n'
|
||||
+ b'#include "PPCLinkage.h"\n'
|
||||
+ b'#include "PPCMCTargetDesc.h"\n'
|
||||
+ b'#include "PPCMapping.h"\n'
|
||||
+ b'#include "PPCPredicates.h"\n\n'
|
||||
+ b'#include "PPCRegisterInfo.h"\n\n'
|
||||
)
|
||||
case "PPCInstPrinter.h":
|
||||
return (
|
||||
b'#include "../../LEB128.h"\n'
|
||||
+ b'#include "../../MCDisassembler.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstrDesc.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "../../SStream.h"\n'
|
||||
+ b'#include "PPCMCTargetDesc.h"\n\n'
|
||||
)
|
||||
case "PPCMCTargetDesc.h":
|
||||
return (
|
||||
b'#include "../../LEB128.h"\n'
|
||||
+ b'#include "../../MathExtras.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstrDesc.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
)
|
||||
log.fatal(f"No includes given for PPC source file: {filename}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_ARM_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "ARMDisassembler.cpp":
|
||||
return (
|
||||
b'#include "../../LEB128.h"\n'
|
||||
+ b'#include "../../MCDisassembler.h"\n'
|
||||
+ b'#include "../../MCFixedLenDisassembler.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstrDesc.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "../../MathExtras.h"\n'
|
||||
+ b'#include "../../cs_priv.h"\n'
|
||||
+ b'#include "../../utils.h"\n'
|
||||
+ b'#include "ARMAddressingModes.h"\n'
|
||||
+ b'#include "ARMBaseInfo.h"\n'
|
||||
+ b'#include "ARMDisassemblerExtension.h"\n'
|
||||
+ b'#include "ARMInstPrinter.h"\n'
|
||||
+ b'#include "ARMLinkage.h"\n'
|
||||
+ b'#include "ARMMapping.h"\n\n'
|
||||
+ b"#define GET_INSTRINFO_MC_DESC\n"
|
||||
+ b'#include "ARMGenInstrInfo.inc"\n\n'
|
||||
)
|
||||
case "ARMInstPrinter.cpp":
|
||||
return (
|
||||
b'#include "../../Mapping.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "../../SStream.h"\n'
|
||||
+ b'#include "../../utils.h"\n'
|
||||
+ b'#include "ARMAddressingModes.h"\n'
|
||||
+ b'#include "ARMBaseInfo.h"\n'
|
||||
+ b'#include "ARMDisassemblerExtension.h"\n'
|
||||
+ b'#include "ARMInstPrinter.h"\n'
|
||||
+ b'#include "ARMLinkage.h"\n'
|
||||
+ b'#include "ARMMapping.h"\n\n'
|
||||
+ b"#define GET_BANKEDREG_IMPL\n"
|
||||
+ b'#include "ARMGenSystemRegister.inc"\n'
|
||||
)
|
||||
case "ARMInstPrinter.h":
|
||||
return (
|
||||
b'#include "ARMMapping.h"\n\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../SStream.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "../../utils.h"\n\n'
|
||||
)
|
||||
case "ARMBaseInfo.cpp":
|
||||
return b'#include "ARMBaseInfo.h"\n\n'
|
||||
case "ARMAddressingModes.h":
|
||||
return b"#include <assert.h>\n" + b'#include "../../MathExtras.h"\n\n'
|
||||
log.fatal(f"No includes given for ARM source file: {filename}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_AArch64_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "AArch64Disassembler.cpp":
|
||||
return (
|
||||
b'#include "../../MCFixedLenDisassembler.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstrDesc.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "../../LEB128.h"\n'
|
||||
+ b'#include "../../MCDisassembler.h"\n'
|
||||
+ b'#include "../../cs_priv.h"\n'
|
||||
+ b'#include "../../utils.h"\n'
|
||||
+ b'#include "AArch64AddressingModes.h"\n'
|
||||
+ b'#include "AArch64BaseInfo.h"\n'
|
||||
+ b'#include "AArch64DisassemblerExtension.h"\n'
|
||||
+ b'#include "AArch64Linkage.h"\n'
|
||||
+ b'#include "AArch64Mapping.h"\n\n'
|
||||
+ b"#define GET_INSTRINFO_MC_DESC\n"
|
||||
+ b'#include "AArch64GenInstrInfo.inc"\n\n'
|
||||
+ b"#define GET_INSTRINFO_ENUM\n"
|
||||
+ b'#include "AArch64GenInstrInfo.inc"\n\n'
|
||||
)
|
||||
case "AArch64InstPrinter.cpp":
|
||||
return (
|
||||
b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "../../SStream.h"\n'
|
||||
+ b'#include "../../utils.h"\n'
|
||||
+ b'#include "AArch64AddressingModes.h"\n'
|
||||
+ b'#include "AArch64BaseInfo.h"\n'
|
||||
+ b'#include "AArch64DisassemblerExtension.h"\n'
|
||||
+ b'#include "AArch64InstPrinter.h"\n'
|
||||
+ b'#include "AArch64Linkage.h"\n'
|
||||
+ b'#include "AArch64Mapping.h"\n\n'
|
||||
+ b"#define GET_BANKEDREG_IMPL\n"
|
||||
+ b'#include "AArch64GenSystemOperands.inc"\n\n'
|
||||
+ b"#define CONCATs(a, b) CONCATS(a, b)\n"
|
||||
+ b"#define CONCATS(a, b) a##b\n\n"
|
||||
)
|
||||
case "AArch64InstPrinter.h":
|
||||
return (
|
||||
b'#include "AArch64Mapping.h"\n\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "../../SStream.h"\n'
|
||||
+ b'#include "../../utils.h"\n\n'
|
||||
)
|
||||
case "AArch64BaseInfo.cpp":
|
||||
return b'#include "AArch64BaseInfo.h"\n\n'
|
||||
case "AArch64BaseInfo.h":
|
||||
return (
|
||||
b'#include "../../utils.h"\n'
|
||||
+ b"#define GET_REGINFO_ENUM\n"
|
||||
+ b'#include "AArch64GenRegisterInfo.inc"\n\n'
|
||||
+ b"#define GET_INSTRINFO_ENUM\n"
|
||||
+ b'#include "AArch64GenInstrInfo.inc"\n\n'
|
||||
)
|
||||
case "AArch64AddressingModes.h":
|
||||
return b"#include <assert.h>\n" + b'#include "../../MathExtras.h"\n\n'
|
||||
log.fatal(f"No includes given for AArch64 source file: {filename}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_LoongArch_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "LoongArchDisassembler.cpp":
|
||||
return (
|
||||
b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MathExtras.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "../../MCDisassembler.h"\n'
|
||||
+ b'#include "../../MCFixedLenDisassembler.h"\n'
|
||||
+ b'#include "../../cs_priv.h"\n'
|
||||
+ b'#include "../../utils.h"\n'
|
||||
+ b'#include "LoongArchDisassemblerExtension.h"\n'
|
||||
+ b"#define GET_SUBTARGETINFO_ENUM\n"
|
||||
+ b'#include "LoongArchGenSubtargetInfo.inc"\n\n'
|
||||
+ b"#define GET_INSTRINFO_ENUM\n"
|
||||
+ b'#include "LoongArchGenInstrInfo.inc"\n\n'
|
||||
+ b"#define GET_REGINFO_ENUM\n"
|
||||
+ b'#include "LoongArchGenRegisterInfo.inc"\n\n'
|
||||
)
|
||||
case "LoongArchInstPrinter.cpp":
|
||||
return (
|
||||
b'#include "LoongArchMapping.h"\n'
|
||||
+ b'#include "LoongArchInstPrinter.h"\n\n'
|
||||
+ b"#define GET_SUBTARGETINFO_ENUM\n"
|
||||
+ b'#include "LoongArchGenSubtargetInfo.inc"\n\n'
|
||||
+ b"#define GET_INSTRINFO_ENUM\n"
|
||||
+ b'#include "LoongArchGenInstrInfo.inc"\n\n'
|
||||
+ b"#define GET_REGINFO_ENUM\n"
|
||||
+ b'#include "LoongArchGenRegisterInfo.inc"\n\n'
|
||||
)
|
||||
case "LoongArchInstPrinter.h":
|
||||
return (
|
||||
b'#include "../../MCInstPrinter.h"\n' + b'#include "../../cs_priv.h"\n'
|
||||
)
|
||||
log.fatal(f"No includes given for LoongArch source file: {filename}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_Mips_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "MipsDisassembler.cpp":
|
||||
return (
|
||||
b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MathExtras.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "../../MCDisassembler.h"\n'
|
||||
+ b'#include "../../MCFixedLenDisassembler.h"\n'
|
||||
+ b'#include "../../cs_priv.h"\n'
|
||||
+ b'#include "../../utils.h"\n'
|
||||
+ b"#define GET_SUBTARGETINFO_ENUM\n"
|
||||
+ b'#include "MipsGenSubtargetInfo.inc"\n\n'
|
||||
+ b"#define GET_INSTRINFO_ENUM\n"
|
||||
+ b'#include "MipsGenInstrInfo.inc"\n\n'
|
||||
+ b"#define GET_REGINFO_ENUM\n"
|
||||
+ b'#include "MipsGenRegisterInfo.inc"\n\n'
|
||||
)
|
||||
case "MipsInstPrinter.cpp":
|
||||
return (
|
||||
b'#include "MipsMapping.h"\n'
|
||||
+ b'#include "MipsInstPrinter.h"\n\n'
|
||||
+ b"#define GET_SUBTARGETINFO_ENUM\n"
|
||||
+ b'#include "MipsGenSubtargetInfo.inc"\n\n'
|
||||
+ b"#define GET_INSTRINFO_ENUM\n"
|
||||
+ b'#include "MipsGenInstrInfo.inc"\n\n'
|
||||
+ b"#define GET_REGINFO_ENUM\n"
|
||||
+ b'#include "MipsGenRegisterInfo.inc"\n\n'
|
||||
)
|
||||
case "MipsInstPrinter.h":
|
||||
return (
|
||||
b'#include "../../MCInstPrinter.h"\n' + b'#include "../../cs_priv.h"\n'
|
||||
)
|
||||
log.fatal(f"No includes given for Mips source file: {filename}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_SystemZ_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "SystemZDisassembler.cpp":
|
||||
return (
|
||||
b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MathExtras.h"\n'
|
||||
+ b'#include "../../cs_priv.h"\n'
|
||||
+ b'#include "../../utils.h"\n\n'
|
||||
+ b'#include "SystemZMCTargetDesc.h"\n'
|
||||
+ b'#include "SystemZDisassemblerExtension.h"\n\n'
|
||||
+ b"#define GET_SUBTARGETINFO_ENUM\n"
|
||||
+ b'#include "SystemZGenSubtargetInfo.inc"\n\n'
|
||||
+ b"#define GET_INSTRINFO_ENUM\n"
|
||||
+ b'#include "SystemZGenInstrInfo.inc"\n\n'
|
||||
+ b"#define GET_REGINFO_ENUM\n"
|
||||
+ b'#include "SystemZGenRegisterInfo.inc"\n\n'
|
||||
)
|
||||
case "SystemZInstPrinter.cpp":
|
||||
return (
|
||||
b'#include "../../MCAsmInfo.h"\n'
|
||||
+ b'#include "SystemZMapping.h"\n'
|
||||
+ b'#include "SystemZInstPrinter.h"\n\n'
|
||||
+ b"#define GET_SUBTARGETINFO_ENUM\n"
|
||||
+ b'#include "SystemZGenSubtargetInfo.inc"\n\n'
|
||||
+ b"#define GET_INSTRINFO_ENUM\n"
|
||||
+ b'#include "SystemZGenInstrInfo.inc"\n\n'
|
||||
+ b"#define GET_REGINFO_ENUM\n"
|
||||
+ b'#include "SystemZGenRegisterInfo.inc"\n\n'
|
||||
)
|
||||
case "SystemZInstPrinter.h":
|
||||
return b"\n"
|
||||
case "SystemZMCTargetDesc.h":
|
||||
return (
|
||||
b'#include "../../MCInstPrinter.h"\n' + b'#include "../../cs_priv.h"\n'
|
||||
)
|
||||
case "SystemZMCTargetDesc.cpp":
|
||||
return (
|
||||
b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n\n'
|
||||
+ b'#include "SystemZMCTargetDesc.h"\n'
|
||||
+ b'#include "SystemZInstPrinter.h"\n\n'
|
||||
+ b"#define GET_INSTRINFO_MC_DESC\n"
|
||||
+ b"#define ENABLE_INSTR_PREDICATE_VERIFIER\n"
|
||||
+ b'#include "SystemZGenInstrInfo.inc"\n\n'
|
||||
+ b"#define GET_SUBTARGETINFO_MC_DESC\n"
|
||||
+ b'#include "SystemZGenSubtargetInfo.inc"\n\n'
|
||||
+ b"#define GET_REGINFO_MC_DESC\n"
|
||||
+ b'#include "SystemZGenRegisterInfo.inc"\n\n'
|
||||
)
|
||||
log.fatal(f"No includes given for SystemZ source file: {filename}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_Xtensa_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "XtensaDisassembler.cpp":
|
||||
return b"""
|
||||
#include "../../MathExtras.h"
|
||||
#include "../../MCDisassembler.h"
|
||||
#include "../../MCFixedLenDisassembler.h"
|
||||
#include "../../SStream.h"
|
||||
#include "../../cs_priv.h"
|
||||
#include "priv.h"
|
||||
|
||||
#define GET_INSTRINFO_MC_DESC
|
||||
#include "XtensaGenInstrInfo.inc"
|
||||
|
||||
"""
|
||||
case "XtensaInstPrinter.cpp":
|
||||
return b"""
|
||||
#include "../../MCInstPrinter.h"
|
||||
#include "../../SStream.h"
|
||||
#include "XtensaMapping.h"
|
||||
#include "priv.h"
|
||||
"""
|
||||
case _:
|
||||
return b""
|
||||
|
||||
|
||||
def get_ARC_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "ARCDisassembler.cpp":
|
||||
return (
|
||||
b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../SStream.h"\n'
|
||||
+ b'#include "../../MCDisassembler.h"\n'
|
||||
+ b'#include "../../MCFixedLenDisassembler.h"\n'
|
||||
+ b'#include "../../MathExtras.h"\n'
|
||||
+ b'#include "../../utils.h"\n'
|
||||
)
|
||||
case "ARCInstPrinter.cpp":
|
||||
return (
|
||||
b'#include "../../SStream.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstPrinter.h"\n'
|
||||
+ b'#include "ARCInfo.h"\n'
|
||||
+ b'#include "ARCInstPrinter.h"\n'
|
||||
+ b'#include "ARCLinkage.h"\n'
|
||||
+ b'#include "ARCMapping.h"\n'
|
||||
)
|
||||
case "ARCInstPrinter.h":
|
||||
return b'#include "../../SStream.h"\n' + b'#include "../../MCInst.h"\n'
|
||||
log.fatal(f"No includes given for ARC source file: {filename}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_sparc_includes(filename: str) -> bytes:
|
||||
match filename:
|
||||
case "SparcDisassembler.cpp":
|
||||
return (
|
||||
b'#include "../../MCDisassembler.h"\n'
|
||||
+ b'#include "../../MCFixedLenDisassembler.h"\n'
|
||||
+ b'#include "SparcDisassemblerExtension.h"\n'
|
||||
+ b'#include "SparcLinkage.h"\n'
|
||||
+ b'#include "SparcMapping.h"\n'
|
||||
+ b'#include "SparcMCTargetDesc.h"\n'
|
||||
)
|
||||
case "SparcInstPrinter.cpp":
|
||||
return (
|
||||
b'#include "SparcInstrInfo.h"\n'
|
||||
+ b'#include "SparcInstPrinter.h"\n'
|
||||
+ b'#include "SparcLinkage.h"\n'
|
||||
+ b'#include "SparcMCTargetDesc.h"\n'
|
||||
+ b'#include "SparcMapping.h"\n'
|
||||
+ b'#include "SparcRegisterInfo.h"\n\n'
|
||||
)
|
||||
case "SparcInstPrinter.h":
|
||||
return b'#include "SparcMCTargetDesc.h"\n\n'
|
||||
case "SparcMCTargetDesc.h":
|
||||
return (
|
||||
b'#include "../../LEB128.h"\n'
|
||||
+ b'#include "../../MathExtras.h"\n'
|
||||
+ b'#include "../../MCInst.h"\n'
|
||||
+ b'#include "../../MCInstrDesc.h"\n'
|
||||
+ b'#include "../../MCRegisterInfo.h"\n'
|
||||
)
|
||||
log.fatal(f"No includes given for Sparc source file: {filename}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def get_general_macros():
|
||||
return (
|
||||
b"#define CONCAT(a, b) CONCAT_(a, b)\n" b"#define CONCAT_(a, b) a ## _ ## b\n"
|
||||
)
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class InlineToStaticInline(Patch):
|
||||
"""
|
||||
Removes the qualified identifier of the class from method definitions.
|
||||
Translating them to functions.
|
||||
|
||||
Patch inline void FUNCTION(...) {...}
|
||||
to static inline void FUNCTION(...) {...}
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(function_definition"
|
||||
' ((storage_class_specifier) @scs (#eq? @scs "inline"))'
|
||||
" (_)+"
|
||||
") @inline_def"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "inline_def"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
inline_def = captures[0][0]
|
||||
inline_def = get_text(src, inline_def.start_byte, inline_def.end_byte)
|
||||
return b"static " + inline_def
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class IsOptionalDef(Patch):
|
||||
"""
|
||||
Patch OpInfo[i].isOptionalDef()
|
||||
to MCOperandInfo_isOptionalDef(&OpInfo[i])
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
" (field_expression"
|
||||
" (subscript_expression"
|
||||
" ((identifier) @op_info_var)"
|
||||
" ((_) @index)"
|
||||
" )"
|
||||
' ((field_identifier) @fid (#eq? @fid "isOptionalDef"))'
|
||||
" )"
|
||||
") @is_optional_def"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "is_optional_def"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
op_info_var = captures[1][0]
|
||||
index = captures[2][0]
|
||||
op_info_var = get_text(src, op_info_var.start_byte, op_info_var.end_byte)
|
||||
index = get_text(src, index.start_byte, index.end_byte)
|
||||
return b"MCOperandInfo_isOptionalDef(&" + op_info_var + index + b")"
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class IsPredicate(Patch):
|
||||
"""
|
||||
Patch OpInfo[i].isPredicate()
|
||||
to MCOperandInfo_isPredicate(&OpInfo[i])
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
" (field_expression"
|
||||
" (subscript_expression"
|
||||
" ((identifier) @op_info_var)"
|
||||
" ((_) @index)"
|
||||
" )"
|
||||
' ((field_identifier) @fid (#eq? @fid "isPredicate"))'
|
||||
" )"
|
||||
") @is_predicate"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "is_predicate"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
op_info_var = captures[1][0]
|
||||
index = captures[2][0]
|
||||
op_info_var = get_text(src, op_info_var.start_byte, op_info_var.end_byte)
|
||||
index = get_text(src, index.start_byte, index.end_byte)
|
||||
return b"MCOperandInfo_isPredicate(&" + op_info_var + index + b")"
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class IsOperandRegImm(Patch):
|
||||
"""
|
||||
Patch OPERAND.isReg()
|
||||
to MCOperand_isReg(OPERAND)
|
||||
|
||||
Same for isImm() | isExpr()
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression"
|
||||
" (field_expression"
|
||||
" ((_) @operand)"
|
||||
' ((field_identifier) @field_id (#match? @field_id "is(Reg|Imm|Expr)"))'
|
||||
" )"
|
||||
" (argument_list)"
|
||||
") @is_operand"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "is_operand"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# The operand
|
||||
operand: Node = captures[1][0]
|
||||
# 'isReg()/isImm()/isExpr'
|
||||
get_reg_imm = captures[2][0]
|
||||
|
||||
fcn = get_text(src, get_reg_imm.start_byte, get_reg_imm.end_byte)
|
||||
op = get_text(src, operand.start_byte, operand.end_byte)
|
||||
return b"MCOperand_" + fcn + b"(" + op + b")"
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class LLVMFallThrough(Patch):
|
||||
"""
|
||||
Patch Remove LLVM_FALLTHROUGH
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(expression_statement"
|
||||
' ((identifier) @id (#eq? @id "LLVM_FALLTHROUGH"))'
|
||||
") @llvm_fall_through"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "llvm_fall_through"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b""
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# Copyright © 2024 Billow <billow.fun@gmail.com>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class LLVM_DEBUG(Patch):
|
||||
"""
|
||||
Patch LLVM_DEBUG(dbgs() << "Error msg")
|
||||
to ""
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return """
|
||||
(call_expression (
|
||||
(identifier) @fcn_name (#eq? @fcn_name "LLVM_DEBUG")
|
||||
(argument_list (
|
||||
(binary_expression (
|
||||
(call_expression)
|
||||
(string_literal) @err_msg
|
||||
))
|
||||
))
|
||||
)) @llvm_debug"""
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "llvm_debug"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b""
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class LLVMUnreachable(Patch):
|
||||
"""
|
||||
Patch llvm_unreachable("Error msg")
|
||||
to assert(0 && "Error msg")
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression ("
|
||||
' (identifier) @fcn_name (#eq? @fcn_name "llvm_unreachable")'
|
||||
" (argument_list) @err_msg"
|
||||
")) @llvm_unreachable"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "llvm_unreachable"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
err_msg = captures[2][0]
|
||||
err_msg = get_text(src, err_msg.start_byte, err_msg.end_byte).strip(b"()")
|
||||
res = b"CS_ASSERT(0 && " + err_msg + b")"
|
||||
return res
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class MethodToFunction(Patch):
|
||||
"""
|
||||
Removes the qualified identifier of the class from method definitions.
|
||||
Translating them to functions.
|
||||
|
||||
Patch void CLASS::METHOD_NAME(...) {...}
|
||||
to void METHOD_NAME(...) {...}
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(function_declarator"
|
||||
" (qualified_identifier"
|
||||
" (namespace_identifier)"
|
||||
" (identifier) @method_name"
|
||||
" )"
|
||||
" (parameter_list) @param_list"
|
||||
") @method_def"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "method_def"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
name = captures[1][0]
|
||||
parameter_list = captures[2][0]
|
||||
name = get_text(src, name.start_byte, name.end_byte)
|
||||
parameter_list = get_text(
|
||||
src, parameter_list.start_byte, parameter_list.end_byte
|
||||
)
|
||||
res = name + parameter_list
|
||||
return res
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class MethodTypeQualifier(Patch):
|
||||
"""
|
||||
Patch Removes type qualifiers like "const" etc. from methods.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(function_declarator"
|
||||
" (["
|
||||
" (qualified_identifier)"
|
||||
" (identifier)"
|
||||
" ]) @id"
|
||||
" (parameter_list) @param_list"
|
||||
" (type_qualifier)"
|
||||
")"
|
||||
"@method_type_qualifier"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "method_type_qualifier"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
identifier = captures[1][0]
|
||||
parameter_list = captures[2][0]
|
||||
identifier = get_text(src, identifier.start_byte, identifier.end_byte)
|
||||
p_list = get_text(src, parameter_list.start_byte, parameter_list.end_byte)
|
||||
res = identifier + p_list
|
||||
return res
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class NamespaceAnon(Patch):
|
||||
"""
|
||||
Patch namespace {CONTENT}
|
||||
to CONTENT
|
||||
|
||||
Only for anonymous or llvm namespaces
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(namespace_definition"
|
||||
" (declaration_list) @decl_list"
|
||||
") @namespace_def"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "namespace_def"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
decl_list = captures[1][0]
|
||||
dl = get_text(src, decl_list.start_byte, decl_list.end_byte).strip(b"{}")
|
||||
return dl
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import (
|
||||
get_text,
|
||||
namespace_enum,
|
||||
namespace_fcn_def,
|
||||
namespace_struct,
|
||||
)
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class NamespaceArch(Patch):
|
||||
"""
|
||||
Patch namespace ArchSpecificNamespace {CONTENT}
|
||||
to CONTENT
|
||||
|
||||
Patches namespaces specific to architecture. This needs to patch enums and functions within this namespace.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(namespace_definition"
|
||||
" (namespace_identifier)"
|
||||
" (declaration_list) @decl_list"
|
||||
") @namespace_def"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "namespace_def"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
namespace = captures[0][0]
|
||||
decl_list = captures[1][0]
|
||||
namespace_id = get_text(
|
||||
src,
|
||||
namespace.named_children[0].start_byte,
|
||||
namespace.named_children[0].end_byte,
|
||||
)
|
||||
|
||||
# We need to prepend the namespace id to all enum members, function declarators and struct types.
|
||||
# Because in the generated files they are accessed via NAMESPACE::X which becomes NAMESPACE_X.
|
||||
res = b""
|
||||
for d in decl_list.named_children:
|
||||
match d.type:
|
||||
case "enum_specifier":
|
||||
res += namespace_enum(src, namespace_id, d) + b";\n\n"
|
||||
case "declaration" | "function_definition":
|
||||
res += namespace_fcn_def(src, namespace_id, d) + b"\n\n"
|
||||
case "struct_specifier":
|
||||
res += namespace_struct(src, namespace_id, d) + b";\n\n"
|
||||
case _:
|
||||
res += get_text(src, d.start_byte, d.end_byte) + b"\n"
|
||||
return (
|
||||
b"// CS namespace begin: "
|
||||
+ namespace_id
|
||||
+ b"\n\n"
|
||||
+ res
|
||||
+ b"// CS namespace end: "
|
||||
+ namespace_id
|
||||
+ b"\n\n"
|
||||
)
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class NamespaceLLVM(Patch):
|
||||
"""
|
||||
Patch namespace llvm {CONTENT}
|
||||
to CONTENT
|
||||
|
||||
Only for anonymous or llvm namespaces
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(namespace_definition"
|
||||
' (namespace_identifier) @id (#eq? @id "llvm")'
|
||||
" (declaration_list) @decl_list"
|
||||
") @namespace_def"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "namespace_def"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
decl_list = captures[2][0]
|
||||
dl = get_text(src, decl_list.start_byte, decl_list.end_byte).strip(b"{}")
|
||||
return dl
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class OutStreamParam(Patch):
|
||||
"""
|
||||
Patches the parameter list only:
|
||||
|
||||
Patch void function(int a, raw_ostream &OS)
|
||||
to void function(int a, SStream *OS)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(parameter_list"
|
||||
" (_)*"
|
||||
" (parameter_declaration"
|
||||
' ((type_identifier) @tid (#eq? @tid "raw_ostream"))'
|
||||
" (_)"
|
||||
" )"
|
||||
" (_)*"
|
||||
") @ostream_param"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "ostream_param"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
param_list = list()
|
||||
for param in captures[0][0].named_children:
|
||||
p_text = get_text(src, param.start_byte, param.end_byte)
|
||||
if b"raw_ostream" in p_text:
|
||||
p_text = p_text.replace(b"raw_ostream", b"SStream").replace(b"&", b"*")
|
||||
param_list.append(p_text)
|
||||
res = b"(" + b", ".join(param_list) + b")"
|
||||
return res
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class Override(Patch):
|
||||
"""
|
||||
Patch function(args) override
|
||||
to function(args)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(function_declarator "
|
||||
" ((field_identifier) @declarator)"
|
||||
" ((parameter_list) @parameter_list)"
|
||||
' ((virtual_specifier) @specifier (#eq? @specifier "override"))'
|
||||
") @override"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "override"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get function name
|
||||
declarator: Node = captures[1][0]
|
||||
# Get parameter list
|
||||
parameter_list: Node = captures[2][0]
|
||||
decl = get_text(src, declarator.start_byte, declarator.end_byte)
|
||||
params = get_text(src, parameter_list.start_byte, parameter_list.end_byte)
|
||||
return decl + params
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
|
||||
class Patch:
|
||||
priority: int = None
|
||||
|
||||
def __init__(self, priority: int = 0):
|
||||
self.priority = priority
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
"""
|
||||
Returns a search pattern for the syntax tree of the C++ file.
|
||||
The search pattern must be formed according to:
|
||||
https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries
|
||||
|
||||
Also, each pattern needs to be assigned a name in order to work.
|
||||
See: https://github.com/tree-sitter/py-tree-sitter/issues/77
|
||||
|
||||
:return: The search pattern which matches a part in the syntax tree which will be patched.
|
||||
"""
|
||||
log.fatal("Method must be overloaded.")
|
||||
exit(1)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
"""
|
||||
:return: The name of the capture which matches the complete syntax to be patched.
|
||||
"""
|
||||
log.fatal("Method must be overloaded.")
|
||||
exit(1)
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
"""
|
||||
Patches the given subtree accordingly and returns the patch as string.
|
||||
|
||||
:param src: The source code currently patched.
|
||||
:param captures: The subtree and its name which needs to be patched.
|
||||
:param **kwargs: Additional arguments the Patch might need.
|
||||
:return: The patched version of the code.
|
||||
"""
|
||||
log.fatal("Method must be overloaded.")
|
||||
exit(1)
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class PredicateBlockFunctions(Patch):
|
||||
"""
|
||||
Patch VPTBlock.instrInVPTBlock()
|
||||
to VPTBlock_instrInVPTBlock(&(MI->csh->VPTBlock))
|
||||
|
||||
And other functions of VPTBlock and ITBlock
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression "
|
||||
" (field_expression"
|
||||
' ((identifier) @block_var (#match? @block_var "[VI][PT]T?Block"))'
|
||||
" ((field_identifier) @field_id)"
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @block_fcns"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "block_fcns"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
block_var = captures[1][0]
|
||||
fcn_id = captures[2][0]
|
||||
args = captures[3][0]
|
||||
block_var_text = get_text(src, block_var.start_byte, block_var.end_byte)
|
||||
fcn_id_text = get_text(src, fcn_id.start_byte, fcn_id.end_byte)
|
||||
args_text = get_text(src, args.start_byte, args.end_byte)
|
||||
mcinst_var: bytes = get_MCInst_var_name(src, block_var)
|
||||
|
||||
a = b"&(" + mcinst_var + b"->csh->" + block_var_text + b")"
|
||||
args_text = args_text.strip(b"()")
|
||||
if args_text:
|
||||
a += b"," + args_text
|
||||
return block_var_text + b"_" + fcn_id_text + b"(" + a + b")"
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class PrintAnnotation(Patch):
|
||||
"""
|
||||
Removes printAnnotation(...) calls.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression ("
|
||||
' (identifier) @fcn_name (#eq? @fcn_name "printAnnotation")'
|
||||
" (argument_list)"
|
||||
")) @print_annotation"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "print_annotation"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b""
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_MCInst_var_name, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class PrintRegImmShift(Patch):
|
||||
"""
|
||||
Patch printRegImmShift(...)
|
||||
to printRegImmShift(MI, ...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
self.apply_only_to = {"files": ["ARMInstPrinter.cpp"], "archs": list()}
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression ("
|
||||
' (identifier) @fcn_name (#eq? @fcn_name "printRegImmShift")'
|
||||
" ((argument_list) @arg_list)"
|
||||
")) @print_call"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "print_call"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
call: Node = captures[0][0]
|
||||
mcinst_var = get_MCInst_var_name(src, call)
|
||||
params = captures[2][0]
|
||||
params = get_text(src, params.start_byte, params.end_byte)
|
||||
return b"printRegImmShift(" + mcinst_var + b", " + params.strip(b"(")
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class QualifiedIdentifier(Patch):
|
||||
"""
|
||||
Patch NAMESPACE::ID
|
||||
to NAMESPACE_ID
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return "(qualified_identifier) @qualified_id"
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "qualified_id"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
if len(captures[0][0].named_children) > 1:
|
||||
identifier = captures[0][0].named_children[1]
|
||||
identifier = get_text(src, identifier.start_byte, identifier.end_byte)
|
||||
namespace = captures[0][0].named_children[0]
|
||||
namespace = get_text(src, namespace.start_byte, namespace.end_byte)
|
||||
else:
|
||||
# The namespace can be omitted. E.g. std::transform(..., ::tolower)
|
||||
namespace = b""
|
||||
identifier = captures[0][0].named_children[0]
|
||||
identifier = get_text(src, identifier.start_byte, identifier.end_byte)
|
||||
match (namespace, identifier):
|
||||
case (b"std", b"size"):
|
||||
return b"sizeof"
|
||||
case _:
|
||||
return namespace + b"_" + identifier
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class ReferencesDecl(Patch):
|
||||
"""
|
||||
Patch TYPE &Param
|
||||
to TYPE *Param
|
||||
|
||||
Param is optional
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"["
|
||||
"(reference_declarator)"
|
||||
"(type_identifier) (abstract_reference_declarator)"
|
||||
"] @reference_decl"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "reference_decl"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
ref_decl: Node = captures[0][0]
|
||||
ref_decl_text = get_text(src, ref_decl.start_byte, ref_decl.end_byte)
|
||||
|
||||
res = re.sub(rb"&", b"*", ref_decl_text)
|
||||
return res
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_capture_node, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class RegClassContains(Patch):
|
||||
"""
|
||||
Patch ...getRegClass(...).contains(Reg)
|
||||
to MCRegisterClass_contains(...getRegClass(...), Reg)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression"
|
||||
" (field_expression"
|
||||
' ((_) @reg_class (#match? @reg_class ".+getRegClass.+"))'
|
||||
' ((field_identifier) @field_id (#eq? @field_id "contains"))'
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @reg_class_contains"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "reg_class_contains"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
reg_class_getter: Node = get_capture_node(captures, "reg_class")
|
||||
arg_list: Node = get_capture_node(captures, "arg_list")
|
||||
args = get_text(src, arg_list.start_byte, arg_list.end_byte).strip(b"()")
|
||||
reg_class = get_text(
|
||||
src, reg_class_getter.start_byte, reg_class_getter.end_byte
|
||||
)
|
||||
res = b"MCRegisterClass_contains(" + reg_class + b", " + args + b")"
|
||||
return res
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class STIArgument(Patch):
|
||||
"""
|
||||
Patch printSomeOperand(MI, NUM, STI, NUM)
|
||||
to printSomeOperand(MI, NUM, NUM)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return '(argument_list (_) (_) (_)? ((identifier) @id (#eq? @id "STI")) (_)) @sti_arg'
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "sti_arg"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
param_list = list()
|
||||
for param in captures[0][0].named_children:
|
||||
p_text = get_text(src, param.start_byte, param.end_byte)
|
||||
if b"STI" in p_text:
|
||||
continue
|
||||
param_list.append(p_text)
|
||||
res = b"(" + b", ".join(param_list) + b")"
|
||||
return res
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class STIFeatureBits(Patch):
|
||||
"""
|
||||
Patch STI.getFeatureBits()[ARCH::FLAG]
|
||||
to ARCH_getFeatureBits(Inst->csh->mode, ARCH::FLAG)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int, arch: bytes):
|
||||
self.arch = arch
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
# Search for featureBits usage.
|
||||
return (
|
||||
"(subscript_expression "
|
||||
" (call_expression"
|
||||
" (field_expression"
|
||||
" (identifier)"
|
||||
' ((field_identifier) @fid (#eq? @fid "getFeatureBits"))'
|
||||
" )"
|
||||
" (argument_list)"
|
||||
" )"
|
||||
" (subscript_argument_list ((qualified_identifier) @flag))"
|
||||
") @sti_feature_bits"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "sti_feature_bits"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get flag name of feature bit.
|
||||
qualified_id: Node = captures[2][0]
|
||||
flag = get_text(src, qualified_id.start_byte, qualified_id.end_byte)
|
||||
return self.arch + b"_getFeatureBits(Inst->csh->mode, " + flag + b")"
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class SubtargetInfoParam(Patch):
|
||||
"""
|
||||
Patch Removes MCSubtargetInfo &STI parameter
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(parameter_list"
|
||||
" (_)*"
|
||||
" (parameter_declaration"
|
||||
' ((type_identifier) @tid (#eq? @tid "MCSubtargetInfo"))'
|
||||
" (_)"
|
||||
" )"
|
||||
" (_)*"
|
||||
") @subtarget_info_param"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "subtarget_info_param"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
param_list = list()
|
||||
for param in captures[0][0].named_children:
|
||||
p_text = get_text(src, param.start_byte, param.end_byte)
|
||||
if b"MCSubtargetInfo" in p_text:
|
||||
continue
|
||||
param_list.append(p_text)
|
||||
res = b"(" + b", ".join(param_list) + b")"
|
||||
return res
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class SetOpcode(Patch):
|
||||
"""
|
||||
Patch Inst.setOpcode(...)
|
||||
to MCInst_setOpcode(Inst, ...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
" (field_expression ("
|
||||
" ((identifier) @inst_var)"
|
||||
' ((field_identifier) @field_id (#eq? @field_id "setOpcode")))'
|
||||
" )"
|
||||
" (argument_list) @arg_list"
|
||||
") @set_opcode"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "set_opcode"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Instruction variable
|
||||
inst_var: Node = captures[1][0]
|
||||
arg_list: Node = captures[3][0]
|
||||
|
||||
inst = get_text(src, inst_var.start_byte, inst_var.end_byte)
|
||||
args = get_text(src, arg_list.start_byte, arg_list.end_byte)
|
||||
if args != b"()":
|
||||
args = b", " + args
|
||||
else:
|
||||
args = b""
|
||||
return b"MCInst_setOpcode(" + inst + args + b")"
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
from autosync.cpptranslator.TemplateCollector import TemplateCollector
|
||||
|
||||
|
||||
class SignExtend(Patch):
|
||||
"""
|
||||
Patch SignExtend32<A>(...)
|
||||
to SignExtend32(..., A)
|
||||
|
||||
Same for SignExtend64
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
" (template_function"
|
||||
' ((identifier) @name (#match? @name "SignExtend(32|64)"))'
|
||||
" ((template_argument_list) @templ_args)"
|
||||
" )"
|
||||
" ((argument_list) @fcn_args)"
|
||||
") @sign_extend"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "sign_extend"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
sign_extend: Node = captures[1][0]
|
||||
templ_args: Node = captures[2][0]
|
||||
fcn_args: Node = captures[3][0]
|
||||
|
||||
name = get_text(src, sign_extend.start_byte, sign_extend.end_byte)
|
||||
t_args = get_text(src, templ_args.start_byte, templ_args.end_byte)
|
||||
t_args = b", ".join(TemplateCollector.templ_params_to_list(t_args))
|
||||
f_args = get_text(src, fcn_args.start_byte, fcn_args.end_byte)
|
||||
return name + b"(" + f_args + b", " + t_args + b")"
|
||||
@@ -0,0 +1,35 @@
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class Size(Patch):
|
||||
"""
|
||||
Patch Bytes.size()
|
||||
to BytesLen
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
q = (
|
||||
"(call_expression "
|
||||
" (field_expression"
|
||||
" ((identifier) @inst_var)"
|
||||
' ((field_identifier) @field_id_op (#eq? @field_id_op "size"))'
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @size"
|
||||
)
|
||||
return q
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "size"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
# Get operand variable name (Bytes, ArrayRef)
|
||||
op_var: Node = captures[1][0]
|
||||
op = get_text(src, op_var.start_byte, op_var.end_byte)
|
||||
return op + b"Len"
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_function_params_of_node, get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class SizeAssignment(Patch):
|
||||
"""
|
||||
Patch Size = <num>
|
||||
to *Size = <num>
|
||||
|
||||
if Size is a reference.
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(assignment_expression"
|
||||
' ((identifier) @id (#eq? @id "Size"))'
|
||||
") @assign"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "assign"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
assign = captures[0][0]
|
||||
assign_text = get_text(src, assign.start_byte, assign.end_byte)
|
||||
|
||||
param_list = get_function_params_of_node(assign)
|
||||
if not param_list:
|
||||
return assign_text
|
||||
|
||||
for p in param_list.named_children:
|
||||
p_text = get_text(src, p.start_byte, p.end_byte)
|
||||
if b"&Size" in p_text:
|
||||
return re.sub(b"Size", b"*Size", assign_text)
|
||||
|
||||
return assign_text
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text, get_text_from_node
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class StreamOperations(Patch):
|
||||
"""
|
||||
Patch OS << ...
|
||||
to SStream_concat(OS, ...)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(expression_statement"
|
||||
" (binary_expression"
|
||||
" ((binary_expression)"
|
||||
' "<<"'
|
||||
" (_))*"
|
||||
" ) @bin_expr"
|
||||
") @stream"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "stream"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
bin_expr = captures[1][0]
|
||||
# Extract operands passed to the stream into a list.
|
||||
ops = list()
|
||||
while bin_expr.type == "binary_expression":
|
||||
ops.append(bin_expr.named_children[1])
|
||||
bin_expr = bin_expr.named_children[0]
|
||||
s_name = get_text(src, bin_expr.start_byte, bin_expr.end_byte)
|
||||
# We added the operands from right to left.
|
||||
# We reversing it so the left most operand comes first.
|
||||
ops.reverse()
|
||||
|
||||
res = b""
|
||||
# Capstone uses the following functions to copy the strings to a buffer:
|
||||
# SStream_concat - Copies multiple strings.
|
||||
# SStream_concat1 - Copies a char.
|
||||
# SStream_concat0 - Copies a string and null terminates the buffer.
|
||||
last_op: Node = ops[-1]
|
||||
op: Node = ops[0]
|
||||
string_ops = list()
|
||||
i = 0
|
||||
while op != last_op:
|
||||
if op.type == "char_literal":
|
||||
if len(string_ops) != 0:
|
||||
# Make a SStream_concat call with all string literals collected before.
|
||||
res += (
|
||||
b"SStream_concat("
|
||||
+ s_name
|
||||
+ b', "'
|
||||
+ b"%s" * len(string_ops)
|
||||
+ b'", '
|
||||
+ b", ".join(
|
||||
[
|
||||
get_text(src, o.start_byte, o.end_byte)
|
||||
for o in string_ops
|
||||
]
|
||||
)
|
||||
+ b");\n"
|
||||
)
|
||||
string_ops.clear()
|
||||
res += (
|
||||
b"SStream_concat1("
|
||||
+ s_name
|
||||
+ b", "
|
||||
+ get_text(src, op.start_byte, op.end_byte)
|
||||
+ b");\n"
|
||||
)
|
||||
else:
|
||||
string_ops.append(op)
|
||||
i += 1
|
||||
op = ops[i]
|
||||
|
||||
if len(string_ops) != 0:
|
||||
res += (
|
||||
b"SStream_concat("
|
||||
+ s_name
|
||||
+ b', "'
|
||||
+ b"%s" * len(string_ops)
|
||||
+ b'", '
|
||||
+ b", ".join(
|
||||
[get_text(src, o.start_byte, o.end_byte) for o in string_ops]
|
||||
)
|
||||
+ b");\n"
|
||||
)
|
||||
string_ops.clear()
|
||||
|
||||
last_op_text = get_text(src, last_op.start_byte, last_op.end_byte)
|
||||
if last_op.type == "char_literal":
|
||||
res += (
|
||||
b"SStream_concat0("
|
||||
+ s_name
|
||||
+ b", "
|
||||
+ last_op_text.replace(b"'", b'"')
|
||||
+ b");\n"
|
||||
)
|
||||
elif last_op.type == "identifier":
|
||||
queue_str = f"""
|
||||
(declaration (
|
||||
(primitive_type) @typ
|
||||
(init_declarator
|
||||
(identifier) @ident (#eq? @ident "{last_op_text.decode("utf8")}")
|
||||
)
|
||||
)) @decl
|
||||
"""
|
||||
query = kwargs["ts_cpp_lang"].query(queue_str)
|
||||
root_node = kwargs["tree"].root_node
|
||||
query_result = list(
|
||||
filter(
|
||||
lambda x: "typ" in x[1],
|
||||
query.matches(root_node),
|
||||
)
|
||||
)
|
||||
if len(query_result) == 0:
|
||||
res += b"SStream_concat0(" + s_name + b", " + last_op_text + b");"
|
||||
else:
|
||||
typ = get_text_from_node(src, query_result[0][1]["typ"][-1])
|
||||
match typ:
|
||||
case b"int":
|
||||
res += b"printInt32(" + s_name + b", " + last_op_text + b");"
|
||||
case b"int64_t":
|
||||
res += b"printInt64(" + s_name + b", " + last_op_text + b");"
|
||||
case _:
|
||||
res += (
|
||||
b"SStream_concat0(" + s_name + b", " + last_op_text + b");"
|
||||
)
|
||||
else:
|
||||
res += b"SStream_concat0(" + s_name + b", " + last_op_text + b");"
|
||||
stream = captures[0][0]
|
||||
if len(ops) > 1 and stream.parent.type in ["if_statement"]:
|
||||
# If statements without {} brackets might execute a single line `OS << ...;` statement.
|
||||
# Which we then translate into multiple lines. For this case we need to add the brackets.
|
||||
res = b"{ " + res + b" }"
|
||||
return res
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import parse_function_capture
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
from autosync.cpptranslator.TemplateCollector import (
|
||||
TemplateCollector,
|
||||
TemplateRefInstance,
|
||||
)
|
||||
|
||||
|
||||
class TemplateDeclaration(Patch):
|
||||
"""
|
||||
Patch template<A, B>
|
||||
RET_TYPE TemplateFunction(...);
|
||||
|
||||
to #define DECLARE_TemplateFunction_A_B \
|
||||
RET_TYPE CONCAT(TemplateFunction, CONCAT(A, B))(...);
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int, template_collector: TemplateCollector):
|
||||
self.collector: TemplateCollector = template_collector
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(template_declaration"
|
||||
" ((template_parameter_list) @templ_params)"
|
||||
" (declaration"
|
||||
" ((storage_class_specifier)* @storage_class_id)"
|
||||
" ([(type_identifier)(primitive_type)] @type_id)"
|
||||
" (function_declarator"
|
||||
" ((identifier) @fcn_name)"
|
||||
" ((parameter_list) @fcn_params)"
|
||||
" )"
|
||||
" )"
|
||||
") @template_decl"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "template_decl"
|
||||
|
||||
def get_patch(
|
||||
self, captures: list[tuple[Node, str]], src: bytes, **kwargs
|
||||
) -> bytes:
|
||||
t_params, sc, tid, f_name, f_params, _ = parse_function_capture(captures, src)
|
||||
if f_name in self.collector.templates_with_arg_deduction:
|
||||
return sc + tid + b" " + f_name + f_params + b";"
|
||||
|
||||
declaration = (
|
||||
b"#define DECLARE_" + f_name + b"(" + b", ".join(t_params) + b")\n"
|
||||
)
|
||||
declaration += (
|
||||
sc
|
||||
+ b" "
|
||||
+ tid
|
||||
+ b" "
|
||||
+ TemplateCollector.get_macro_c_call(f_name, t_params, f_params)
|
||||
+ b";"
|
||||
)
|
||||
declaration = declaration.replace(b"\n", b" \\\n") + b"\n"
|
||||
|
||||
template_instance: TemplateRefInstance
|
||||
declared_implementations = list()
|
||||
if f_name not in self.collector.template_refs:
|
||||
self.collector.log_missing_ref_and_exit(f_name)
|
||||
|
||||
for template_instance in self.collector.template_refs[f_name]:
|
||||
d = (
|
||||
b"DECLARE_"
|
||||
+ f_name
|
||||
+ b"("
|
||||
+ b", ".join(template_instance.get_args_for_decl())
|
||||
+ b");\n"
|
||||
)
|
||||
if d in declared_implementations:
|
||||
continue
|
||||
declared_implementations.append(d)
|
||||
declaration += d
|
||||
return declaration
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
import re
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import parse_function_capture
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
from autosync.cpptranslator.TemplateCollector import (
|
||||
TemplateCollector,
|
||||
TemplateRefInstance,
|
||||
)
|
||||
|
||||
|
||||
class TemplateDefinition(Patch):
|
||||
"""
|
||||
Patch template<A, B>
|
||||
RET_TYPE TemplateFunction(...) {...}
|
||||
|
||||
to #define DEFINE_TemplateFunction_A_B \
|
||||
RET_TYPE CONCAT(TemplateFunction, CONCAT(A, B))(...) {...}
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int, template_collector: TemplateCollector):
|
||||
self.collector: TemplateCollector = template_collector
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(template_declaration"
|
||||
" ((template_parameter_list) @templ_params)"
|
||||
" (function_definition"
|
||||
" ((storage_class_specifier)* @storage_class_id)"
|
||||
" ([(type_identifier)(primitive_type)] @type_id)"
|
||||
" (function_declarator"
|
||||
" ((identifier) @fcn_name)"
|
||||
" ((parameter_list) @fcn_params)"
|
||||
" )"
|
||||
" ((compound_statement) @compound)"
|
||||
" )"
|
||||
") @template_def"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "template_def"
|
||||
|
||||
def get_patch(
|
||||
self, captures: list[tuple[Node, str]], src: bytes, **kwargs
|
||||
) -> bytes:
|
||||
t_params, sc, tid, f_name, f_params, f_compound = parse_function_capture(
|
||||
captures, src
|
||||
)
|
||||
if f_name in self.collector.templates_with_arg_deduction:
|
||||
return sc + tid + b" " + f_name + f_params + f_compound
|
||||
|
||||
definition = b"#define DEFINE_" + f_name + b"(" + b", ".join(t_params) + b")\n"
|
||||
definition += (
|
||||
sc
|
||||
+ b" "
|
||||
+ tid
|
||||
+ b" "
|
||||
+ TemplateCollector.get_macro_c_call(f_name, t_params, f_params)
|
||||
+ f_compound
|
||||
)
|
||||
# Remove // comments
|
||||
definition = re.sub(b" *//.*", b"", definition)
|
||||
definition = definition.replace(b"\n", b" \\\n") + b"\n"
|
||||
|
||||
template_instance: TemplateRefInstance
|
||||
declared_implementations = list()
|
||||
if f_name not in self.collector.template_refs:
|
||||
self.collector.log_missing_ref_and_exit(f_name)
|
||||
|
||||
for template_instance in self.collector.template_refs[f_name]:
|
||||
d = (
|
||||
b"DEFINE_"
|
||||
+ f_name
|
||||
+ b"("
|
||||
+ b", ".join(template_instance.get_args_for_decl())
|
||||
+ b");\n"
|
||||
)
|
||||
if d in declared_implementations:
|
||||
continue
|
||||
declared_implementations.append(d)
|
||||
definition += d
|
||||
return definition
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
import logging as log
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class TemplateParamDecl(Patch):
|
||||
"""
|
||||
Example:
|
||||
|
||||
Patch ArrayRef<uint8_t> x
|
||||
to const uint8_t *x, size_t xLen
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(parameter_declaration"
|
||||
" (template_type"
|
||||
" (type_identifier) @templ_type"
|
||||
" (template_argument_list) @arg_list"
|
||||
" )"
|
||||
" (identifier) @param_id"
|
||||
") @template_param_decl"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "template_param_decl"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
template_type = captures[1][0]
|
||||
arg_list = captures[2][0]
|
||||
param_id = captures[3][0]
|
||||
templ_type = get_text(src, template_type.start_byte, template_type.end_byte)
|
||||
args = get_text(src, arg_list.start_byte, arg_list.end_byte)
|
||||
p_id = get_text(src, param_id.start_byte, param_id.end_byte)
|
||||
|
||||
if templ_type == b"ArrayRef":
|
||||
res = (
|
||||
b"const "
|
||||
+ args.strip(b"<>")
|
||||
+ b" *"
|
||||
+ p_id
|
||||
+ b", size_t "
|
||||
+ p_id
|
||||
+ b"Len"
|
||||
)
|
||||
return res
|
||||
log.fatal(f"Template type {templ_type} not handled as parameter")
|
||||
exit(1)
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
from autosync.cpptranslator.TemplateCollector import TemplateCollector
|
||||
|
||||
|
||||
class TemplateRefs(Patch):
|
||||
"""
|
||||
Patch TemplateFunction<A, B>
|
||||
to CONCAT(TemplateFunction, CONCAT(A, B))
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(template_function"
|
||||
" ((identifier) @name)"
|
||||
" ((template_argument_list) @templ_args)"
|
||||
") @template_refs"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "template_refs"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
tc: Node = captures[1][0]
|
||||
templ_args: Node = captures[2][0]
|
||||
|
||||
name = get_text(src, tc.start_byte, tc.end_byte)
|
||||
t_params = get_text(src, templ_args.start_byte, templ_args.end_byte)
|
||||
if name == b"static_cast" or name == b"dyn_cast":
|
||||
return t_params.replace(b"<", b"(").replace(b">", b")")
|
||||
t_params_list = TemplateCollector.templ_params_to_list(t_params)
|
||||
res = TemplateCollector.get_macro_c_call(name, t_params_list)
|
||||
return res
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class UseMarkup(Patch):
|
||||
"""
|
||||
Patch UseMarkup
|
||||
to getUseMarkup()
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return '((identifier) @use_markup (#eq? @use_markup "UseMarkup"))'
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "use_markup"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b"getUseMarkup()"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
|
||||
|
||||
class UsingDeclaration(Patch):
|
||||
"""
|
||||
Patch Removes declarations with the keyword "using"
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return "([(using_declaration) (alias_declaration)]) @using_declaration"
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "using_declaration"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
return b""
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# Copyright © 2022 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from tree_sitter import Node
|
||||
|
||||
from autosync.cpptranslator.patches.Helper import get_text
|
||||
from autosync.cpptranslator.patches.Patch import Patch
|
||||
from autosync.cpptranslator.TemplateCollector import TemplateCollector
|
||||
|
||||
|
||||
class IsUInt(Patch):
|
||||
"""
|
||||
Patch isUInt|isInt<N>(...)
|
||||
to isUInt|isInt(..., N)
|
||||
"""
|
||||
|
||||
def __init__(self, priority: int):
|
||||
super().__init__(priority)
|
||||
|
||||
def get_search_pattern(self) -> str:
|
||||
return (
|
||||
"(call_expression"
|
||||
" (template_function"
|
||||
' ((identifier) @id (#match? @id "isUInt|isInt"))'
|
||||
" ((template_argument_list) @templ_args)"
|
||||
" )"
|
||||
" ((argument_list) @arg_list)"
|
||||
") @is_u_int"
|
||||
)
|
||||
|
||||
def get_main_capture_name(self) -> str:
|
||||
return "is_u_int"
|
||||
|
||||
def get_patch(self, captures: [(Node, str)], src: bytes, **kwargs) -> bytes:
|
||||
identifier: Node = captures[1][0]
|
||||
templ_args: Node = captures[2][0]
|
||||
args_list: Node = captures[3][0]
|
||||
|
||||
name = get_text(src, identifier.start_byte, identifier.end_byte)
|
||||
targs = get_text(src, templ_args.start_byte, templ_args.end_byte).strip(b"<>")
|
||||
args = get_text(src, args_list.start_byte, args_list.end_byte).strip(b"()")
|
||||
|
||||
res = name + b"N(" + targs + b", " + args + b")"
|
||||
|
||||
return res
|
||||
+4544
File diff suppressed because it is too large
Load Diff
+20
@@ -0,0 +1,20 @@
|
||||
from tree_sitter import Node, Query
|
||||
|
||||
|
||||
# Queries for the given pattern and converts the query back to the tree-siter v22.3 format.
|
||||
# Which is: A list of tuples where the first element is the
|
||||
# Node of the capture and the second one is the name.
|
||||
def query_captures_22_3(query: Query, node: Node) -> list[tuple[Node, str]]:
|
||||
result = list()
|
||||
captures = query.captures(node)
|
||||
# Captures are no longer sorted by start point.
|
||||
captures_sorted = dict()
|
||||
nodes: list[Node]
|
||||
for name, nodes in captures.items():
|
||||
captures_sorted[name] = sorted(nodes, key=lambda n: n.start_point)
|
||||
while len(captures_sorted) != 0:
|
||||
for name, nodes in captures_sorted.items():
|
||||
node = nodes.pop(0)
|
||||
result.append((node, name))
|
||||
captures_sorted = {k: l for k, l in captures_sorted.items() if len(l) != 0}
|
||||
return result
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"inc_tables": [
|
||||
{
|
||||
"name": "Disassembler",
|
||||
"tblgen_arg": "--gen-disassembler",
|
||||
"inc_name": "DisassemblerTables",
|
||||
"only_arch": [],
|
||||
"lang": ["CCS", "C++"]
|
||||
},
|
||||
{
|
||||
"name": "AsmWriter",
|
||||
"tblgen_arg": "--gen-asm-writer",
|
||||
"inc_name": "AsmWriter",
|
||||
"only_arch": [],
|
||||
"lang": ["CCS", "C++"]
|
||||
},
|
||||
{
|
||||
"name": "RegisterInfo",
|
||||
"tblgen_arg": "--gen-register-info",
|
||||
"inc_name": "RegisterInfo",
|
||||
"only_arch": [],
|
||||
"lang": ["CCS"]
|
||||
},
|
||||
{
|
||||
"name": "InstrInfo",
|
||||
"tblgen_arg": "--gen-instr-info",
|
||||
"inc_name": "InstrInfo",
|
||||
"only_arch": [],
|
||||
"lang": ["CCS"]
|
||||
},
|
||||
{
|
||||
"name": "SubtargetInfo",
|
||||
"tblgen_arg": "--gen-subtarget",
|
||||
"inc_name": "SubtargetInfo",
|
||||
"only_arch": [],
|
||||
"lang": ["CCS"]
|
||||
},
|
||||
{
|
||||
"name": "Mapping",
|
||||
"tblgen_arg": "--gen-asm-matcher",
|
||||
"inc_name": "",
|
||||
"only_arch": [],
|
||||
"lang": ["CCS"]
|
||||
},
|
||||
{
|
||||
"name": "SystemOperand",
|
||||
"tblgen_arg": "--gen-searchable-tables",
|
||||
"inc_name": "",
|
||||
"only_arch": ["AArch64", "ARM", "Sparc"],
|
||||
"lang": ["CCS"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<!--
|
||||
Copyright © 2024 Rot127 <unisono@quyllur.org>
|
||||
SPDX-License-Identifier: BSD-3
|
||||
-->
|
||||
|
||||
lit configurations for MC regression test generation.
|
||||
As an introduction see: https://medium.com/@mshockwave/using-llvm-lit-out-of-tree-5cddada85a78 ([archived](https://web.archive.org/web/20240421091240/https://medium.com/@mshockwave/using-llvm-lit-out-of-tree-5cddada85a78))
|
||||
@@ -0,0 +1,14 @@
|
||||
# Copyright © 2024 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from autosync.PathVarHandler import PathVarHandler
|
||||
import lit.formats
|
||||
|
||||
config.name = "Generate Capstone MC regression tests"
|
||||
config.test_format = lit.formats.ShTest(True)
|
||||
|
||||
config.suffixes = [".txt", ".s"]
|
||||
|
||||
config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt"]
|
||||
|
||||
config.test_source_root = PathVarHandler().get_path("{LLVM_LIT_TEST_DIR}")
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright © 2024 Rot127 <unisono@quyllur.org>
|
||||
# SPDX-License-Identifier: BSD-3
|
||||
|
||||
from autosync.Targets import TARGETS_LLVM_NAMING
|
||||
from autosync.PathVarHandler import PathVarHandler
|
||||
from pathlib import Path
|
||||
|
||||
import lit.llvm
|
||||
|
||||
lit.llvm.initialize(lit_config, config)
|
||||
|
||||
config.llvm_src_root = str(PathVarHandler().get_path("{LLVM_ROOT}").absolute())
|
||||
config.root.targets = " ".join(TARGETS_LLVM_NAMING)
|
||||
|
||||
lit_cfg_dir = PathVarHandler().get_path("{LLVM_LIT_TEST_DIR}")
|
||||
lit_config.load_config(config, lit_cfg_dir.joinpath("lit.cfg.py"))
|
||||
@@ -0,0 +1,151 @@
|
||||
{
|
||||
"use_assembly_tests": [
|
||||
"Xtensa",
|
||||
"Sparc"
|
||||
],
|
||||
"exclude_disassembly_tests": [
|
||||
"Xtensa"
|
||||
],
|
||||
"unify_test_cases": [
|
||||
"ARM"
|
||||
],
|
||||
"additional_mattr":
|
||||
{
|
||||
"AArch64":
|
||||
[
|
||||
"+all"
|
||||
]
|
||||
},
|
||||
"mandatory_options":
|
||||
{
|
||||
"Mips":
|
||||
[
|
||||
"CS_OPT_SYNTAX_NOREGNAME"
|
||||
],
|
||||
"PPC":
|
||||
[
|
||||
"CS_OPT_ONLY_OFFSET_BRANCH",
|
||||
"CS_OPT_SYNTAX_NOREGNAME"
|
||||
|
||||
],
|
||||
"ARM":
|
||||
[
|
||||
"CS_OPT_ONLY_OFFSET_BRANCH"
|
||||
],
|
||||
"AArch64":
|
||||
[
|
||||
"CS_OPT_ONLY_OFFSET_BRANCH"
|
||||
]
|
||||
},
|
||||
"default_endianess":
|
||||
{
|
||||
"SystemZ": "CS_MODE_BIG_ENDIAN",
|
||||
"PPC": "CS_MODE_BIG_ENDIAN",
|
||||
"Sparc": "CS_MODE_BIG_ENDIAN"
|
||||
},
|
||||
"remove_options":
|
||||
{
|
||||
"Mips":
|
||||
[
|
||||
"mips",
|
||||
"dsp",
|
||||
"dspr2",
|
||||
"dspr3",
|
||||
"mips3d",
|
||||
"msa",
|
||||
"eva",
|
||||
"crc",
|
||||
"virt",
|
||||
"ginv",
|
||||
"fp64",
|
||||
"+virt",
|
||||
"mt"
|
||||
],
|
||||
"PPC": [
|
||||
"powerpc64-unknown-linux-gnu"
|
||||
]
|
||||
},
|
||||
"replace_option_map":
|
||||
{
|
||||
"Mips":
|
||||
{
|
||||
"mips-unknown-linux": ["CS_MODE_BIG_ENDIAN"],
|
||||
"mips-unknown-linux-gnu": ["CS_MODE_BIG_ENDIAN"],
|
||||
"mips32-unknown-linux": ["CS_MODE_BIG_ENDIAN"],
|
||||
"mips64-unknown-linux": ["CS_MODE_BIG_ENDIAN"],
|
||||
"mips64-unknown-linux-gnu": ["CS_MODE_BIG_ENDIAN"],
|
||||
"mips64el-unknown-linux": ["CS_MODE_LITTLE_ENDIAN"],
|
||||
"mips64el-unknown-linux-gnu": ["CS_MODE_LITTLE_ENDIAN"],
|
||||
"mipsel": ["CS_MODE_LITTLE_ENDIAN"],
|
||||
"mipsel-unknown-linux": ["CS_MODE_LITTLE_ENDIAN"],
|
||||
"mipsel-unknown-linux-gnu": ["CS_MODE_LITTLE_ENDIAN"],
|
||||
"mips16": ["CS_MODE_MIPS16"],
|
||||
"mips32": ["CS_MODE_MIPS32"],
|
||||
"mips64": ["CS_MODE_MIPS64"],
|
||||
"micromips": ["CS_MODE_MICRO"],
|
||||
"mips1": ["CS_MODE_MIPS1"],
|
||||
"mips2": ["CS_MODE_MIPS2"],
|
||||
"mips32r2": ["CS_MODE_MIPS32R2"],
|
||||
"mips32r3": ["CS_MODE_MIPS32R3"],
|
||||
"mips32r5": ["CS_MODE_MIPS32R5"],
|
||||
"mips32r6": ["CS_MODE_MIPS32R6"],
|
||||
"mips3": ["CS_MODE_MIPS3"],
|
||||
"mips4": ["CS_MODE_MIPS4"],
|
||||
"mips5": ["CS_MODE_MIPS5"],
|
||||
"mips64r2": ["CS_MODE_MIPS64R2"],
|
||||
"mips64r3": ["CS_MODE_MIPS64R3"],
|
||||
"mips64r5": ["CS_MODE_MIPS64R5"],
|
||||
"mips64r6": ["CS_MODE_MIPS64R6"],
|
||||
"octeon": ["CS_MODE_OCTEON"],
|
||||
"octeon+": ["CS_MODE_OCTEONP"],
|
||||
"nanomips": ["CS_MODE_NANOMIPS"],
|
||||
"nms1": ["CS_MODE_NMS1"],
|
||||
"i7200": ["CS_MODE_I7200"],
|
||||
"mips_nofloat": ["CS_MODE_MIPS_NOFLOAT"],
|
||||
"mips_ptr64": ["CS_MODE_MIPS_PTR64"]
|
||||
},
|
||||
"SystemZ": {
|
||||
"arch8": ["CS_MODE_SYSTEMZ_ARCH8"],
|
||||
"arch9": ["CS_MODE_SYSTEMZ_ARCH9"],
|
||||
"arch10": ["CS_MODE_SYSTEMZ_ARCH10"],
|
||||
"arch11": ["CS_MODE_SYSTEMZ_ARCH11"],
|
||||
"arch12": ["CS_MODE_SYSTEMZ_ARCH12"],
|
||||
"arch13": ["CS_MODE_SYSTEMZ_ARCH13"],
|
||||
"arch14": ["CS_MODE_SYSTEMZ_ARCH14"],
|
||||
"z10": ["CS_MODE_SYSTEMZ_Z10"],
|
||||
"z196": ["CS_MODE_SYSTEMZ_Z196"],
|
||||
"zec12": ["CS_MODE_SYSTEMZ_ZEC12"],
|
||||
"z13": ["CS_MODE_SYSTEMZ_Z13"],
|
||||
"z14": ["CS_MODE_SYSTEMZ_Z14"],
|
||||
"z15": ["CS_MODE_SYSTEMZ_Z15"],
|
||||
"z16": ["CS_MODE_SYSTEMZ_Z16"],
|
||||
"generic": ["CS_MODE_SYSTEMZ_GENERIC"]
|
||||
},
|
||||
"PPC": {
|
||||
"powerpc64-unknown-unknown": ["CS_MODE_BIG_ENDIAN", "CS_MODE_64"],
|
||||
"powerpc64-unknown-linux": ["CS_MODE_BIG_ENDIAN", "CS_MODE_64"],
|
||||
"powerpc64-unknown-aix-gnu": ["CS_MODE_BIG_ENDIAN", "CS_MODE_64", "CS_MODE_AIX_OS"],
|
||||
"powerpc64le-unknown-unknown": ["CS_MODE_LITTLE_ENDIAN", "CS_MODE_64"],
|
||||
"powerpc64-ibm-aix-xcoff": ["CS_MODE_BIG_ENDIAN", "CS_MODE_64", "CS_MODE_AIX_OS"],
|
||||
"powerpc-unknown-aix-gnu": ["CS_MODE_BIG_ENDIAN", "CS_MODE_32", "CS_MODE_AIX_OS"],
|
||||
"pwr7": ["CS_MODE_PWR7"],
|
||||
"pwr8": ["CS_MODE_PWR8"],
|
||||
"pwr9": ["CS_MODE_PWR9"],
|
||||
"pwr10": ["CS_MODE_PWR10"],
|
||||
"pwr10": ["CS_MODE_PWR10"],
|
||||
"future": ["CS_MODE_PPC_ISA_FUTURE"],
|
||||
"modern-aix-as": ["CS_MODE_MODERN_AIX_AS"],
|
||||
"spe": ["CS_MODE_SPE"],
|
||||
"a2": ["CS_MODE_BOOKE"]
|
||||
},
|
||||
"Sparc": {
|
||||
"sparc": ["CS_MODE_BIG_ENDIAN"],
|
||||
"sparc-unknown-linux": ["CS_MODE_BIG_ENDIAN"],
|
||||
"sparc64-linux-gnu": ["CS_MODE_V9"],
|
||||
"sparc64-unknown-linux-gnu": ["CS_MODE_V9"],
|
||||
"sparcv9": ["CS_MODE_V9"],
|
||||
"sparcv9-unknown-linux": ["CS_MODE_V9"],
|
||||
"sparcel-linux-gnu": ["CS_MODE_LITTLE_ENDIAN"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"paths": {
|
||||
"{LLVM_ROOT}": "{AUTO_SYNC_ROOT}/vendor/llvm_root/",
|
||||
"{LLVM_TARGET_DIR}": "{LLVM_ROOT}/llvm/lib/Target/",
|
||||
"{LLVM_MC_TEST_DIR}": "{LLVM_ROOT}/llvm/test/MC/",
|
||||
"{LLVM_TBLGEN_BIN}": "{LLVM_ROOT}/build/bin/llvm-tblgen",
|
||||
"{LLVM_LIT_TEST_DIR}": "{AUTO_SYNC_SRC}/lit_config/",
|
||||
"{LLVM_INCLUDE_DIR}": "{LLVM_ROOT}/llvm/include/",
|
||||
"{VENDOR_DIR}": "{AUTO_SYNC_ROOT}/vendor/",
|
||||
"{BUILD_DIR}": "{AUTO_SYNC_ROOT}/build/",
|
||||
"{C_INC_OUT_DIR}": "{BUILD_DIR}/llvm_c_inc/",
|
||||
"{INC_GEN_CONF_FILE}": "{AUTO_SYNC_SRC}/inc_gen.json",
|
||||
"{CPP_INC_OUT_DIR}": "{BUILD_DIR}/llvm_cpp_inc/",
|
||||
"{CPP_TRANSLATOR_DIR}": "{AUTO_SYNC_SRC}/cpptranslator/",
|
||||
"{CPP_TRANSLATOR_CONFIG}": "{CPP_TRANSLATOR_DIR}/arch_config.json",
|
||||
"{CPP_TRANSLATOR_TEST_DIR}": "{CPP_TRANSLATOR_DIR}/Tests/",
|
||||
"{PATCHES_TEST_DIR}": "{CPP_TRANSLATOR_TEST_DIR}/Patches/",
|
||||
"{PATCHES_TEST_CONFIG}": "{PATCHES_TEST_DIR}/test_arch_config.json",
|
||||
"{DIFFER_PERSISTENCE_FILE}": "{CPP_TRANSLATOR_DIR}/saved_patches.json",
|
||||
"{CPP_TRANSLATOR_TRANSLATION_OUT_DIR}": "{BUILD_DIR}/translate_out/",
|
||||
"{CPP_TRANSLATOR_DIFF_OUT_DIR}": "{BUILD_DIR}/diff_out/",
|
||||
"{INC_PATCH_DIR}": "{AUTO_SYNC_ROOT}/inc_patches/",
|
||||
"{CS_INCLUDE_DIR}": "{CS_ROOT}/include/capstone/",
|
||||
"{CS_ARCH_MODULE_DIR}": "{CS_ROOT}/arch/",
|
||||
"{CS_CLANG_FORMAT_FILE}": "{CS_ROOT}/.clang-format",
|
||||
"{HEADER_PATCHER_TEST_HEADER_FILE}": "{AUTO_SYNC_SRC}/Tests/test_header.h",
|
||||
"{HEADER_PATCHER_TEST_INC_FILE}": "{AUTO_SYNC_SRC}/Tests/test_include.inc",
|
||||
"{HEADER_GEN_TEST_AARCH64_FILE}": "{AUTO_SYNC_SRC}/Tests/test_aarch64_header.h",
|
||||
"{HEADER_GEN_TEST_ARM64_FILE}": "{AUTO_SYNC_SRC}/Tests/test_arm64_header.h",
|
||||
"{HEADER_GEN_TEST_ARM64_OUT_FILE}": "{AUTO_SYNC_SRC}/Tests/test_arm64_header.h.out",
|
||||
"{HEADER_GEN_TEST_SYSTEMZ_FILE}": "{AUTO_SYNC_SRC}/Tests/test_systemz_header.h",
|
||||
"{HEADER_GEN_TEST_SYSZ_FILE}": "{AUTO_SYNC_SRC}/Tests/test_sysz_header.h",
|
||||
"{HEADER_GEN_TEST_SYSZ_OUT_FILE}": "{AUTO_SYNC_SRC}/Tests/test_sysz_header.h.out",
|
||||
"{DIFFER_TEST_DIR}": "{CPP_TRANSLATOR_TEST_DIR}/Differ/",
|
||||
"{DIFFER_TEST_CONFIG_FILE}": "{DIFFER_TEST_DIR}/test_arch_config.json",
|
||||
"{DIFFER_TEST_OLD_SRC_DIR}": "{DIFFER_TEST_DIR}/old_src/",
|
||||
"{DIFFER_TEST_NEW_SRC_DIR}": "{DIFFER_TEST_DIR}/new_src/",
|
||||
"{DIFFER_TEST_OUTPUT_DIR}": "{DIFFER_TEST_DIR}/output/",
|
||||
"{DIFFER_TEST_EXPECTED_DIR}": "{DIFFER_TEST_DIR}/expected/",
|
||||
"{DIFFER_TEST_PERSISTENCE_FILE}": "{DIFFER_TEST_DIR}/test_saved_patches.json",
|
||||
"{AUTO_SYNC_TEST_DIR}": "{AUTO_SYNC_SRC}/Tests/",
|
||||
"{MCUPDATER_CONFIG_FILE}": "{AUTO_SYNC_SRC}/mcupdater.json",
|
||||
"{MCUPDATER_TEST_DIR}": "{AUTO_SYNC_TEST_DIR}/MCUpdaterTests/",
|
||||
"{MCUPDATER_TEST_DIR_EXPECTED}": "{AUTO_SYNC_TEST_DIR}/MCUpdaterTests/expected",
|
||||
"{MCUPDATER_OUT_DIR}": "{BUILD_DIR}/mc_out/",
|
||||
"{MCUPDATER_OUT_FUZZ_DIR}": "{BUILD_DIR}/mc_out_fuzz/",
|
||||
"{MCUPDATER_TEST_OUT_DIR}": "{MCUPDATER_TEST_DIR}/test_output/",
|
||||
"{MCUPDATER_TEST_OUT_FUZZ_DIR}": "{MCUPDATER_TEST_DIR}/test_output_fuzz/",
|
||||
"{MC_DIR}": "{CS_ROOT}/tests/MC/",
|
||||
"{LEGACY_MC_DIR}": "{CS_ROOT}/suite/MC/"
|
||||
},
|
||||
"create_during_runtime": [
|
||||
"{BUILD_DIR}",
|
||||
"{C_INC_OUT_DIR}",
|
||||
"{CPP_INC_OUT_DIR}",
|
||||
"{CPP_TRANSLATOR_TRANSLATION_OUT_DIR}",
|
||||
"{CPP_TRANSLATOR_DIFF_OUT_DIR}",
|
||||
"{HEADER_GEN_TEST_ARM64_OUT_FILE}",
|
||||
"{HEADER_GEN_TEST_SYSZ_OUT_FILE}",
|
||||
"{MCUPDATER_OUT_DIR}",
|
||||
"{MCUPDATER_TEST_OUT_DIR}",
|
||||
"{MCUPDATER_OUT_FUZZ_DIR}",
|
||||
"{MCUPDATER_TEST_OUT_FUZZ_DIR}"
|
||||
],
|
||||
"ignore_missing": [
|
||||
"{DIFFER_TEST_PERSISTENCE_FILE}"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user