brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.4 KiB · 74e8778 Raw
405 lines · python
1#!/usr/bin/env python32 3"""A script to generate FileCheck statements for 'opt' regression tests.4 5This script is a utility to update LLVM opt test cases with new6FileCheck patterns. It can either update all of the tests in the file or7a single test function.8 9Example usage:10 11# Default to using `opt` as found in your PATH.12$ update_test_checks.py test/foo.ll13 14# Override the path lookup.15$ update_test_checks.py --tool-binary=../bin/opt test/foo.ll16 17# Use a custom tool instead of `opt`.18$ update_test_checks.py --tool=yourtool test/foo.ll19 20Workflow:211. Make a compiler patch that requires updating some number of FileCheck lines22   in regression test files.232. Save the patch and revert it from your local work area.243. Update the RUN-lines in the affected regression tests to look canonical.25   Example: "; RUN: opt < %s -instcombine -S | FileCheck %s"264. Refresh the FileCheck lines for either the entire file or select functions by27   running this script.285. Commit the fresh baseline of checks.296. Apply your patch from step 1 and rebuild your local binaries.307. Re-run this script on affected regression tests.318. Check the diffs to ensure the script has done something reasonable.329. Submit a patch including the regression test diffs for review.33"""34 35from __future__ import print_function36 37from sys import stderr38from traceback import print_exc39import argparse40import os  # Used to advertise this file's name ("autogenerated_note").41import re42import sys43 44from UpdateTestChecks import common45 46 47def update_test(ti: common.TestInfo):48    # If requested we scrub trailing attribute annotations, e.g., '#0', together with whitespaces49    if ti.args.scrub_attributes:50        common.SCRUB_TRAILING_WHITESPACE_TEST_RE = (51            common.SCRUB_TRAILING_WHITESPACE_AND_ATTRIBUTES_RE52        )53    else:54        common.SCRUB_TRAILING_WHITESPACE_TEST_RE = common.SCRUB_TRAILING_WHITESPACE_RE55 56    tool_basename = ti.args.tool57 58    prefix_list = []59    for l in ti.run_lines:60        if "|" not in l:61            common.warn("Skipping unparsable RUN line: " + l)62            continue63 64        cropped_content = l65        if "%if" in l:66            match = re.search(r"%{\s*(.*?)\s*%}", l)67            if match:68                cropped_content = match.group(1)69 70        commands = [cmd.strip() for cmd in cropped_content.split("|")]71        assert len(commands) >= 272        preprocess_cmd = None73        if len(commands) > 2:74            preprocess_cmd = " | ".join(commands[:-2])75        tool_cmd = commands[-2]76        filecheck_cmd = commands[-1]77        common.verify_filecheck_prefixes(filecheck_cmd)78        if not tool_cmd.startswith(tool_basename + " "):79            common.warn("Skipping non-%s RUN line: %s" % (tool_basename, l))80            continue81 82        if not filecheck_cmd.startswith("FileCheck "):83            common.warn("Skipping non-FileChecked RUN line: " + l)84            continue85 86        tool_cmd_args = tool_cmd[len(tool_basename) :].strip()87        tool_cmd_args = tool_cmd_args.replace("< %s", "").replace("%s", "").strip()88        check_prefixes = common.get_check_prefixes(filecheck_cmd)89 90        # FIXME: We should use multiple check prefixes to common check lines. For91        # now, we just ignore all but the last.92        prefix_list.append((check_prefixes, tool_cmd_args, preprocess_cmd))93 94    ginfo = common.make_ir_generalizer(ti.args.version, ti.args.check_globals == "none")95    global_vars_seen_dict = {}96    global_tbaa_records_for_prefixes = {}97    builder = common.FunctionTestBuilder(98        run_list=prefix_list,99        flags=ti.args,100        scrubber_args=[],101        path=ti.path,102        ginfo=ginfo,103    )104 105    tool_binary = ti.args.tool_binary106    if not tool_binary:107        tool_binary = tool_basename108 109    for prefixes, tool_args, preprocess_cmd in prefix_list:110        common.debug("Extracted tool cmd: " + tool_basename + " " + tool_args)111        common.debug("Extracted FileCheck prefixes: " + str(prefixes))112 113        raw_tool_output = common.invoke_tool(114            tool_binary,115            tool_args,116            ti.path,117            preprocess_cmd=preprocess_cmd,118            verbose=ti.args.verbose,119        )120        builder.process_run_line(121            common.OPT_FUNCTION_RE,122            common.scrub_body,123            raw_tool_output,124            prefixes,125        )126        builder.processed_prefixes(prefixes)127 128        # Extract TBAA metadata for later usage in check lines.129        tbaa_map = common.get_tbaa_records(ti.args.version, raw_tool_output)130        global_tbaa_records_for_prefixes[tuple(prefixes)] = tbaa_map131 132    prefix_set = set([prefix for prefixes, _, _ in prefix_list for prefix in prefixes])133 134    if not ti.args.reset_variable_names:135        original_check_lines = common.collect_original_check_lines(ti, prefix_set)136    else:137        original_check_lines = {}138 139    func_dict = builder.finish_and_get_func_dict()140    is_in_function = False141    is_in_function_start = False142    has_checked_pre_function_globals = False143    common.debug("Rewriting FileCheck prefixes:", str(prefix_set))144    output_lines = []145 146    include_generated_funcs = common.find_arg_in_test(147        ti,148        lambda args: ti.args.include_generated_funcs,149        "--include-generated-funcs",150        True,151    )152    generated_prefixes = []153    if include_generated_funcs:154        # Generate the appropriate checks for each function.  We need to emit155        # these in the order according to the generated output so that CHECK-LABEL156        # works properly.  func_order provides that.157 158        # We can't predict where various passes might insert functions so we can't159        # be sure the input function order is maintained.  Therefore, first spit160        # out all the source lines.161        common.dump_input_lines(output_lines, ti, prefix_set, ";")162 163        args = ti.args164        if args.check_globals != "none":165            generated_prefixes.extend(166                common.add_global_checks(167                    builder.global_var_dict(),168                    ";",169                    prefix_list,170                    output_lines,171                    ginfo,172                    global_vars_seen_dict,173                    global_tbaa_records_for_prefixes,174                    args.preserve_names,175                    True,176                    args.check_globals,177                )178            )179 180        # Now generate all the checks.181        generated_prefixes.extend(182            common.add_checks_at_end(183                output_lines,184                prefix_list,185                builder.func_order(),186                ";",187                lambda my_output_lines, prefixes, func: common.add_ir_checks(188                    my_output_lines,189                    ";",190                    prefixes,191                    func_dict,192                    func,193                    False,194                    args.function_signature,195                    ginfo,196                    global_vars_seen_dict,197                    global_tbaa_records_for_prefixes,198                    is_filtered=builder.is_filtered(),199                    original_check_lines=original_check_lines.get(func, {}),200                    check_inst_comments=args.check_inst_comments,201                ),202            )203        )204    else:205        # "Normal" mode.206        dropped_previous_line = False207        for input_line_info in ti.iterlines(output_lines):208            input_line = input_line_info.line209            args = input_line_info.args210            if is_in_function_start:211                if input_line == "":212                    continue213                if input_line.lstrip().startswith(";"):214                    m = common.CHECK_RE.match(input_line)215                    if not m or m.group(1) not in prefix_set:216                        output_lines.append(input_line)217                        continue218 219                # Print out the various check lines here.220                generated_prefixes.extend(221                    common.add_ir_checks(222                        output_lines,223                        ";",224                        prefix_list,225                        func_dict,226                        func_name,227                        args.preserve_names,228                        args.function_signature,229                        ginfo,230                        global_vars_seen_dict,231                        global_tbaa_records_for_prefixes,232                        is_filtered=builder.is_filtered(),233                        original_check_lines=original_check_lines.get(func_name, {}),234                        check_inst_comments=args.check_inst_comments,235                    )236                )237                is_in_function_start = False238 239            m = common.IR_FUNCTION_RE.match(input_line)240            if m and not has_checked_pre_function_globals:241                if args.check_globals:242                    generated_prefixes.extend(243                        common.add_global_checks(244                            builder.global_var_dict(),245                            ";",246                            prefix_list,247                            output_lines,248                            ginfo,249                            global_vars_seen_dict,250                            global_tbaa_records_for_prefixes,251                            args.preserve_names,252                            True,253                            args.check_globals,254                        )255                    )256                has_checked_pre_function_globals = True257 258            if common.should_add_line_to_output(259                input_line,260                prefix_set,261                skip_global_checks=not is_in_function,262                skip_same_checks=dropped_previous_line,263            ):264                # This input line of the function body will go as-is into the output.265                # Except make leading whitespace uniform: 2 spaces. 4 for debug records/switch cases.266                indent = (267                    " " * 4268                    if (269                        common.IS_DEBUG_RECORD_RE.match(input_line)270                        or (271                            ti.args.version > 6272                            and common.IS_SWITCH_CASE_RE.match(input_line)273                        )274                    )275                    else " " * 2276                )277                input_line = common.SCRUB_LEADING_WHITESPACE_RE.sub(indent, input_line)278                output_lines.append(input_line)279                dropped_previous_line = False280                if input_line.strip() == "}":281                    is_in_function = False282                    continue283            else:284                # If we are removing a check line, and the next line is CHECK-SAME, it MUST also be removed285                dropped_previous_line = True286 287            if is_in_function:288                continue289 290            m = common.IR_FUNCTION_RE.match(input_line)291            if not m:292                continue293            func_name = m.group(1)294            if args.function is not None and func_name != args.function:295                # When filtering on a specific function, skip all others.296                continue297            is_in_function = is_in_function_start = True298 299    if args.check_globals != "none":300        generated_prefixes.extend(301            common.add_global_checks(302                builder.global_var_dict(),303                ";",304                prefix_list,305                output_lines,306                ginfo,307                global_vars_seen_dict,308                global_tbaa_records_for_prefixes,309                args.preserve_names,310                False,311                args.check_globals,312            )313        )314    if ti.args.gen_unused_prefix_body:315        output_lines.extend(316            ti.get_checks_for_unused_prefixes(prefix_list, generated_prefixes)317        )318    common.debug("Writing %d lines to %s..." % (len(output_lines), ti.path))319 320    with open(ti.path, "wb") as f:321        f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines])322 323 324def main():325    from argparse import RawTextHelpFormatter326 327    parser = argparse.ArgumentParser(328        description=__doc__, formatter_class=RawTextHelpFormatter329    )330    parser.add_argument(331        "--tool",332        default="opt",333        help='The name of the tool used to generate the test case (defaults to "opt")',334    )335    parser.add_argument(336        "--tool-binary",337        "--opt-binary",338        help="The tool binary used to generate the test case",339    )340    parser.add_argument("--function", help="The function in the test file to update")341    parser.add_argument(342        "-p", "--preserve-names", action="store_true", help="Do not scrub IR names"343    )344    parser.add_argument(345        "--function-signature",346        action="store_true",347        help="Keep function signature information around for the check line",348    )349    parser.add_argument(350        "--scrub-attributes",351        action="store_true",352        help="Remove attribute annotations (#0) from the end of check line",353    )354    parser.add_argument(355        "--check-attributes",356        action="store_true",357        help='Check "Function Attributes" for functions',358    )359    parser.add_argument(360        "--check-globals",361        nargs="?",362        const="all",363        default="default",364        choices=["none", "smart", "all"],365        help="Check global entries (global variables, metadata, attribute sets, ...) for functions",366    )367    parser.add_argument(368        "--check-inst-comments",369        action="store_true",370        default=False,371        help="Check the generated comments describing instructions (e.g., -print-predicate-info/print<memssa>)",372    )373    parser.add_argument(374        "--reset-variable-names",375        action="store_true",376        help="Reset all variable names to correspond closely to the variable names in IR. "377        "This tends to result in larger diffs.",378    )379    parser.add_argument("tests", nargs="+")380    initial_args = common.parse_commandline_args(parser)381 382    script_name = os.path.basename(__file__)383 384    if initial_args.tool_binary:385        tool_basename = os.path.basename(initial_args.tool_binary)386        if not re.match(r"^%s(-\d+)?(\.exe)?$" % (initial_args.tool), tool_basename):387            common.error("Unexpected tool name: " + tool_basename)388            sys.exit(1)389 390    returncode = 0391    for ti in common.itertests(392        initial_args.tests, parser, script_name="utils/" + script_name393    ):394        try:395            update_test(ti)396        except Exception as e:397            stderr.write(f"Error: Failed to update test {ti.path}\n")398            print_exc()399            returncode = 1400    return returncode401 402 403if __name__ == "__main__":404    sys.exit(main())405