12b892b05SBrox Chen#!/usr/bin/env python3 22b892b05SBrox Chen""" 32b892b05SBrox ChenA test update script. This script is a utility to update LLVM 'llvm-mc' based test cases with new FileCheck patterns. 42b892b05SBrox Chen""" 52b892b05SBrox Chen 62b892b05SBrox Chenfrom __future__ import print_function 72b892b05SBrox Chen 82b892b05SBrox Chenimport argparse 9528e975aSBrox Chenimport functools 102b892b05SBrox Chenimport os # Used to advertise this file's name ("autogenerated_note"). 112b892b05SBrox Chen 122b892b05SBrox Chenfrom UpdateTestChecks import common 132b892b05SBrox Chen 142b892b05SBrox Chenimport subprocess 152b892b05SBrox Chenimport re 162b892b05SBrox Chen 172b892b05SBrox Chenmc_LIKE_TOOLS = [ 182b892b05SBrox Chen "llvm-mc", 192b892b05SBrox Chen] 202b892b05SBrox ChenERROR_RE = re.compile(r":\d+: (warning|error): .*") 212b892b05SBrox ChenERROR_CHECK_RE = re.compile(r"# COM: .*") 222b892b05SBrox ChenOUTPUT_SKIPPED_RE = re.compile(r"(.text)") 232b892b05SBrox ChenCOMMENT = {"asm": "//", "dasm": "#"} 242b892b05SBrox Chen 252b892b05SBrox Chen 26*6f973fd4SBrox Chendef invoke_tool(exe, check_rc, cmd_args, testline, verbose=False): 272b892b05SBrox Chen if isinstance(cmd_args, list): 282b892b05SBrox Chen args = [applySubstitutions(a, substitutions) for a in cmd_args] 292b892b05SBrox Chen else: 302b892b05SBrox Chen args = cmd_args 312b892b05SBrox Chen 322b892b05SBrox Chen cmd = 'echo "' + testline + '" | ' + exe + " " + args 332b892b05SBrox Chen if verbose: 342b892b05SBrox Chen print("Command: ", cmd) 35*6f973fd4SBrox Chen 36*6f973fd4SBrox Chen out = subprocess.run( 37*6f973fd4SBrox Chen cmd, 38*6f973fd4SBrox Chen shell=True, 39*6f973fd4SBrox Chen check=check_rc, 40*6f973fd4SBrox Chen stdout=subprocess.PIPE, 41*6f973fd4SBrox Chen stderr=subprocess.DEVNULL, 42*6f973fd4SBrox Chen ).stdout 43*6f973fd4SBrox Chen 442b892b05SBrox Chen # Fix line endings to unix CR style. 452b892b05SBrox Chen return out.decode().replace("\r\n", "\n") 462b892b05SBrox Chen 472b892b05SBrox Chen 482b892b05SBrox Chen# create tests line-by-line, here we just filter out the check lines and comments 492b892b05SBrox Chen# and treat all others as tests 502b892b05SBrox Chendef isTestLine(input_line, mc_mode): 512b892b05SBrox Chen line = input_line.strip() 522b892b05SBrox Chen # Skip empty and comment lines 532b892b05SBrox Chen if not line or line.startswith(COMMENT[mc_mode]): 542b892b05SBrox Chen return False 552b892b05SBrox Chen # skip any CHECK lines. 562b892b05SBrox Chen elif common.CHECK_RE.match(input_line): 572b892b05SBrox Chen return False 582b892b05SBrox Chen return True 592b892b05SBrox Chen 602b892b05SBrox Chen 61528e975aSBrox Chendef isRunLine(l): 62528e975aSBrox Chen return common.RUN_LINE_RE.match(l) 63528e975aSBrox Chen 64528e975aSBrox Chen 652b892b05SBrox Chendef hasErr(err): 662b892b05SBrox Chen return err and ERROR_RE.search(err) is not None 672b892b05SBrox Chen 682b892b05SBrox Chen 692b892b05SBrox Chendef getErrString(err): 702b892b05SBrox Chen if not err: 712b892b05SBrox Chen return "" 722b892b05SBrox Chen 732b892b05SBrox Chen # take the first match 742b892b05SBrox Chen for line in err.splitlines(): 752b892b05SBrox Chen s = ERROR_RE.search(line) 762b892b05SBrox Chen if s: 772b892b05SBrox Chen return s.group(0) 782b892b05SBrox Chen return "" 792b892b05SBrox Chen 802b892b05SBrox Chen 812b892b05SBrox Chendef getOutputString(out): 822b892b05SBrox Chen if not out: 832b892b05SBrox Chen return "" 842b892b05SBrox Chen output = "" 852b892b05SBrox Chen 862b892b05SBrox Chen for line in out.splitlines(): 872b892b05SBrox Chen if OUTPUT_SKIPPED_RE.search(line): 882b892b05SBrox Chen continue 892b892b05SBrox Chen if line.strip("\t ") == "": 902b892b05SBrox Chen continue 912b892b05SBrox Chen output += line.lstrip("\t ") 922b892b05SBrox Chen return output 932b892b05SBrox Chen 942b892b05SBrox Chen 952b892b05SBrox Chendef should_add_line_to_output(input_line, prefix_set, mc_mode): 962b892b05SBrox Chen # special check line 972b892b05SBrox Chen if mc_mode == "dasm" and ERROR_CHECK_RE.search(input_line): 982b892b05SBrox Chen return False 992b892b05SBrox Chen else: 1002b892b05SBrox Chen return common.should_add_line_to_output( 1012b892b05SBrox Chen input_line, prefix_set, comment_marker=COMMENT[mc_mode] 1022b892b05SBrox Chen ) 1032b892b05SBrox Chen 1042b892b05SBrox Chen 1052b892b05SBrox Chendef getStdCheckLine(prefix, output, mc_mode): 1062b892b05SBrox Chen o = "" 1072b892b05SBrox Chen for line in output.splitlines(): 1082b892b05SBrox Chen o += COMMENT[mc_mode] + " " + prefix + ": " + line + "\n" 1092b892b05SBrox Chen return o 1102b892b05SBrox Chen 1112b892b05SBrox Chen 112*6f973fd4SBrox Chendef getErrCheckLine(prefix, output, mc_mode, line_offset=1): 113*6f973fd4SBrox Chen return ( 114*6f973fd4SBrox Chen COMMENT[mc_mode] 115*6f973fd4SBrox Chen + " " 116*6f973fd4SBrox Chen + prefix 117*6f973fd4SBrox Chen + ": " 118*6f973fd4SBrox Chen + ":[[@LINE-{}]]".format(line_offset) 119*6f973fd4SBrox Chen + output 120*6f973fd4SBrox Chen + "\n" 121*6f973fd4SBrox Chen ) 1222b892b05SBrox Chen 1232b892b05SBrox Chen 1242b892b05SBrox Chendef main(): 1252b892b05SBrox Chen parser = argparse.ArgumentParser(description=__doc__) 1262b892b05SBrox Chen parser.add_argument( 1272b892b05SBrox Chen "--llvm-mc-binary", 1282b892b05SBrox Chen default=None, 1292b892b05SBrox Chen help='The "mc" binary to use to generate the test case', 1302b892b05SBrox Chen ) 1312b892b05SBrox Chen parser.add_argument( 1322b892b05SBrox Chen "--tool", 1332b892b05SBrox Chen default=None, 1342b892b05SBrox Chen help="Treat the given tool name as an mc-like tool for which check lines should be generated", 1352b892b05SBrox Chen ) 1362b892b05SBrox Chen parser.add_argument( 1372b892b05SBrox Chen "--default-march", 1382b892b05SBrox Chen default=None, 1392b892b05SBrox Chen help="Set a default -march for when neither triple nor arch are found in a RUN line", 1402b892b05SBrox Chen ) 141528e975aSBrox Chen parser.add_argument( 142528e975aSBrox Chen "--unique", 143528e975aSBrox Chen action="store_true", 144528e975aSBrox Chen default=False, 145528e975aSBrox Chen help="remove duplicated test line if found", 146528e975aSBrox Chen ) 147528e975aSBrox Chen parser.add_argument( 148528e975aSBrox Chen "--sort", 149528e975aSBrox Chen action="store_true", 150528e975aSBrox Chen default=False, 151528e975aSBrox Chen help="sort testline in alphabetic order (keep run-lines on top), this option could be dangerous as it" 152528e975aSBrox Chen "could change the order of lines that are not expected", 153528e975aSBrox Chen ) 1542b892b05SBrox Chen parser.add_argument("tests", nargs="+") 1552b892b05SBrox Chen initial_args = common.parse_commandline_args(parser) 1562b892b05SBrox Chen 1572b892b05SBrox Chen script_name = os.path.basename(__file__) 1582b892b05SBrox Chen 1592b892b05SBrox Chen for ti in common.itertests( 1602b892b05SBrox Chen initial_args.tests, parser, script_name="utils/" + script_name 1612b892b05SBrox Chen ): 1622b892b05SBrox Chen if ti.path.endswith(".s"): 1632b892b05SBrox Chen mc_mode = "asm" 1642b892b05SBrox Chen elif ti.path.endswith(".txt"): 1652b892b05SBrox Chen mc_mode = "dasm" 166528e975aSBrox Chen 167528e975aSBrox Chen if ti.args.sort: 168528e975aSBrox Chen print("sorting with dasm(.txt) file is not supported!") 169528e975aSBrox Chen return -1 170528e975aSBrox Chen 1712b892b05SBrox Chen else: 1722b892b05SBrox Chen common.warn("Expected .s and .txt, Skipping file : ", ti.path) 1732b892b05SBrox Chen continue 1742b892b05SBrox Chen 1752b892b05SBrox Chen triple_in_ir = None 1762b892b05SBrox Chen for l in ti.input_lines: 1772b892b05SBrox Chen m = common.TRIPLE_IR_RE.match(l) 1782b892b05SBrox Chen if m: 1792b892b05SBrox Chen triple_in_ir = m.groups()[0] 1802b892b05SBrox Chen break 1812b892b05SBrox Chen 1822b892b05SBrox Chen run_list = [] 1832b892b05SBrox Chen for l in ti.run_lines: 1842b892b05SBrox Chen if "|" not in l: 1852b892b05SBrox Chen common.warn("Skipping unparsable RUN line: " + l) 1862b892b05SBrox Chen continue 1872b892b05SBrox Chen 1882b892b05SBrox Chen commands = [cmd.strip() for cmd in l.split("|")] 1892b892b05SBrox Chen assert len(commands) >= 2 1902b892b05SBrox Chen mc_cmd = " | ".join(commands[:-1]) 1912b892b05SBrox Chen filecheck_cmd = commands[-1] 1922b892b05SBrox Chen 1932b892b05SBrox Chen # special handling for negating exit status 194*6f973fd4SBrox Chen # if not is used in runline, disable rc check, since 195*6f973fd4SBrox Chen # the command might or might not 196*6f973fd4SBrox Chen # return non-zero code on a single line run 197*6f973fd4SBrox Chen check_rc = True 198*6f973fd4SBrox Chen mc_cmd_args = mc_cmd.strip().split() 199*6f973fd4SBrox Chen if mc_cmd_args[0] == "not": 200*6f973fd4SBrox Chen check_rc = False 201*6f973fd4SBrox Chen mc_tool = mc_cmd_args[1] 202*6f973fd4SBrox Chen mc_cmd = mc_cmd[len(mc_cmd_args[0]) :].strip() 203*6f973fd4SBrox Chen else: 204*6f973fd4SBrox Chen mc_tool = mc_cmd_args[0] 2052b892b05SBrox Chen 2062b892b05SBrox Chen triple_in_cmd = None 2072b892b05SBrox Chen m = common.TRIPLE_ARG_RE.search(mc_cmd) 2082b892b05SBrox Chen if m: 2092b892b05SBrox Chen triple_in_cmd = m.groups()[0] 2102b892b05SBrox Chen 2112b892b05SBrox Chen march_in_cmd = ti.args.default_march 2122b892b05SBrox Chen m = common.MARCH_ARG_RE.search(mc_cmd) 2132b892b05SBrox Chen if m: 2142b892b05SBrox Chen march_in_cmd = m.groups()[0] 2152b892b05SBrox Chen 2162b892b05SBrox Chen common.verify_filecheck_prefixes(filecheck_cmd) 2172b892b05SBrox Chen 2182b892b05SBrox Chen mc_like_tools = mc_LIKE_TOOLS[:] 2192b892b05SBrox Chen if ti.args.tool: 2202b892b05SBrox Chen mc_like_tools.append(ti.args.tool) 2212b892b05SBrox Chen if mc_tool not in mc_like_tools: 2222b892b05SBrox Chen common.warn("Skipping non-mc RUN line: " + l) 2232b892b05SBrox Chen continue 2242b892b05SBrox Chen 2252b892b05SBrox Chen if not filecheck_cmd.startswith("FileCheck "): 2262b892b05SBrox Chen common.warn("Skipping non-FileChecked RUN line: " + l) 2272b892b05SBrox Chen continue 2282b892b05SBrox Chen 2292b892b05SBrox Chen mc_cmd_args = mc_cmd[len(mc_tool) :].strip() 2302b892b05SBrox Chen mc_cmd_args = mc_cmd_args.replace("< %s", "").replace("%s", "").strip() 2312b892b05SBrox Chen check_prefixes = common.get_check_prefixes(filecheck_cmd) 2322b892b05SBrox Chen 2332b892b05SBrox Chen run_list.append( 2342b892b05SBrox Chen ( 2352b892b05SBrox Chen check_prefixes, 2362b892b05SBrox Chen mc_tool, 237*6f973fd4SBrox Chen check_rc, 2382b892b05SBrox Chen mc_cmd_args, 2392b892b05SBrox Chen triple_in_cmd, 2402b892b05SBrox Chen march_in_cmd, 2412b892b05SBrox Chen ) 2422b892b05SBrox Chen ) 2432b892b05SBrox Chen 2442b892b05SBrox Chen # find all test line from input 2452b892b05SBrox Chen testlines = [l for l in ti.input_lines if isTestLine(l, mc_mode)] 246528e975aSBrox Chen # remove duplicated lines to save running time 247528e975aSBrox Chen testlines = list(dict.fromkeys(testlines)) 248528e975aSBrox Chen common.debug("Valid test line found: ", len(testlines)) 249528e975aSBrox Chen 2502b892b05SBrox Chen run_list_size = len(run_list) 2512b892b05SBrox Chen testnum = len(testlines) 2522b892b05SBrox Chen 2532b892b05SBrox Chen raw_output = [] 2542b892b05SBrox Chen raw_prefixes = [] 2552b892b05SBrox Chen for ( 2562b892b05SBrox Chen prefixes, 2572b892b05SBrox Chen mc_tool, 258*6f973fd4SBrox Chen check_rc, 2592b892b05SBrox Chen mc_args, 2602b892b05SBrox Chen triple_in_cmd, 2612b892b05SBrox Chen march_in_cmd, 2622b892b05SBrox Chen ) in run_list: 2632b892b05SBrox Chen common.debug("Extracted mc cmd:", mc_tool, mc_args) 2642b892b05SBrox Chen common.debug("Extracted FileCheck prefixes:", str(prefixes)) 2652b892b05SBrox Chen common.debug("Extracted triple :", str(triple_in_cmd)) 2662b892b05SBrox Chen common.debug("Extracted march:", str(march_in_cmd)) 2672b892b05SBrox Chen 2682b892b05SBrox Chen triple = triple_in_cmd or triple_in_ir 2692b892b05SBrox Chen if not triple: 2702b892b05SBrox Chen triple = common.get_triple_from_march(march_in_cmd) 2712b892b05SBrox Chen 2722b892b05SBrox Chen raw_output.append([]) 2732b892b05SBrox Chen for line in testlines: 2742b892b05SBrox Chen # get output for each testline 2752b892b05SBrox Chen out = invoke_tool( 2762b892b05SBrox Chen ti.args.llvm_mc_binary or mc_tool, 277*6f973fd4SBrox Chen check_rc, 2782b892b05SBrox Chen mc_args, 2792b892b05SBrox Chen line, 2802b892b05SBrox Chen verbose=ti.args.verbose, 2812b892b05SBrox Chen ) 2822b892b05SBrox Chen raw_output[-1].append(out) 2832b892b05SBrox Chen 2842b892b05SBrox Chen common.debug("Collect raw tool lines:", str(len(raw_output[-1]))) 2852b892b05SBrox Chen 2862b892b05SBrox Chen raw_prefixes.append(prefixes) 2872b892b05SBrox Chen 2882b892b05SBrox Chen output_lines = [] 289528e975aSBrox Chen generated_prefixes = {} 2902b892b05SBrox Chen used_prefixes = set() 2912b892b05SBrox Chen prefix_set = set([prefix for p in run_list for prefix in p[0]]) 2922b892b05SBrox Chen common.debug("Rewriting FileCheck prefixes:", str(prefix_set)) 2932b892b05SBrox Chen 2942b892b05SBrox Chen for test_id in range(testnum): 2952b892b05SBrox Chen input_line = testlines[test_id] 2962b892b05SBrox Chen 2972b892b05SBrox Chen # a {prefix : output, [runid] } dict 2982b892b05SBrox Chen # insert output to a prefix-key dict, and do a max sorting 2992b892b05SBrox Chen # to select the most-used prefix which share the same output string 3002b892b05SBrox Chen p_dict = {} 3012b892b05SBrox Chen for run_id in range(run_list_size): 3022b892b05SBrox Chen out = raw_output[run_id][test_id] 3032b892b05SBrox Chen 3042b892b05SBrox Chen if hasErr(out): 3052b892b05SBrox Chen o = getErrString(out) 3062b892b05SBrox Chen else: 3072b892b05SBrox Chen o = getOutputString(out) 3082b892b05SBrox Chen 3092b892b05SBrox Chen prefixes = raw_prefixes[run_id] 3102b892b05SBrox Chen 3112b892b05SBrox Chen for p in prefixes: 3122b892b05SBrox Chen if p not in p_dict: 3132b892b05SBrox Chen p_dict[p] = o, [run_id] 3142b892b05SBrox Chen else: 3152b892b05SBrox Chen if p_dict[p] == (None, []): 3162b892b05SBrox Chen continue 3172b892b05SBrox Chen 3182b892b05SBrox Chen prev_o, run_ids = p_dict[p] 3192b892b05SBrox Chen if o == prev_o: 3202b892b05SBrox Chen run_ids.append(run_id) 3212b892b05SBrox Chen p_dict[p] = o, run_ids 3222b892b05SBrox Chen else: 3232b892b05SBrox Chen # conflict, discard 3242b892b05SBrox Chen p_dict[p] = None, [] 3252b892b05SBrox Chen 3262b892b05SBrox Chen p_dict_sorted = dict( 3272b892b05SBrox Chen sorted(p_dict.items(), key=lambda item: -len(item[1][1])) 3282b892b05SBrox Chen ) 3292b892b05SBrox Chen 3302b892b05SBrox Chen # prefix is selected and generated with most shared output lines 3312b892b05SBrox Chen # each run_id can only be used once 3322b892b05SBrox Chen gen_prefix = "" 3332b892b05SBrox Chen used_runid = set() 334*6f973fd4SBrox Chen 335*6f973fd4SBrox Chen # line number diff between generated prefix and testline 336*6f973fd4SBrox Chen line_offset = 1 3372b892b05SBrox Chen for prefix, tup in p_dict_sorted.items(): 3382b892b05SBrox Chen o, run_ids = tup 3392b892b05SBrox Chen 3402b892b05SBrox Chen if len(run_ids) == 0: 3412b892b05SBrox Chen continue 3422b892b05SBrox Chen 3432b892b05SBrox Chen skip = False 3442b892b05SBrox Chen for i in run_ids: 3452b892b05SBrox Chen if i in used_runid: 3462b892b05SBrox Chen skip = True 3472b892b05SBrox Chen else: 3482b892b05SBrox Chen used_runid.add(i) 3492b892b05SBrox Chen if not skip: 3502b892b05SBrox Chen used_prefixes.add(prefix) 3512b892b05SBrox Chen 3522b892b05SBrox Chen if hasErr(o): 353*6f973fd4SBrox Chen newline = getErrCheckLine(prefix, o, mc_mode, line_offset) 3542b892b05SBrox Chen else: 355*6f973fd4SBrox Chen newline = getStdCheckLine(prefix, o, mc_mode) 356*6f973fd4SBrox Chen 357*6f973fd4SBrox Chen if newline: 358*6f973fd4SBrox Chen gen_prefix += newline 359*6f973fd4SBrox Chen line_offset += 1 3602b892b05SBrox Chen 361528e975aSBrox Chen generated_prefixes[input_line] = gen_prefix.rstrip("\n") 3622b892b05SBrox Chen 3632b892b05SBrox Chen # write output 3642b892b05SBrox Chen for input_info in ti.iterlines(output_lines): 3652b892b05SBrox Chen input_line = input_info.line 366528e975aSBrox Chen if input_line in testlines: 3672b892b05SBrox Chen output_lines.append(input_line) 368528e975aSBrox Chen output_lines.append(generated_prefixes[input_line]) 3692b892b05SBrox Chen 3702b892b05SBrox Chen elif should_add_line_to_output(input_line, prefix_set, mc_mode): 3712b892b05SBrox Chen output_lines.append(input_line) 3722b892b05SBrox Chen 373528e975aSBrox Chen if ti.args.unique or ti.args.sort: 374528e975aSBrox Chen # split with double newlines 375528e975aSBrox Chen test_units = "\n".join(output_lines).split("\n\n") 3762b892b05SBrox Chen 377528e975aSBrox Chen # select the key line for each test unit 378528e975aSBrox Chen test_dic = {} 379528e975aSBrox Chen for unit in test_units: 380528e975aSBrox Chen lines = unit.split("\n") 381528e975aSBrox Chen for l in lines: 382528e975aSBrox Chen # if contains multiple lines, use 383528e975aSBrox Chen # the first testline or runline as key 384528e975aSBrox Chen if isTestLine(l, mc_mode): 385528e975aSBrox Chen test_dic[unit] = l 386528e975aSBrox Chen break 387528e975aSBrox Chen if isRunLine(l): 388528e975aSBrox Chen test_dic[unit] = l 389528e975aSBrox Chen break 390528e975aSBrox Chen 391528e975aSBrox Chen # unique 392528e975aSBrox Chen if ti.args.unique: 393528e975aSBrox Chen new_test_units = [] 394528e975aSBrox Chen written_lines = set() 395528e975aSBrox Chen for unit in test_units: 396528e975aSBrox Chen # if not testline/runline, we just add it 397528e975aSBrox Chen if unit not in test_dic: 398528e975aSBrox Chen new_test_units.append(unit) 399528e975aSBrox Chen else: 400528e975aSBrox Chen if test_dic[unit] in written_lines: 401528e975aSBrox Chen common.debug("Duplicated test skipped: ", unit) 402528e975aSBrox Chen continue 403528e975aSBrox Chen 404528e975aSBrox Chen written_lines.add(test_dic[unit]) 405528e975aSBrox Chen new_test_units.append(unit) 406528e975aSBrox Chen test_units = new_test_units 407528e975aSBrox Chen 408528e975aSBrox Chen # sort 409528e975aSBrox Chen if ti.args.sort: 410528e975aSBrox Chen 411528e975aSBrox Chen def getkey(l): 412528e975aSBrox Chen # find key of test unit, otherwise use first line 413528e975aSBrox Chen if l in test_dic: 414528e975aSBrox Chen line = test_dic[l] 415528e975aSBrox Chen else: 416528e975aSBrox Chen line = l.split("\n")[0] 417528e975aSBrox Chen 418528e975aSBrox Chen # runline placed on the top 419528e975aSBrox Chen return (not isRunLine(line), line) 420528e975aSBrox Chen 421528e975aSBrox Chen test_units = sorted(test_units, key=getkey) 422528e975aSBrox Chen 423528e975aSBrox Chen # join back to be output string 424528e975aSBrox Chen output_lines = "\n\n".join(test_units).split("\n") 425528e975aSBrox Chen 426528e975aSBrox Chen # output 4272b892b05SBrox Chen if ti.args.gen_unused_prefix_body: 4282b892b05SBrox Chen output_lines.extend( 4292b892b05SBrox Chen ti.get_checks_for_unused_prefixes(run_list, used_prefixes) 4302b892b05SBrox Chen ) 4312b892b05SBrox Chen 4322b892b05SBrox Chen common.debug("Writing %d lines to %s..." % (len(output_lines), ti.path)) 4332b892b05SBrox Chen with open(ti.path, "wb") as f: 4342b892b05SBrox Chen f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines]) 4352b892b05SBrox Chen 4362b892b05SBrox Chen 4372b892b05SBrox Chenif __name__ == "__main__": 4382b892b05SBrox Chen main() 439