• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import json
2import os
3import re
4import sys
5import subprocess
6
7
8def run_command(command, cwd=None):
9    p = subprocess.Popen(command, cwd=cwd)
10    if p.wait() != 0:
11        print("command `{}` failed...".format(" ".join(command)))
12        sys.exit(1)
13
14
15def clone_repository(repo_name, path, repo_url, sub_paths=None):
16    if os.path.exists(path):
17        while True:
18            choice = input("There is already a `{}` folder, do you want to update it? [y/N]".format(path))
19            if choice == "" or choice.lower() == "n":
20                print("Skipping repository update.")
21                return
22            elif choice.lower() == "y":
23                print("Updating repository...")
24                run_command(["git", "pull", "origin"], cwd=path)
25                return
26            else:
27                print("Didn't understand answer...")
28    print("Cloning {} repository...".format(repo_name))
29    if sub_paths is None:
30        run_command(["git", "clone", repo_url, "--depth", "1", path])
31    else:
32        run_command(["git", "clone", repo_url, "--filter=tree:0", "--no-checkout", path])
33        run_command(["git", "sparse-checkout", "init"], cwd=path)
34        run_command(["git", "sparse-checkout", "set", *sub_paths], cwd=path)
35        run_command(["git", "checkout"], cwd=path)
36
37
38def append_intrinsic(array, intrinsic_name, translation):
39    array.append((intrinsic_name, translation))
40
41
42def convert_to_string(content):
43    if content.__class__.__name__ == 'bytes':
44        return content.decode('utf-8')
45    return content
46
47
48def extract_instrinsics_from_llvm(llvm_path, intrinsics):
49    p = subprocess.Popen(
50        ["llvm-tblgen", "llvm/IR/Intrinsics.td"],
51        cwd=os.path.join(llvm_path, "llvm/include"),
52        stdout=subprocess.PIPE)
53    output, err = p.communicate()
54    lines = convert_to_string(output).splitlines()
55    pos = 0
56    while pos < len(lines):
57        line = lines[pos]
58        if not line.startswith("def "):
59            pos += 1
60            continue
61        intrinsic = line.split(" ")[1].strip()
62        content = line
63        while pos < len(lines):
64            line = lines[pos].split(" // ")[0].strip()
65            content += line
66            pos += 1
67            if line == "}":
68                break
69        entries = re.findall('string ClangBuiltinName = "(\\w+)";', content)
70        current_arch = re.findall('string TargetPrefix = "(\\w+)";', content)
71        if len(entries) == 1 and len(current_arch) == 1:
72            current_arch = current_arch[0]
73            intrinsic = intrinsic.split("_")
74            if len(intrinsic) < 2 or intrinsic[0] != "int":
75                continue
76            intrinsic[0] = "llvm"
77            intrinsic = ".".join(intrinsic)
78            if current_arch not in intrinsics:
79                intrinsics[current_arch] = []
80            append_intrinsic(intrinsics[current_arch], intrinsic, entries[0])
81
82
83def append_translation(json_data, p, array):
84    it = json_data["index"][p]
85    content = it["docs"].split('`')
86    if len(content) != 5:
87        return
88    append_intrinsic(array, content[1], content[3])
89
90
91def extract_instrinsics_from_llvmint(llvmint, intrinsics):
92    archs = [
93        "AMDGPU",
94        "aarch64",
95        "arm",
96        "cuda",
97        "hexagon",
98        "mips",
99        "nvvm",
100        "ppc",
101        "ptx",
102        "x86",
103        "xcore",
104    ]
105
106    json_file = os.path.join(llvmint, "target/doc/llvmint.json")
107    # We need to regenerate the documentation!
108    run_command(
109        ["cargo", "rustdoc", "--", "-Zunstable-options", "--output-format", "json"],
110        cwd=llvmint,
111    )
112    with open(json_file, "r", encoding="utf8") as f:
113        json_data = json.loads(f.read())
114    for p in json_data["paths"]:
115        it = json_data["paths"][p]
116        if it["crate_id"] != 0:
117            # This is from an external crate.
118            continue
119        if it["kind"] != "function":
120            # We're only looking for functions.
121            continue
122        # if len(it["path"]) == 2:
123        #   # This is a "general" intrinsic, not bound to a specific arch.
124        #   append_translation(json_data, p, general)
125        #   continue
126        if len(it["path"]) != 3 or it["path"][1] not in archs:
127            continue
128        arch = it["path"][1]
129        if arch not in intrinsics:
130            intrinsics[arch] = []
131        append_translation(json_data, p, intrinsics[arch])
132
133
134def fill_intrinsics(intrinsics, from_intrinsics, all_intrinsics):
135    for arch in from_intrinsics:
136        if arch not in intrinsics:
137            intrinsics[arch] = []
138        for entry in from_intrinsics[arch]:
139            if entry[0] in all_intrinsics:
140                if all_intrinsics[entry[0]] == entry[1]:
141                    # This is a "full" duplicate, both the LLVM instruction and the GCC
142                    # translation are the same.
143                    continue
144                intrinsics[arch].append((entry[0], entry[1], True))
145            else:
146                intrinsics[arch].append((entry[0], entry[1], False))
147                all_intrinsics[entry[0]] = entry[1]
148
149
150def update_intrinsics(llvm_path, llvmint, llvmint2):
151    intrinsics_llvm = {}
152    intrinsics_llvmint = {}
153    all_intrinsics = {}
154
155    extract_instrinsics_from_llvm(llvm_path, intrinsics_llvm)
156    extract_instrinsics_from_llvmint(llvmint, intrinsics_llvmint)
157    extract_instrinsics_from_llvmint(llvmint2, intrinsics_llvmint)
158
159    intrinsics = {}
160    # We give priority to translations from LLVM over the ones from llvmint.
161    fill_intrinsics(intrinsics, intrinsics_llvm, all_intrinsics)
162    fill_intrinsics(intrinsics, intrinsics_llvmint, all_intrinsics)
163
164    archs = [arch for arch in intrinsics]
165    archs.sort()
166
167    output_file = os.path.join(
168        os.path.dirname(os.path.abspath(__file__)),
169        "../src/intrinsic/archs.rs",
170    )
171    print("Updating content of `{}`...".format(output_file))
172    with open(output_file, "w", encoding="utf8") as out:
173        out.write("// File generated by `rustc_codegen_gcc/tools/generate_intrinsics.py`\n")
174        out.write("// DO NOT EDIT IT!\n")
175        out.write("match name {\n")
176        for arch in archs:
177            if len(intrinsics[arch]) == 0:
178                continue
179            intrinsics[arch].sort(key=lambda x: (x[0], x[2]))
180            out.write('    // {}\n'.format(arch))
181            for entry in intrinsics[arch]:
182                if entry[2] is True: # if it is a duplicate
183                    out.write('    // [DUPLICATE]: "{}" => "{}",\n'.format(entry[0], entry[1]))
184                elif "_round_mask" in entry[1]:
185                    out.write('    // [INVALID CONVERSION]: "{}" => "{}",\n'.format(entry[0], entry[1]))
186                else:
187                    out.write('    "{}" => "{}",\n'.format(entry[0], entry[1]))
188        out.write('    _ => unimplemented!("***** unsupported LLVM intrinsic {}", name),\n')
189        out.write("}\n")
190    print("Done!")
191
192
193def main():
194    llvm_path = os.path.join(
195        os.path.dirname(os.path.abspath(__file__)),
196        "llvm-project",
197    )
198    llvmint_path = os.path.join(
199        os.path.dirname(os.path.abspath(__file__)),
200        "llvmint",
201    )
202    llvmint2_path = os.path.join(
203        os.path.dirname(os.path.abspath(__file__)),
204        "llvmint-2",
205    )
206
207    # First, we clone the LLVM repository if it's not already here.
208    clone_repository(
209        "llvm-project",
210        llvm_path,
211        "https://github.com/llvm/llvm-project",
212        sub_paths=["llvm/include/llvm/IR", "llvm/include/llvm/CodeGen/"],
213    )
214    clone_repository(
215        "llvmint",
216        llvmint_path,
217        "https://github.com/GuillaumeGomez/llvmint",
218    )
219    clone_repository(
220        "llvmint2",
221        llvmint2_path,
222        "https://github.com/antoyo/llvmint",
223    )
224    update_intrinsics(llvm_path, llvmint_path, llvmint2_path)
225
226
227if __name__ == "__main__":
228    sys.exit(main())
229