blob: 5390323407779d9287121dd0cfd771bc32448c8a [file] [log] [blame]
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())