d6aadc684b
Prevent invalid intrinsics conversion generation
230 lines
7.7 KiB
Python
230 lines
7.7 KiB
Python
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import subprocess
|
|
from os import walk
|
|
|
|
|
|
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):
|
|
p = subprocess.Popen(
|
|
["llvm-tblgen", "llvm/IR/Intrinsics.td"],
|
|
cwd=os.path.join(llvm_path, "llvm/include"),
|
|
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] == 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())
|