1#!/usr/bin/env python 2 3"""A test case update script. 4 5This script is a utility to update LLVM 'llc' based test cases with new 6FileCheck patterns. It can either update all of the tests in the file or 7a single test function. 8""" 9 10from __future__ import print_function 11 12import argparse 13import os # Used to advertise this file's name ("autogenerated_note"). 14import string 15import subprocess 16import sys 17import re 18 19from UpdateTestChecks import asm, common 20 21ADVERT = '; NOTE: Assertions have been autogenerated by ' 22 23 24def main(): 25 parser = argparse.ArgumentParser(description=__doc__) 26 parser.add_argument('-v', '--verbose', action='store_true', 27 help='Show verbose output') 28 parser.add_argument('--llc-binary', default='llc', 29 help='The "llc" binary to use to generate the test case') 30 parser.add_argument( 31 '--function', help='The function in the test file to update') 32 parser.add_argument( 33 '--extra_scrub', action='store_true', 34 help='Always use additional regex to further reduce diffs between various subtargets') 35 parser.add_argument( 36 '--x86_scrub_rip', action='store_true', default=True, 37 help='Use more regex for x86 matching to reduce diffs between various subtargets') 38 parser.add_argument( 39 '--no_x86_scrub_rip', action='store_false', dest='x86_scrub_rip') 40 parser.add_argument('tests', nargs='+') 41 args = parser.parse_args() 42 43 autogenerated_note = (ADVERT + 'utils/' + os.path.basename(__file__)) 44 45 for test in args.tests: 46 if args.verbose: 47 print('Scanning for RUN lines in test file: %s' % (test,), file=sys.stderr) 48 with open(test) as f: 49 input_lines = [l.rstrip() for l in f] 50 51 triple_in_ir = None 52 for l in input_lines: 53 m = common.TRIPLE_IR_RE.match(l) 54 if m: 55 triple_in_ir = m.groups()[0] 56 break 57 58 raw_lines = [m.group(1) 59 for m in [common.RUN_LINE_RE.match(l) for l in input_lines] if m] 60 run_lines = [raw_lines[0]] if len(raw_lines) > 0 else [] 61 for l in raw_lines[1:]: 62 if run_lines[-1].endswith("\\"): 63 run_lines[-1] = run_lines[-1].rstrip("\\") + " " + l 64 else: 65 run_lines.append(l) 66 67 if args.verbose: 68 print('Found %d RUN lines:' % (len(run_lines),), file=sys.stderr) 69 for l in run_lines: 70 print(' RUN: ' + l, file=sys.stderr) 71 72 run_list = [] 73 for l in run_lines: 74 commands = [cmd.strip() for cmd in l.split('|', 1)] 75 llc_cmd = commands[0] 76 77 triple_in_cmd = None 78 m = common.TRIPLE_ARG_RE.search(llc_cmd) 79 if m: 80 triple_in_cmd = m.groups()[0] 81 82 filecheck_cmd = '' 83 if len(commands) > 1: 84 filecheck_cmd = commands[1] 85 if not llc_cmd.startswith('llc '): 86 print('WARNING: Skipping non-llc RUN line: ' + l, file=sys.stderr) 87 continue 88 89 if not filecheck_cmd.startswith('FileCheck '): 90 print('WARNING: Skipping non-FileChecked RUN line: ' + l, file=sys.stderr) 91 continue 92 93 llc_cmd_args = llc_cmd[len('llc'):].strip() 94 llc_cmd_args = llc_cmd_args.replace('< %s', '').replace('%s', '').strip() 95 96 check_prefixes = [item for m in common.CHECK_PREFIX_RE.finditer(filecheck_cmd) 97 for item in m.group(1).split(',')] 98 if not check_prefixes: 99 check_prefixes = ['CHECK'] 100 101 # FIXME: We should use multiple check prefixes to common check lines. For 102 # now, we just ignore all but the last. 103 run_list.append((check_prefixes, llc_cmd_args, triple_in_cmd)) 104 105 func_dict = {} 106 for p in run_list: 107 prefixes = p[0] 108 for prefix in prefixes: 109 func_dict.update({prefix: dict()}) 110 for prefixes, llc_args, triple_in_cmd in run_list: 111 if args.verbose: 112 print('Extracted LLC cmd: llc ' + llc_args, file=sys.stderr) 113 print('Extracted FileCheck prefixes: ' + str(prefixes), file=sys.stderr) 114 115 raw_tool_output = common.invoke_tool(args.llc_binary, llc_args, test) 116 if not (triple_in_cmd or triple_in_ir): 117 print("Cannot find a triple. Assume 'x86'", file=sys.stderr) 118 119 asm.build_function_body_dictionary_for_triple(args, raw_tool_output, 120 triple_in_cmd or triple_in_ir or 'x86', prefixes, func_dict) 121 122 is_in_function = False 123 is_in_function_start = False 124 func_name = None 125 prefix_set = set([prefix for p in run_list for prefix in p[0]]) 126 if args.verbose: 127 print('Rewriting FileCheck prefixes: %s' % (prefix_set,), file=sys.stderr) 128 output_lines = [] 129 output_lines.append(autogenerated_note) 130 131 for input_line in input_lines: 132 if is_in_function_start: 133 if input_line == '': 134 continue 135 if input_line.lstrip().startswith(';'): 136 m = common.CHECK_RE.match(input_line) 137 if not m or m.group(1) not in prefix_set: 138 output_lines.append(input_line) 139 continue 140 141 # Print out the various check lines here. 142 asm.add_asm_checks(output_lines, ';', run_list, func_dict, func_name) 143 is_in_function_start = False 144 145 if is_in_function: 146 if common.should_add_line_to_output(input_line, prefix_set): 147 # This input line of the function body will go as-is into the output. 148 output_lines.append(input_line) 149 else: 150 continue 151 if input_line.strip() == '}': 152 is_in_function = False 153 continue 154 155 # Discard any previous script advertising. 156 if input_line.startswith(ADVERT): 157 continue 158 159 # If it's outside a function, it just gets copied to the output. 160 output_lines.append(input_line) 161 162 m = common.IR_FUNCTION_RE.match(input_line) 163 if not m: 164 continue 165 func_name = m.group(1) 166 if args.function is not None and func_name != args.function: 167 # When filtering on a specific function, skip all others. 168 continue 169 is_in_function = is_in_function_start = True 170 171 if args.verbose: 172 print('Writing %d lines to %s...' % (len(output_lines), test), file=sys.stderr) 173 174 with open(test, 'wb') as f: 175 f.writelines([l + '\n' for l in output_lines]) 176 177 178if __name__ == '__main__': 179 main() 180