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=None): 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"], cwd=path) return else: print("Didn't understand answer...") print("Cloning {} repository...".format(repo_name)) if sub_paths is None: run_command(["git", "clone", repo_url, "--depth", "1", path]) else: 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_instrinsics_from_llvm(llvm_path, intrinsics): p = subprocess.Popen( ["llvm-tblgen", "llvm/IR/Intrinsics.td"], cwd=os.path.join(llvm_path, "llvm/include"), stdout=subprocess.PIPE) output, err = p.communicate() lines = convert_to_string(output).splitlines() pos = 0 while pos < len(lines): line = lines[pos] if not line.startswith("def "): pos += 1 continue intrinsic = line.split(" ")[1].strip() content = line while pos < len(lines): line = lines[pos].split(" // ")[0].strip() content += line pos += 1 if line == "}": break entries = re.findall('string ClangBuiltinName = "(\\w+)";', content) current_arch = re.findall('string TargetPrefix = "(\\w+)";', content) if len(entries) == 1 and len(current_arch) == 1: current_arch = current_arch[0] 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, entries[0]) def append_translation(json_data, p, array): it = json_data["index"][p] content = it["docs"].split('`') if len(content) != 5: return append_intrinsic(array, content[1], content[3]) def extract_instrinsics_from_llvmint(llvmint, intrinsics): archs = [ "AMDGPU", "aarch64", "arm", "cuda", "hexagon", "mips", "nvvm", "ppc", "ptx", "x86", "xcore", ] json_file = os.path.join(llvmint, "target/doc/llvmint.json") # We need to regenerate the documentation! run_command( ["cargo", "rustdoc", "--", "-Zunstable-options", "--output-format", "json"], cwd=llvmint, ) with open(json_file, "r", encoding="utf8") as f: json_data = json.loads(f.read()) for p in json_data["paths"]: it = json_data["paths"][p] if it["crate_id"] != 0: # This is from an external crate. continue if it["kind"] != "function": # We're only looking for functions. continue # if len(it["path"]) == 2: # # This is a "general" intrinsic, not bound to a specific arch. # append_translation(json_data, p, general) # continue if len(it["path"]) != 3 or it["path"][1] not in archs: continue arch = it["path"][1] if arch not in intrinsics: intrinsics[arch] = [] append_translation(json_data, p, intrinsics[arch]) def fill_intrinsics(intrinsics, from_intrinsics, all_intrinsics): for arch in from_intrinsics: if arch not in intrinsics: intrinsics[arch] = [] for entry in from_intrinsics[arch]: if entry[0] in all_intrinsics: if all_intrinsics[entry[0]] == entry[1]: # This is a "full" duplicate, both the LLVM instruction and the GCC # translation are the same. continue intrinsics[arch].append((entry[0], entry[1], True)) else: intrinsics[arch].append((entry[0], entry[1], False)) all_intrinsics[entry[0]] = entry[1] def update_intrinsics(llvm_path, llvmint, llvmint2): intrinsics_llvm = {} intrinsics_llvmint = {} all_intrinsics = {} extract_instrinsics_from_llvm(llvm_path, intrinsics_llvm) extract_instrinsics_from_llvmint(llvmint, intrinsics_llvmint) extract_instrinsics_from_llvmint(llvmint2, intrinsics_llvmint) intrinsics = {} # We give priority to translations from LLVM over the ones from llvmint. fill_intrinsics(intrinsics, intrinsics_llvm, all_intrinsics) fill_intrinsics(intrinsics, intrinsics_llvmint, all_intrinsics) archs = [arch for arch in intrinsics] archs.sort() output_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "../src/intrinsic/archs.rs", ) 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`\n") out.write("// DO NOT EDIT IT!\n") out.write("match name {\n") for arch in archs: if len(intrinsics[arch]) == 0: continue intrinsics[arch].sort(key=lambda x: (x[0], x[2])) out.write(' // {}\n'.format(arch)) for entry in intrinsics[arch]: if entry[2] is True: # if it is a duplicate out.write(' // [DUPLICATE]: "{}" => "{}",\n'.format(entry[0], entry[1])) elif "_round_mask" in entry[1]: out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(entry[0], entry[1])) else: out.write(' "{}" => "{}",\n'.format(entry[0], entry[1])) out.write(' _ => unimplemented!("***** unsupported LLVM intrinsic {}", name),\n') out.write("}\n") print("Done!") def main(): llvm_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "llvm-project", ) llvmint_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "llvmint", ) llvmint2_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "llvmint-2", ) # First, we clone the LLVM repository if it's not already here. clone_repository( "llvm-project", llvm_path, "https://github.com/llvm/llvm-project", sub_paths=["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"], ) clone_repository( "llvmint", llvmint_path, "https://github.com/GuillaumeGomez/llvmint", ) clone_repository( "llvmint2", llvmint2_path, "https://github.com/antoyo/llvmint", ) update_intrinsics(llvm_path, llvmint_path, llvmint2_path) if __name__ == "__main__": sys.exit(main())