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