| 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()) |