197 lines · python
1#!/usr/bin/env python32 3"""Updates FileCheck checks in MIR tests.4 5This script is a utility to update MIR based tests with new FileCheck6patterns.7 8The checks added by this script will cover the entire body of each9function it handles. Virtual registers used are given names via10FileCheck patterns, so if you do want to check a subset of the body it11should be straightforward to trim out the irrelevant parts. None of12the YAML metadata will be checked, other than function names, and fixedStack13if the --print-fixed-stack option is used.14 15If there are multiple llc commands in a test, the full set of checks16will be repeated for each different check pattern. Checks for patterns17that are common between different commands will be left as-is by18default, or removed if the --remove-common-prefixes flag is provided.19"""20 21from __future__ import print_function22 23from sys import stderr24from traceback import print_exc25import argparse26import collections27import glob28import os29import re30import subprocess31import sys32 33from UpdateTestChecks import common34from UpdateTestChecks import mir35 36 37class LLC:38 def __init__(self, bin):39 self.bin = bin40 41 def __call__(self, args, ir):42 if ir.endswith(".mir"):43 args = "{} -x mir".format(args)44 with open(ir) as ir_file:45 stdout = subprocess.check_output(46 "{} {}".format(self.bin, args), shell=True, stdin=ir_file47 )48 if sys.version_info[0] > 2:49 stdout = stdout.decode()50 # Fix line endings to unix CR style.51 stdout = stdout.replace("\r\n", "\n")52 return stdout53 54 55def log(msg, verbose=True):56 if verbose:57 print(msg, file=sys.stderr)58 59 60def find_triple_in_ir(lines, verbose=False):61 for l in lines:62 m = common.TRIPLE_IR_RE.match(l)63 if m:64 return m.group(1)65 return None66 67 68def build_run_list(test, run_lines, verbose=False):69 run_list = []70 all_prefixes = []71 for l in run_lines:72 if "|" not in l:73 common.warn("Skipping unparsable RUN line: " + l)74 continue75 76 commands = [cmd.strip() for cmd in l.split("|", 1)]77 llc_cmd = commands[0]78 filecheck_cmd = commands[1] if len(commands) > 1 else ""79 common.verify_filecheck_prefixes(filecheck_cmd)80 81 if not llc_cmd.startswith("llc "):82 common.warn("Skipping non-llc RUN line: {}".format(l), test_file=test)83 continue84 if not filecheck_cmd.startswith("FileCheck "):85 common.warn(86 "Skipping non-FileChecked RUN line: {}".format(l), test_file=test87 )88 continue89 90 triple = None91 m = common.TRIPLE_ARG_RE.search(llc_cmd)92 if m:93 triple = m.group(1)94 # If we find -march but not -mtriple, use that.95 m = common.MARCH_ARG_RE.search(llc_cmd)96 if m and not triple:97 triple = "{}--".format(m.group(1))98 99 cmd_args = llc_cmd[len("llc") :].strip()100 cmd_args = cmd_args.replace("< %s", "").replace("%s", "").strip()101 check_prefixes = common.get_check_prefixes(filecheck_cmd)102 all_prefixes += check_prefixes103 104 run_list.append((check_prefixes, cmd_args, triple))105 106 # Sort prefixes that are shared between run lines before unshared prefixes.107 # This causes us to prefer printing shared prefixes.108 for run in run_list:109 run[0].sort(key=lambda prefix: -all_prefixes.count(prefix))110 111 return run_list112 113 114def update_test_file(args, test, autogenerated_note):115 with open(test) as fd:116 input_lines = [l.rstrip() for l in fd]117 118 triple_in_ir = find_triple_in_ir(input_lines, args.verbose)119 run_lines = common.find_run_lines(test, input_lines)120 run_list = build_run_list(test, run_lines, args.verbose)121 122 func_dict = {}123 for run in run_list:124 for prefix in run[0]:125 func_dict.update({prefix: dict()})126 for prefixes, llc_args, triple_in_cmd in run_list:127 log("Extracted LLC cmd: llc {}".format(llc_args), args.verbose)128 log("Extracted FileCheck prefixes: {}".format(prefixes), args.verbose)129 130 raw_tool_output = args.llc_binary(llc_args, test)131 if not triple_in_cmd and not triple_in_ir:132 common.warn("No triple found: skipping file", test_file=test)133 return134 135 mir.build_function_info_dictionary(136 test,137 raw_tool_output,138 triple_in_cmd or triple_in_ir,139 prefixes,140 func_dict,141 args.verbose,142 )143 144 prefix_set = set([prefix for run in run_list for prefix in run[0]])145 log("Rewriting FileCheck prefixes: {}".format(prefix_set), args.verbose)146 147 output_lines = mir.add_mir_checks(148 input_lines,149 prefix_set,150 autogenerated_note,151 test,152 run_list,153 func_dict,154 args.print_fixed_stack,155 first_check_is_next=False,156 at_the_function_name=False,157 )158 159 log("Writing {} lines to {}...".format(len(output_lines), test), args.verbose)160 161 with open(test, "wb") as fd:162 fd.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines])163 164 165def main():166 parser = argparse.ArgumentParser(167 description=__doc__, formatter_class=argparse.RawTextHelpFormatter168 )169 parser.add_argument(170 "--llc-binary",171 default="llc",172 type=LLC,173 help='The "llc" binary to generate the test case with',174 )175 parser.add_argument(176 "--print-fixed-stack",177 action="store_true",178 help="Add check lines for fixedStack",179 )180 parser.add_argument("tests", nargs="+")181 args = common.parse_commandline_args(parser)182 183 script_name = os.path.basename(__file__)184 returncode = 0185 for ti in common.itertests(args.tests, parser, script_name="utils/" + script_name):186 try:187 update_test_file(ti.args, ti.path, ti.test_autogenerated_note)188 except Exception as e:189 stderr.write(f"Error: Failed to update test {ti.path}\n")190 print_exc()191 returncode = 1192 return returncode193 194 195if __name__ == "__main__":196 sys.exit(main())197