Example #1
0
def main():
    from argparse import RawTextHelpFormatter
    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=RawTextHelpFormatter)
    parser.add_argument('--opt-binary',
                        default='opt',
                        help='The opt binary used to generate the test case')
    parser.add_argument('--function',
                        help='The function in the test file to update')
    parser.add_argument('-p',
                        '--preserve-names',
                        action='store_true',
                        help='Do not scrub IR names')
    parser.add_argument(
        '--function-signature',
        action='store_true',
        help='Keep function signature information around for the check line')
    parser.add_argument(
        '--scrub-attributes',
        action='store_true',
        help='Remove attribute annotations (#0) from the end of check line')
    parser.add_argument('--check-attributes',
                        action='store_true',
                        help='Check "Function Attributes" for functions')
    parser.add_argument(
        '--check-globals',
        action='store_true',
        help=
        'Check global entries (global variables, metadata, attribute sets, ...) for functions'
    )
    parser.add_argument('tests', nargs='+')
    initial_args = common.parse_commandline_args(parser)

    script_name = os.path.basename(__file__)
    opt_basename = os.path.basename(initial_args.opt_binary)
    if not re.match(r'^opt(-\d+)?(\.exe)?$', opt_basename):
        common.error('Unexpected opt name: ' + opt_basename)
        sys.exit(1)
    opt_basename = 'opt'

    for ti in common.itertests(initial_args.tests,
                               parser,
                               script_name='utils/' + script_name):
        # If requested we scrub trailing attribute annotations, e.g., '#0', together with whitespaces
        if ti.args.scrub_attributes:
            common.SCRUB_TRAILING_WHITESPACE_TEST_RE = common.SCRUB_TRAILING_WHITESPACE_AND_ATTRIBUTES_RE
        else:
            common.SCRUB_TRAILING_WHITESPACE_TEST_RE = common.SCRUB_TRAILING_WHITESPACE_RE

        prefix_list = []
        for l in ti.run_lines:
            if '|' not in l:
                common.warn('Skipping unparseable RUN line: ' + l)
                continue

            commands = [cmd.strip() for cmd in l.split('|')]
            assert len(commands) >= 2
            preprocess_cmd = None
            if len(commands) > 2:
                preprocess_cmd = " | ".join(commands[:-2])
            tool_cmd = commands[-2]
            filecheck_cmd = commands[-1]
            common.verify_filecheck_prefixes(filecheck_cmd)
            if not tool_cmd.startswith(opt_basename + ' '):
                common.warn('Skipping non-%s RUN line: %s' % (opt_basename, l))
                continue

            if not filecheck_cmd.startswith('FileCheck '):
                common.warn('Skipping non-FileChecked RUN line: ' + l)
                continue

            tool_cmd_args = tool_cmd[len(opt_basename):].strip()
            tool_cmd_args = tool_cmd_args.replace('< %s',
                                                  '').replace('%s',
                                                              '').strip()

            check_prefixes = [
                item for m in common.CHECK_PREFIX_RE.finditer(filecheck_cmd)
                for item in m.group(1).split(',')
            ]
            if not check_prefixes:
                check_prefixes = ['CHECK']

            # FIXME: We should use multiple check prefixes to common check lines. For
            # now, we just ignore all but the last.
            prefix_list.append((check_prefixes, tool_cmd_args, preprocess_cmd))

        global_vars_seen_dict = {}
        builder = common.FunctionTestBuilder(run_list=prefix_list,
                                             flags=ti.args,
                                             scrubber_args=[],
                                             path=ti.path)

        for prefixes, opt_args, preprocess_cmd in prefix_list:
            common.debug('Extracted opt cmd: ' + opt_basename + ' ' + opt_args)
            common.debug('Extracted FileCheck prefixes: ' + str(prefixes))

            raw_tool_output = common.invoke_tool(ti.args.opt_binary,
                                                 opt_args,
                                                 ti.path,
                                                 preprocess_cmd=preprocess_cmd,
                                                 verbose=ti.args.verbose)
            builder.process_run_line(common.OPT_FUNCTION_RE, common.scrub_body,
                                     raw_tool_output, prefixes, False)
            builder.processed_prefixes(prefixes)

        func_dict = builder.finish_and_get_func_dict()
        is_in_function = False
        is_in_function_start = False
        has_checked_pre_function_globals = False
        prefix_set = set(
            [prefix for prefixes, _, _ in prefix_list for prefix in prefixes])
        common.debug('Rewriting FileCheck prefixes:', str(prefix_set))
        output_lines = []

        include_generated_funcs = common.find_arg_in_test(
            ti, lambda args: ti.args.include_generated_funcs,
            '--include-generated-funcs', True)

        if include_generated_funcs:
            # Generate the appropriate checks for each function.  We need to emit
            # these in the order according to the generated output so that CHECK-LABEL
            # works properly.  func_order provides that.

            # We can't predict where various passes might insert functions so we can't
            # be sure the input function order is maintained.  Therefore, first spit
            # out all the source lines.
            common.dump_input_lines(output_lines, ti, prefix_set, ';')

            args = ti.args
            if args.check_globals:
                common.add_global_checks(builder.global_var_dict(), ';',
                                         prefix_list, output_lines,
                                         global_vars_seen_dict,
                                         args.preserve_names, True)

            # Now generate all the checks.
            common.add_checks_at_end(
                output_lines, prefix_list, builder.func_order(), ';',
                lambda my_output_lines, prefixes, func: common.add_ir_checks(
                    my_output_lines,
                    ';',
                    prefixes,
                    func_dict,
                    func,
                    False,
                    args.function_signature,
                    global_vars_seen_dict,
                    is_filtered=builder.is_filtered()))
        else:
            # "Normal" mode.
            for input_line_info in ti.iterlines(output_lines):
                input_line = input_line_info.line
                args = input_line_info.args
                if is_in_function_start:
                    if input_line == '':
                        continue
                    if input_line.lstrip().startswith(';'):
                        m = common.CHECK_RE.match(input_line)
                        if not m or m.group(1) not in prefix_set:
                            output_lines.append(input_line)
                            continue

                    # Print out the various check lines here.
                    common.add_ir_checks(output_lines,
                                         ';',
                                         prefix_list,
                                         func_dict,
                                         func_name,
                                         args.preserve_names,
                                         args.function_signature,
                                         global_vars_seen_dict,
                                         is_filtered=builder.is_filtered())
                    is_in_function_start = False

                m = common.IR_FUNCTION_RE.match(input_line)
                if m and not has_checked_pre_function_globals:
                    if args.check_globals:
                        common.add_global_checks(builder.global_var_dict(),
                                                 ';', prefix_list,
                                                 output_lines,
                                                 global_vars_seen_dict,
                                                 args.preserve_names, True)
                    has_checked_pre_function_globals = True

                if common.should_add_line_to_output(input_line, prefix_set,
                                                    not is_in_function):
                    # This input line of the function body will go as-is into the output.
                    # Except make leading whitespace uniform: 2 spaces.
                    input_line = common.SCRUB_LEADING_WHITESPACE_RE.sub(
                        r'  ', input_line)
                    output_lines.append(input_line)
                    if input_line.strip() == '}':
                        is_in_function = False
                        continue

                if is_in_function:
                    continue

                m = common.IR_FUNCTION_RE.match(input_line)
                if not m:
                    continue
                func_name = m.group(1)
                if args.function is not None and func_name != args.function:
                    # When filtering on a specific function, skip all others.
                    continue
                is_in_function = is_in_function_start = True

        if args.check_globals:
            common.add_global_checks(builder.global_var_dict(), ';',
                                     prefix_list, output_lines,
                                     global_vars_seen_dict,
                                     args.preserve_names, False)
        common.debug('Writing %d lines to %s...' %
                     (len(output_lines), ti.path))

        with open(ti.path, 'wb') as f:
            f.writelines(
                ['{}\n'.format(l).encode('utf-8') for l in output_lines])
Example #2
0
def main():
    initial_args, parser = config()
    script_name = os.path.basename(__file__)

    for ti in common.itertests(initial_args.tests,
                               parser,
                               'utils/' + script_name,
                               comment_prefix='//',
                               argparse_callback=infer_dependent_args):
        # Build a list of filechecked and non-filechecked RUN lines.
        run_list = []
        line2spell_and_mangled_list = collections.defaultdict(list)

        subs = {
            '%s': ti.path,
            '%t': tempfile.NamedTemporaryFile().name,
            '%S': os.getcwd(),
        }

        for l in ti.run_lines:
            commands = [cmd.strip() for cmd in l.split('|')]

            triple_in_cmd = None
            m = common.TRIPLE_ARG_RE.search(commands[0])
            if m:
                triple_in_cmd = m.groups()[0]

            # Parse executable args.
            exec_args = shlex.split(commands[0])
            # Execute non-clang runline.
            if exec_args[0] not in SUBST:
                # Do lit-like substitutions.
                for s in subs:
                    exec_args = [
                        i.replace(s, subs[s]) if s in i else i
                        for i in exec_args
                    ]
                run_list.append((None, exec_args, None, None))
                continue
            # This is a clang runline, apply %clang substitution rule, do lit-like substitutions,
            # and append args.clang_args
            clang_args = exec_args
            clang_args[0:1] = SUBST[clang_args[0]]
            for s in subs:
                clang_args = [
                    i.replace(s, subs[s]) if s in i else i for i in clang_args
                ]
            clang_args += ti.args.clang_args

            # Extract -check-prefix in FileCheck args
            filecheck_cmd = commands[-1]
            common.verify_filecheck_prefixes(filecheck_cmd)
            if not filecheck_cmd.startswith('FileCheck '):
                # Execute non-filechecked clang runline.
                exe = [ti.args.clang] + clang_args
                run_list.append((None, exe, None, None))
                continue

            check_prefixes = [
                item for m in common.CHECK_PREFIX_RE.finditer(filecheck_cmd)
                for item in m.group(1).split(',')
            ]
            if not check_prefixes:
                check_prefixes = ['CHECK']
            run_list.append(
                (check_prefixes, clang_args, commands[1:-1], triple_in_cmd))

        # Execute clang, generate LLVM IR, and extract functions.

        # Store only filechecked runlines.
        filecheck_run_list = [i for i in run_list if i[0]]
        builder = common.FunctionTestBuilder(run_list=filecheck_run_list,
                                             flags=ti.args,
                                             scrubber_args=[],
                                             path=ti.path)

        for prefixes, args, extra_commands, triple_in_cmd in run_list:
            # Execute non-filechecked runline.
            if not prefixes:
                print('NOTE: Executing non-FileChecked RUN line: ' +
                      ' '.join(args),
                      file=sys.stderr)
                exec_run_line(args)
                continue

            clang_args = args
            common.debug('Extracted clang cmd: clang {}'.format(clang_args))
            common.debug('Extracted FileCheck prefixes: {}'.format(prefixes))

            get_function_body(builder, ti.args, ti.path, clang_args,
                              extra_commands, prefixes)

            # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
            # mangled names. Forward all clang args for now.
            for k, v in get_line2spell_and_mangled(ti.args,
                                                   clang_args).items():
                line2spell_and_mangled_list[k].append(v)

        func_dict = builder.finish_and_get_func_dict()
        global_vars_seen_dict = {}
        prefix_set = set(
            [prefix for p in filecheck_run_list for prefix in p[0]])
        output_lines = []
        has_checked_pre_function_globals = False

        include_generated_funcs = common.find_arg_in_test(
            ti, lambda args: ti.args.include_generated_funcs,
            '--include-generated-funcs', True)

        if include_generated_funcs:
            # Generate the appropriate checks for each function.  We need to emit
            # these in the order according to the generated output so that CHECK-LABEL
            # works properly.  func_order provides that.

            # It turns out that when clang generates functions (for example, with
            # -fopenmp), it can sometimes cause functions to be re-ordered in the
            # output, even functions that exist in the source file.  Therefore we
            # can't insert check lines before each source function and instead have to
            # put them at the end.  So the first thing to do is dump out the source
            # lines.
            common.dump_input_lines(output_lines, ti, prefix_set, '//')

            # Now generate all the checks.
            def check_generator(my_output_lines, prefixes, func):
                if '-emit-llvm' in clang_args:
                    common.add_ir_checks(my_output_lines, '//', prefixes,
                                         func_dict, func, False,
                                         ti.args.function_signature,
                                         global_vars_seen_dict)
                else:
                    asm.add_asm_checks(my_output_lines, '//', prefixes,
                                       func_dict, func)

            if ti.args.check_globals:
                common.add_global_checks(builder.global_var_dict(), '//',
                                         run_list, output_lines,
                                         global_vars_seen_dict, True, True)
            common.add_checks_at_end(
                output_lines, filecheck_run_list, builder.func_order(), '//',
                lambda my_output_lines, prefixes, func: check_generator(
                    my_output_lines, prefixes, func))
        else:
            # Normal mode.  Put checks before each source function.
            for line_info in ti.iterlines(output_lines):
                idx = line_info.line_number
                line = line_info.line
                args = line_info.args
                include_line = True
                m = common.CHECK_RE.match(line)
                if m and m.group(1) in prefix_set:
                    continue  # Don't append the existing CHECK lines
                # Skip special separator comments added by commmon.add_global_checks.
                if line.strip() == '//' + common.SEPARATOR:
                    continue
                if idx in line2spell_and_mangled_list:
                    added = set()
                    for spell, mangled in line2spell_and_mangled_list[idx]:
                        # One line may contain multiple function declarations.
                        # Skip if the mangled name has been added before.
                        # The line number may come from an included file,
                        # we simply require the spelling name to appear on the line
                        # to exclude functions from other files.
                        if mangled in added or spell not in line:
                            continue
                        if args.functions is None or any(
                                re.search(regex, spell)
                                for regex in args.functions):
                            last_line = output_lines[-1].strip()
                            while last_line == '//':
                                # Remove the comment line since we will generate a new  comment
                                # line as part of common.add_ir_checks()
                                output_lines.pop()
                                last_line = output_lines[-1].strip()
                            if ti.args.check_globals and not has_checked_pre_function_globals:
                                common.add_global_checks(
                                    builder.global_var_dict(), '//', run_list,
                                    output_lines, global_vars_seen_dict, True,
                                    True)
                                has_checked_pre_function_globals = True
                            if added:
                                output_lines.append('//')
                            added.add(mangled)
                            common.add_ir_checks(output_lines, '//',
                                                 filecheck_run_list, func_dict,
                                                 mangled, False,
                                                 args.function_signature,
                                                 global_vars_seen_dict)
                            if line.rstrip('\n') == '//':
                                include_line = False

                if include_line:
                    output_lines.append(line.rstrip('\n'))

        if ti.args.check_globals:
            common.add_global_checks(builder.global_var_dict(), '//', run_list,
                                     output_lines, global_vars_seen_dict, True,
                                     False)
        common.debug('Writing %d lines to %s...' %
                     (len(output_lines), ti.path))
        with open(ti.path, 'wb') as f:
            f.writelines(
                ['{}\n'.format(l).encode('utf-8') for l in output_lines])

    return 0