150 lines
5.9 KiB
Python
150 lines
5.9 KiB
Python
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import subprocess
|
|
|
|
|
|
def run_command(command, cwd=None):
|
|
p = subprocess.Popen(command, cwd=cwd)
|
|
if p.wait() != 0:
|
|
print("command `{}` failed...".format(" ".join(command)))
|
|
sys.exit(1)
|
|
|
|
|
|
def clone_repository(repo_name, path, repo_url, sub_paths):
|
|
if os.path.exists(path):
|
|
while True:
|
|
choice = input("There is already a `{}` folder, do you want to update it? [y/N]".format(path))
|
|
if choice == "" or choice.lower() == "n":
|
|
print("Skipping repository update.")
|
|
return
|
|
elif choice.lower() == "y":
|
|
print("Updating repository...")
|
|
run_command(["git", "pull", "origin", "main"], cwd=path)
|
|
return
|
|
else:
|
|
print("Didn't understand answer...")
|
|
print("Cloning {} repository...".format(repo_name))
|
|
run_command(["git", "clone", repo_url, "--filter=tree:0", "--no-checkout", path])
|
|
run_command(["git", "sparse-checkout", "init"], cwd=path)
|
|
run_command(["git", "sparse-checkout", "set", *sub_paths], cwd=path)
|
|
run_command(["git", "checkout"], cwd=path)
|
|
|
|
|
|
def append_intrinsic(array, intrinsic_name, translation):
|
|
array.append((intrinsic_name, translation))
|
|
|
|
|
|
def convert_to_string(content):
|
|
if content.__class__.__name__ == 'bytes':
|
|
return content.decode('utf-8')
|
|
return content
|
|
|
|
|
|
def extract_intrinsics_from_llvm(llvm_path):
|
|
intrinsics = {}
|
|
command = ["llvm-tblgen", "llvm/IR/Intrinsics.td", "--dump-json"]
|
|
cwd = os.path.join(llvm_path, "llvm/include")
|
|
print("=> Running command `{}` from `{}`".format(command, cwd))
|
|
p = subprocess.Popen(command, cwd=cwd, stdout=subprocess.PIPE)
|
|
output, err = p.communicate()
|
|
content = json.loads(convert_to_string(output))
|
|
for intrinsic in content:
|
|
data = content[intrinsic]
|
|
if not isinstance(data, dict):
|
|
continue
|
|
current_arch = data.get("TargetPrefix")
|
|
builtin_name = data.get("ClangBuiltinName")
|
|
if current_arch is None or current_arch == "" or builtin_name is None:
|
|
continue
|
|
intrinsic = intrinsic.split("_")
|
|
if len(intrinsic) < 2 or intrinsic[0] != "int":
|
|
continue
|
|
intrinsic[0] = "llvm"
|
|
intrinsic = ".".join(intrinsic)
|
|
if current_arch not in intrinsics:
|
|
intrinsics[current_arch] = []
|
|
append_intrinsic(intrinsics[current_arch], intrinsic, builtin_name)
|
|
|
|
return intrinsics
|
|
|
|
|
|
def update_intrinsics(llvm_path):
|
|
intrinsics = extract_intrinsics_from_llvm(llvm_path)
|
|
|
|
archs = [arch for arch in intrinsics]
|
|
archs.sort()
|
|
|
|
output_file = os.path.join(
|
|
os.path.dirname(os.path.abspath(__file__)),
|
|
"../src/intrinsic/archs.rs",
|
|
)
|
|
# A hashmap of all architectures. This allows us to first match on the architecture, and then on the intrinsics.
|
|
# This speeds up the comparison, and makes our code considerably smaller.
|
|
# Since all intrinsic names start with "llvm.", we skip that prefix.
|
|
print("Updating content of `{}`...".format(output_file))
|
|
with open(output_file, "w", encoding="utf8") as out:
|
|
out.write("""// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`
|
|
// DO NOT EDIT IT!
|
|
/// Translate a given LLVM intrinsic name to an equivalent GCC one.
|
|
fn map_arch_intrinsic(full_name:&str)-> &'static str {
|
|
let Some(name) = full_name.strip_prefix("llvm.") else { unimplemented!("***** unsupported LLVM intrinsic {}", full_name) };
|
|
let Some((arch, name)) = name.split_once('.') else { unimplemented!("***** unsupported LLVM intrinsic llvm.{}", name) };
|
|
let old_arch_res = old_archs(arch, name);
|
|
if let ArchCheckResult::Ok(res) = old_arch_res {
|
|
return res;
|
|
}
|
|
match arch {""")
|
|
for arch in archs:
|
|
if len(intrinsics[arch]) == 0:
|
|
continue
|
|
attribute = "#[expect(non_snake_case)]" if arch[0].isupper() else ""
|
|
out.write("\"{}\" => {{ {} fn {}(name: &str,full_name:&str) -> &'static str {{ match name {{".format(arch, attribute, arch))
|
|
intrinsics[arch].sort(key=lambda x: (x[0], x[1]))
|
|
out.write(' // {}\n'.format(arch))
|
|
for entry in intrinsics[arch]:
|
|
llvm_name = entry[0].removeprefix("llvm.");
|
|
llvm_name = llvm_name.removeprefix(arch);
|
|
llvm_name = llvm_name.removeprefix(".");
|
|
if "_round_mask" in entry[1]:
|
|
out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(llvm_name, entry[1]))
|
|
else:
|
|
out.write(' "{}" => "{}",\n'.format(llvm_name, entry[1]))
|
|
out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"),\n')
|
|
out.write("}} }} {}(name,full_name) }}\n,".format(arch))
|
|
out.write(""" _ => {
|
|
match old_arch_res {
|
|
ArchCheckResult::UnknownIntrinsic => unimplemented!("***** unsupported LLVM intrinsic {full_name}"),
|
|
ArchCheckResult::UnknownArch => unimplemented!("***** unsupported LLVM architecture {arch}, intrinsic: {full_name}"),
|
|
ArchCheckResult::Ok(_) => unreachable!(),
|
|
}
|
|
}""")
|
|
out.write("}\n}")
|
|
subprocess.call(["rustfmt", output_file])
|
|
print("Done!")
|
|
|
|
|
|
def main():
|
|
llvm_path = os.path.join(
|
|
os.path.dirname(os.path.abspath(__file__)),
|
|
"llvm-project",
|
|
)
|
|
|
|
# First, we clone the LLVM repository if it's not already here.
|
|
clone_repository(
|
|
"llvm-project",
|
|
llvm_path,
|
|
"https://github.com/llvm/llvm-project",
|
|
["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"],
|
|
)
|
|
update_intrinsics(llvm_path)
|
|
|
|
# llvm-tblgen can be built with:
|
|
#
|
|
# mkdir llvm-tblgen-build && cd llvm-tblgen-build
|
|
# cmake -G Ninja -DLLVM_ENABLE_PROJECTS="llvm" -DCMAKE_BUILD_TYPE=Release ../llvm
|
|
# ninja llvm-tblgen
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|