mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-30 20:44:34 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			229 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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_intrinsics_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_intrinsics_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_intrinsics_from_llvm(llvm_path, intrinsics_llvm)
 | |
|     extract_intrinsics_from_llvmint(llvmint, intrinsics_llvmint)
 | |
|     extract_intrinsics_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())
 | 
