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