diff options
Diffstat (limited to 'llvm/utils/UpdateTestChecks/mir.py')
| -rw-r--r-- | llvm/utils/UpdateTestChecks/mir.py | 362 | 
1 files changed, 362 insertions, 0 deletions
| diff --git a/llvm/utils/UpdateTestChecks/mir.py b/llvm/utils/UpdateTestChecks/mir.py new file mode 100644 index 0000000..24bb8b3 --- /dev/null +++ b/llvm/utils/UpdateTestChecks/mir.py @@ -0,0 +1,362 @@ +"""MIR test utility functions for UpdateTestChecks scripts.""" + +import re +import sys +from UpdateTestChecks import common +from UpdateTestChecks.common import ( +    CHECK_RE, +    warn, +) + +IR_FUNC_NAME_RE = re.compile( +    r"^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[A-Za-z0-9_.]+)\s*\(" +) +IR_PREFIX_DATA_RE = re.compile(r"^ *(;|$)") +MIR_FUNC_NAME_RE = re.compile(r" *name: *(?P<func>[A-Za-z0-9_.-]+)") +MIR_BODY_BEGIN_RE = re.compile(r" *body: *\|") +MIR_BASIC_BLOCK_RE = re.compile(r" *bb\.[0-9]+.*:$") +MIR_PREFIX_DATA_RE = re.compile(r"^ *(;|bb.[0-9].*: *$|[a-z]+:( |$)|$)") + +VREG_RE = re.compile(r"(%[0-9]+)(?:\.[a-z0-9_]+)?(?::[a-z0-9_]+)?(?:\([<>a-z0-9 ]+\))?") +MI_FLAGS_STR = ( +    r"(frame-setup |frame-destroy |nnan |ninf |nsz |arcp |contract |afn " +    r"|reassoc |nuw |nsw |exact |nofpexcept |nomerge |unpredictable " +    r"|noconvergent |nneg |disjoint |nusw |samesign |inbounds )*" +) +VREG_DEF_FLAGS_STR = r"(?:dead |undef )*" + +# Pattern to match the defined vregs and the opcode of an instruction that +# defines vregs. Opcodes starting with a lower-case 't' are allowed to match +# ARM's thumb instructions, like tADDi8 and t2ADDri. +VREG_DEF_RE = re.compile( +    r"^ *(?P<vregs>{2}{0}(?:, {2}{0})*) = " +    r"{1}(?P<opcode>[A-Zt][A-Za-z0-9_]+)".format( +        VREG_RE.pattern, MI_FLAGS_STR, VREG_DEF_FLAGS_STR +    ) +) + +MIR_FUNC_RE = re.compile( +    r"^---$" +    r"\n" +    r"^ *name: *(?P<func>[A-Za-z0-9_.-]+)$" +    r".*?" +    r"(?:^ *fixedStack: *(\[\])? *\n" +    r"(?P<fixedStack>.*?)\n?" +    r"^ *stack:" +    r".*?)?" +    r"^ *body: *\|\n" +    r"(?P<body>.*?)\n" +    r"^\.\.\.$", +    flags=(re.M | re.S), +) + + +def build_function_info_dictionary( +    test, raw_tool_output, triple, prefixes, func_dict, verbose +): +    for m in MIR_FUNC_RE.finditer(raw_tool_output): +        func = m.group("func") +        fixedStack = m.group("fixedStack") +        body = m.group("body") +        if verbose: +            print("Processing function: {}".format(func), file=sys.stderr) +            for l in body.splitlines(): +                print("  {}".format(l), file=sys.stderr) + +        # Vreg mangling +        mangled = [] +        vreg_map = {} +        for func_line in body.splitlines(keepends=True): +            m = VREG_DEF_RE.match(func_line) +            if m: +                for vreg in VREG_RE.finditer(m.group("vregs")): +                    if vreg.group(1) in vreg_map: +                        name = vreg_map[vreg.group(1)] +                    else: +                        name = mangle_vreg(m.group("opcode"), vreg_map.values()) +                        vreg_map[vreg.group(1)] = name +                    func_line = func_line.replace( +                        vreg.group(1), "[[{}:%[0-9]+]]".format(name), 1 +                    ) +            for number, name in vreg_map.items(): +                func_line = re.sub( +                    r"{}\b".format(number), "[[{}]]".format(name), func_line +                ) +            mangled.append(func_line) +        body = "".join(mangled) + +        for prefix in prefixes: +            info = common.function_body( +                body, fixedStack, None, None, None, None, ginfo=None +            ) +            if func in func_dict[prefix]: +                if ( +                    not func_dict[prefix][func] +                    or func_dict[prefix][func].scrub != info.scrub +                    or func_dict[prefix][func].extrascrub != info.extrascrub +                ): +                    func_dict[prefix][func] = None +            else: +                func_dict[prefix][func] = info + + +def mangle_vreg(opcode, current_names): +    base = opcode +    # Simplify some common prefixes and suffixes +    if opcode.startswith("G_"): +        base = base[len("G_") :] +    if opcode.endswith("_PSEUDO"): +        base = base[: len("_PSEUDO")] +    # Shorten some common opcodes with long-ish names +    base = dict( +        IMPLICIT_DEF="DEF", +        GLOBAL_VALUE="GV", +        CONSTANT="C", +        FCONSTANT="C", +        MERGE_VALUES="MV", +        UNMERGE_VALUES="UV", +        INTRINSIC="INT", +        INTRINSIC_W_SIDE_EFFECTS="INT", +        INSERT_VECTOR_ELT="IVEC", +        EXTRACT_VECTOR_ELT="EVEC", +        SHUFFLE_VECTOR="SHUF", +    ).get(base, base) +    # Avoid ambiguity when opcodes end in numbers +    if len(base.rstrip("0123456789")) < len(base): +        base += "_" + +    i = 0 +    for name in current_names: +        if name.rstrip("0123456789") == base: +            i += 1 +    if i: +        return "{}{}".format(base, i) +    return base + + +def find_mir_functions_with_one_bb(lines, verbose=False): +    result = [] +    cur_func = None +    bbs = 0 +    for line in lines: +        m = MIR_FUNC_NAME_RE.match(line) +        if m: +            if bbs == 1: +                result.append(cur_func) +            cur_func = m.group("func") +            bbs = 0 +        m = MIR_BASIC_BLOCK_RE.match(line) +        if m: +            bbs += 1 +    if bbs == 1: +        result.append(cur_func) +    return result + + +def add_mir_checks_for_function( +    test, +    output_lines, +    run_list, +    func_dict, +    func_name, +    single_bb, +    print_fixed_stack, +    first_check_is_next, +    at_the_function_name, +): +    printed_prefixes = set() +    for run in run_list: +        for prefix in run[0]: +            if prefix in printed_prefixes: +                break +            if not func_dict[prefix][func_name]: +                continue +            if printed_prefixes: +                # Add some space between different check prefixes. +                indent = len(output_lines[-1]) - len(output_lines[-1].lstrip(" ")) +                output_lines.append(" " * indent + ";") +            printed_prefixes.add(prefix) +            add_mir_check_lines( +                test, +                output_lines, +                prefix, +                ("@" if at_the_function_name else "") + func_name, +                single_bb, +                func_dict[prefix][func_name], +                print_fixed_stack, +                first_check_is_next, +            ) +            break +        else: +            warn( +                "Found conflicting asm for function: {}".format(func_name), +                test_file=test, +            ) +    return output_lines + + +def add_mir_check_lines( +    test, +    output_lines, +    prefix, +    func_name, +    single_bb, +    func_info, +    print_fixed_stack, +    first_check_is_next, +): +    func_body = str(func_info).splitlines() +    if single_bb: +        # Don't bother checking the basic block label for a single BB +        func_body.pop(0) + +    if not func_body: +        warn( +            "Function has no instructions to check: {}".format(func_name), +            test_file=test, +        ) +        return + +    first_line = func_body[0] +    indent = len(first_line) - len(first_line.lstrip(" ")) +    # A check comment, indented the appropriate amount +    check = "{:>{}}; {}".format("", indent, prefix) + +    output_lines.append("{}-LABEL: name: {}".format(check, func_name)) + +    if print_fixed_stack: +        output_lines.append("{}: fixedStack:".format(check)) +        for stack_line in func_info.extrascrub.splitlines(): +            filecheck_directive = check + "-NEXT" +            output_lines.append("{}: {}".format(filecheck_directive, stack_line)) + +    first_check = not first_check_is_next +    for func_line in func_body: +        if not func_line.strip(): +            # The mir printer prints leading whitespace so we can't use CHECK-EMPTY: +            output_lines.append(check + "-NEXT: {{" + func_line + "$}}") +            continue +        filecheck_directive = check if first_check else check + "-NEXT" +        first_check = False +        check_line = "{}: {}".format(filecheck_directive, func_line[indent:]).rstrip() +        output_lines.append(check_line) + + +def should_add_mir_line_to_output(input_line, prefix_set): +    # Skip any check lines that we're handling as well as comments +    m = CHECK_RE.match(input_line) +    if (m and m.group(1) in prefix_set) or input_line.strip() == ";": +        return False +    return True + + +def add_mir_checks( +    input_lines, +    prefix_set, +    autogenerated_note, +    test, +    run_list, +    func_dict, +    print_fixed_stack, +    first_check_is_next, +    at_the_function_name, +): +    simple_functions = find_mir_functions_with_one_bb(input_lines) + +    output_lines = [] +    output_lines.append(autogenerated_note) + +    func_name = None +    state = "toplevel" +    for input_line in input_lines: +        if input_line == autogenerated_note: +            continue + +        if state == "toplevel": +            m = IR_FUNC_NAME_RE.match(input_line) +            if m: +                state = "ir function prefix" +                func_name = m.group("func") +            if input_line.rstrip("| \r\n") == "---": +                state = "document" +            output_lines.append(input_line) +        elif state == "document": +            m = MIR_FUNC_NAME_RE.match(input_line) +            if m: +                state = "mir function metadata" +                func_name = m.group("func") +            if input_line.strip() == "...": +                state = "toplevel" +                func_name = None +            if should_add_mir_line_to_output(input_line, prefix_set): +                output_lines.append(input_line) +        elif state == "mir function metadata": +            if should_add_mir_line_to_output(input_line, prefix_set): +                output_lines.append(input_line) +            m = MIR_BODY_BEGIN_RE.match(input_line) +            if m: +                if func_name in simple_functions: +                    # If there's only one block, put the checks inside it +                    state = "mir function prefix" +                    continue +                state = "mir function body" +                add_mir_checks_for_function( +                    test, +                    output_lines, +                    run_list, +                    func_dict, +                    func_name, +                    single_bb=False, +                    print_fixed_stack=print_fixed_stack, +                    first_check_is_next=first_check_is_next, +                    at_the_function_name=at_the_function_name, +                ) +        elif state == "mir function prefix": +            m = MIR_PREFIX_DATA_RE.match(input_line) +            if not m: +                state = "mir function body" +                add_mir_checks_for_function( +                    test, +                    output_lines, +                    run_list, +                    func_dict, +                    func_name, +                    single_bb=True, +                    print_fixed_stack=print_fixed_stack, +                    first_check_is_next=first_check_is_next, +                    at_the_function_name=at_the_function_name, +                ) + +            if should_add_mir_line_to_output(input_line, prefix_set): +                output_lines.append(input_line) +        elif state == "mir function body": +            if input_line.strip() == "...": +                state = "toplevel" +                func_name = None +            if should_add_mir_line_to_output(input_line, prefix_set): +                output_lines.append(input_line) +        elif state == "ir function prefix": +            m = IR_PREFIX_DATA_RE.match(input_line) +            if not m: +                state = "ir function body" +                add_mir_checks_for_function( +                    test, +                    output_lines, +                    run_list, +                    func_dict, +                    func_name, +                    single_bb=False, +                    print_fixed_stack=print_fixed_stack, +                    first_check_is_next=first_check_is_next, +                    at_the_function_name=at_the_function_name, +                ) + +            if should_add_mir_line_to_output(input_line, prefix_set): +                output_lines.append(input_line) +        elif state == "ir function body": +            if input_line.strip() == "}": +                state = "toplevel" +                func_name = None +            if should_add_mir_line_to_output(input_line, prefix_set): +                output_lines.append(input_line) +    return output_lines | 
