about summary refs log tree commit diff
path: root/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_gcc/tools/generate_intrinsics.py')
-rw-r--r--compiler/rustc_codegen_gcc/tools/generate_intrinsics.py228
1 files changed, 228 insertions, 0 deletions
diff --git a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
new file mode 100644
index 00000000000..90fb7bfad27
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
@@ -0,0 +1,228 @@
+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):
+    command = ["llvm-tblgen", "llvm/IR/Intrinsics.td"]
+    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()
+    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())