xref: /llvm-project/llvm/utils/UpdateTestChecks/common.py (revision 6a65b44322ee7ba816283c31f8d71559592ca5e7)
1from __future__ import print_function
2
3import argparse
4import copy
5import glob
6import itertools
7import os
8import re
9import subprocess
10import sys
11import shlex
12
13from typing import List
14
15##### Common utilities for update_*test_checks.py
16
17
18_verbose = False
19_prefix_filecheck_ir_name = ""
20
21"""
22Version changelog:
23
241: Initial version, used by tests that don't specify --version explicitly.
252: --function-signature is now enabled by default and also checks return
26   type/attributes.
273: Opening parenthesis of function args is kept on the first LABEL line
28   in case arguments are split to a separate SAME line.
294: --check-globals now has a third option ('smart'). The others are now called
30   'none' and 'all'. 'smart' is the default.
31"""
32DEFAULT_VERSION = 4
33
34
35SUPPORTED_ANALYSES = {
36    "Branch Probability Analysis",
37    "Cost Model Analysis",
38    "Loop Access Analysis",
39    "Scalar Evolution Analysis",
40}
41
42
43class Regex(object):
44    """Wrap a compiled regular expression object to allow deep copy of a regexp.
45    This is required for the deep copy done in do_scrub.
46
47    """
48
49    def __init__(self, regex):
50        self.regex = regex
51
52    def __deepcopy__(self, memo):
53        result = copy.copy(self)
54        result.regex = self.regex
55        return result
56
57    def search(self, line):
58        return self.regex.search(line)
59
60    def sub(self, repl, line):
61        return self.regex.sub(repl, line)
62
63    def pattern(self):
64        return self.regex.pattern
65
66    def flags(self):
67        return self.regex.flags
68
69
70class Filter(Regex):
71    """Augment a Regex object with a flag indicating whether a match should be
72    added (!is_filter_out) or removed (is_filter_out) from the generated checks.
73
74    """
75
76    def __init__(self, regex, is_filter_out):
77        super(Filter, self).__init__(regex)
78        self.is_filter_out = is_filter_out
79
80    def __deepcopy__(self, memo):
81        result = copy.deepcopy(super(Filter, self), memo)
82        result.is_filter_out = copy.deepcopy(self.is_filter_out, memo)
83        return result
84
85
86def parse_commandline_args(parser):
87    class RegexAction(argparse.Action):
88        """Add a regular expression option value to a list of regular expressions.
89        This compiles the expression, wraps it in a Regex and adds it to the option
90        value list."""
91
92        def __init__(self, option_strings, dest, nargs=None, **kwargs):
93            if nargs is not None:
94                raise ValueError("nargs not allowed")
95            super(RegexAction, self).__init__(option_strings, dest, **kwargs)
96
97        def do_call(self, namespace, values, flags):
98            value_list = getattr(namespace, self.dest)
99            if value_list is None:
100                value_list = []
101
102            try:
103                value_list.append(Regex(re.compile(values, flags)))
104            except re.error as error:
105                raise ValueError(
106                    "{}: Invalid regular expression '{}' ({})".format(
107                        option_string, error.pattern, error.msg
108                    )
109                )
110
111            setattr(namespace, self.dest, value_list)
112
113        def __call__(self, parser, namespace, values, option_string=None):
114            self.do_call(namespace, values, 0)
115
116    class FilterAction(RegexAction):
117        """Add a filter to a list of filter option values."""
118
119        def __init__(self, option_strings, dest, nargs=None, **kwargs):
120            super(FilterAction, self).__init__(option_strings, dest, nargs, **kwargs)
121
122        def __call__(self, parser, namespace, values, option_string=None):
123            super(FilterAction, self).__call__(parser, namespace, values, option_string)
124
125            value_list = getattr(namespace, self.dest)
126
127            is_filter_out = option_string == "--filter-out"
128
129            value_list[-1] = Filter(value_list[-1].regex, is_filter_out)
130
131            setattr(namespace, self.dest, value_list)
132
133    filter_group = parser.add_argument_group(
134        "filtering",
135        """Filters are applied to each output line according to the order given. The
136    first matching filter terminates filter processing for that current line.""",
137    )
138
139    filter_group.add_argument(
140        "--filter",
141        action=FilterAction,
142        dest="filters",
143        metavar="REGEX",
144        help="Only include lines matching REGEX (may be specified multiple times)",
145    )
146    filter_group.add_argument(
147        "--filter-out",
148        action=FilterAction,
149        dest="filters",
150        metavar="REGEX",
151        help="Exclude lines matching REGEX",
152    )
153
154    parser.add_argument(
155        "--include-generated-funcs",
156        action="store_true",
157        help="Output checks for functions not in source",
158    )
159    parser.add_argument(
160        "-v", "--verbose", action="store_true", help="Show verbose output"
161    )
162    parser.add_argument(
163        "-u",
164        "--update-only",
165        action="store_true",
166        help="Only update test if it was already autogened",
167    )
168    parser.add_argument(
169        "--force-update",
170        action="store_true",
171        help="Update test even if it was autogened by a different script",
172    )
173    parser.add_argument(
174        "--enable",
175        action="store_true",
176        dest="enabled",
177        default=True,
178        help="Activate CHECK line generation from this point forward",
179    )
180    parser.add_argument(
181        "--disable",
182        action="store_false",
183        dest="enabled",
184        help="Deactivate CHECK line generation from this point forward",
185    )
186    parser.add_argument(
187        "--replace-value-regex",
188        nargs="+",
189        default=[],
190        help="List of regular expressions to replace matching value names",
191    )
192    parser.add_argument(
193        "--prefix-filecheck-ir-name",
194        default="",
195        help="Add a prefix to FileCheck IR value names to avoid conflicts with scripted names",
196    )
197    parser.add_argument(
198        "--global-value-regex",
199        nargs="+",
200        default=[],
201        help="List of regular expressions that a global value declaration must match to generate a check (has no effect if checking globals is not enabled)",
202    )
203    parser.add_argument(
204        "--global-hex-value-regex",
205        nargs="+",
206        default=[],
207        help="List of regular expressions such that, for matching global value declarations, literal integer values should be encoded in hex in the associated FileCheck directives",
208    )
209    # FIXME: in 3.9, we can use argparse.BooleanOptionalAction. At that point,
210    # we need to rename the flag to just -generate-body-for-unused-prefixes.
211    parser.add_argument(
212        "--no-generate-body-for-unused-prefixes",
213        action="store_false",
214        dest="gen_unused_prefix_body",
215        default=True,
216        help="Generate a function body that always matches for unused prefixes. This is useful when unused prefixes are desired, and it avoids needing to annotate each FileCheck as allowing them.",
217    )
218    # This is the default when regenerating existing tests. The default when
219    # generating new tests is determined by DEFAULT_VERSION.
220    parser.add_argument(
221        "--version", type=int, default=1, help="The version of output format"
222    )
223    args = parser.parse_args()
224    # TODO: This should not be handled differently from the other options
225    global _verbose, _global_value_regex, _global_hex_value_regex
226    _verbose = args.verbose
227    _global_value_regex = args.global_value_regex
228    _global_hex_value_regex = args.global_hex_value_regex
229    return args
230
231
232def parse_args(parser, argv):
233    args = parser.parse_args(argv)
234    if args.version >= 2:
235        args.function_signature = True
236    # TODO: This should not be handled differently from the other options
237    global _verbose, _global_value_regex, _global_hex_value_regex
238    _verbose = args.verbose
239    _global_value_regex = args.global_value_regex
240    _global_hex_value_regex = args.global_hex_value_regex
241    if "check_globals" in args and args.check_globals == "default":
242        args.check_globals = "none" if args.version < 4 else "smart"
243    return args
244
245
246class InputLineInfo(object):
247    def __init__(self, line, line_number, args, argv):
248        self.line = line
249        self.line_number = line_number
250        self.args = args
251        self.argv = argv
252
253
254class TestInfo(object):
255    def __init__(
256        self,
257        test,
258        parser,
259        script_name,
260        input_lines,
261        args,
262        argv,
263        comment_prefix,
264        argparse_callback,
265    ):
266        self.parser = parser
267        self.argparse_callback = argparse_callback
268        self.path = test
269        self.args = args
270        if args.prefix_filecheck_ir_name:
271            global _prefix_filecheck_ir_name
272            _prefix_filecheck_ir_name = args.prefix_filecheck_ir_name
273        self.argv = argv
274        self.input_lines = input_lines
275        self.run_lines = find_run_lines(test, self.input_lines)
276        self.comment_prefix = comment_prefix
277        if self.comment_prefix is None:
278            if self.path.endswith(".mir"):
279                self.comment_prefix = "#"
280            else:
281                self.comment_prefix = ";"
282        self.autogenerated_note_prefix = self.comment_prefix + " " + UTC_ADVERT
283        self.test_autogenerated_note = self.autogenerated_note_prefix + script_name
284        self.test_autogenerated_note += get_autogennote_suffix(parser, self.args)
285        self.test_unused_note = (
286            self.comment_prefix + self.comment_prefix + " " + UNUSED_NOTE
287        )
288
289    def ro_iterlines(self):
290        for line_num, input_line in enumerate(self.input_lines):
291            args, argv = check_for_command(
292                input_line, self.parser, self.args, self.argv, self.argparse_callback
293            )
294            yield InputLineInfo(input_line, line_num, args, argv)
295
296    def iterlines(self, output_lines):
297        output_lines.append(self.test_autogenerated_note)
298        for line_info in self.ro_iterlines():
299            input_line = line_info.line
300            # Discard any previous script advertising.
301            if input_line.startswith(self.autogenerated_note_prefix):
302                continue
303            self.args = line_info.args
304            self.argv = line_info.argv
305            if not self.args.enabled:
306                output_lines.append(input_line)
307                continue
308            yield line_info
309
310    def get_checks_for_unused_prefixes(
311        self, run_list, used_prefixes: List[str]
312    ) -> List[str]:
313        run_list = [element for element in run_list if element[0] is not None]
314        unused_prefixes = set(
315            [prefix for sublist in run_list for prefix in sublist[0]]
316        ).difference(set(used_prefixes))
317
318        ret = []
319        if not unused_prefixes:
320            return ret
321        ret.append(self.test_unused_note)
322        for unused in sorted(unused_prefixes):
323            ret.append(
324                "{comment} {prefix}: {match_everything}".format(
325                    comment=self.comment_prefix,
326                    prefix=unused,
327                    match_everything=r"""{{.*}}""",
328                )
329            )
330        return ret
331
332
333def itertests(
334    test_patterns, parser, script_name, comment_prefix=None, argparse_callback=None
335):
336    for pattern in test_patterns:
337        # On Windows we must expand the patterns ourselves.
338        tests_list = glob.glob(pattern)
339        if not tests_list:
340            warn("Test file pattern '%s' was not found. Ignoring it." % (pattern,))
341            continue
342        for test in tests_list:
343            with open(test) as f:
344                input_lines = [l.rstrip() for l in f]
345            first_line = input_lines[0] if input_lines else ""
346            if UTC_AVOID in first_line:
347                warn("Skipping test that must not be autogenerated: " + test)
348                continue
349            is_regenerate = UTC_ADVERT in first_line
350
351            # If we're generating a new test, set the default version to the latest.
352            argv = sys.argv[:]
353            if not is_regenerate:
354                argv.insert(1, "--version=" + str(DEFAULT_VERSION))
355
356            args = parse_args(parser, argv[1:])
357            if argparse_callback is not None:
358                argparse_callback(args)
359            if is_regenerate:
360                if script_name not in first_line and not args.force_update:
361                    warn(
362                        "Skipping test which wasn't autogenerated by " + script_name,
363                        test,
364                    )
365                    continue
366                args, argv = check_for_command(
367                    first_line, parser, args, argv, argparse_callback
368                )
369            elif args.update_only:
370                assert UTC_ADVERT not in first_line
371                warn("Skipping test which isn't autogenerated: " + test)
372                continue
373            final_input_lines = []
374            for l in input_lines:
375                if UNUSED_NOTE in l:
376                    break
377                final_input_lines.append(l)
378            yield TestInfo(
379                test,
380                parser,
381                script_name,
382                final_input_lines,
383                args,
384                argv,
385                comment_prefix,
386                argparse_callback,
387            )
388
389
390def should_add_line_to_output(
391    input_line,
392    prefix_set,
393    *,
394    skip_global_checks=False,
395    skip_same_checks=False,
396    comment_marker=";",
397):
398    # Skip any blank comment lines in the IR.
399    if not skip_global_checks and input_line.strip() == comment_marker:
400        return False
401    # Skip a special double comment line we use as a separator.
402    if input_line.strip() == comment_marker + SEPARATOR:
403        return False
404    # Skip any blank lines in the IR.
405    # if input_line.strip() == '':
406    #  return False
407    # And skip any CHECK lines. We're building our own.
408    m = CHECK_RE.match(input_line)
409    if m and m.group(1) in prefix_set:
410        if skip_same_checks and CHECK_SAME_RE.match(input_line):
411            # The previous CHECK line was removed, so don't leave this dangling
412            return False
413        if skip_global_checks:
414            # Skip checks only if they are of global value definitions
415            global_ir_value_re = re.compile(r"(\[\[|@)", flags=(re.M))
416            is_global = global_ir_value_re.search(input_line)
417            return not is_global
418        return False
419
420    return True
421
422
423# Perform lit-like substitutions
424def getSubstitutions(sourcepath):
425    sourcedir = os.path.dirname(sourcepath)
426    return [
427        ("%s", sourcepath),
428        ("%S", sourcedir),
429        ("%p", sourcedir),
430        ("%{pathsep}", os.pathsep),
431    ]
432
433
434def applySubstitutions(s, substitutions):
435    for a, b in substitutions:
436        s = s.replace(a, b)
437    return s
438
439
440# Invoke the tool that is being tested.
441def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False):
442    with open(ir) as ir_file:
443        substitutions = getSubstitutions(ir)
444
445        # TODO Remove the str form which is used by update_test_checks.py and
446        # update_llc_test_checks.py
447        # The safer list form is used by update_cc_test_checks.py
448        if preprocess_cmd:
449            # Allow pre-processing the IR file (e.g. using sed):
450            assert isinstance(
451                preprocess_cmd, str
452            )  # TODO: use a list instead of using shell
453            preprocess_cmd = applySubstitutions(preprocess_cmd, substitutions).strip()
454            if verbose:
455                print(
456                    "Pre-processing input file: ",
457                    ir,
458                    " with command '",
459                    preprocess_cmd,
460                    "'",
461                    sep="",
462                    file=sys.stderr,
463                )
464            # Python 2.7 doesn't have subprocess.DEVNULL:
465            with open(os.devnull, "w") as devnull:
466                pp = subprocess.Popen(
467                    preprocess_cmd, shell=True, stdin=devnull, stdout=subprocess.PIPE
468                )
469                ir_file = pp.stdout
470
471        if isinstance(cmd_args, list):
472            args = [applySubstitutions(a, substitutions) for a in cmd_args]
473            stdout = subprocess.check_output([exe] + args, stdin=ir_file)
474        else:
475            stdout = subprocess.check_output(
476                exe + " " + applySubstitutions(cmd_args, substitutions),
477                shell=True,
478                stdin=ir_file,
479            )
480        if sys.version_info[0] > 2:
481            # FYI, if you crashed here with a decode error, your run line probably
482            # results in bitcode or other binary format being written to the pipe.
483            # For an opt test, you probably want to add -S or -disable-output.
484            stdout = stdout.decode()
485    # Fix line endings to unix CR style.
486    return stdout.replace("\r\n", "\n")
487
488
489##### LLVM IR parser
490RUN_LINE_RE = re.compile(r"^\s*(?://|[;#])\s*RUN:\s*(.*)$")
491CHECK_PREFIX_RE = re.compile(r"--?check-prefix(?:es)?[= ](\S+)")
492PREFIX_RE = re.compile("^[a-zA-Z0-9_-]+$")
493CHECK_RE = re.compile(
494    r"^\s*(?://|[;#])\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL|-SAME|-EMPTY)?:"
495)
496CHECK_SAME_RE = re.compile(r"^\s*(?://|[;#])\s*([^:]+?)(?:-SAME)?:")
497
498UTC_ARGS_KEY = "UTC_ARGS:"
499UTC_ARGS_CMD = re.compile(r".*" + UTC_ARGS_KEY + r"\s*(?P<cmd>.*)\s*$")
500UTC_ADVERT = "NOTE: Assertions have been autogenerated by "
501UTC_AVOID = "NOTE: Do not autogenerate"
502UNUSED_NOTE = "NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:"
503
504OPT_FUNCTION_RE = re.compile(
505    r"^(\s*;\s*Function\sAttrs:\s(?P<attrs>[\w\s():,]+?))?\s*define\s+(?P<funcdef_attrs_and_ret>[^@]*)@(?P<func>[\w.$-]+?)\s*"
506    r"(?P<args_and_sig>\((\)|(.*?[\w.-]+?)\))[^{]*\{)\n(?P<body>.*?)^\}$",
507    flags=(re.M | re.S),
508)
509
510ANALYZE_FUNCTION_RE = re.compile(
511    r"^\s*\'(?P<analysis>[\w\s-]+?)\'\s+for\s+function\s+\'(?P<func>[\w.$-]+?)\':"
512    r"\s*\n(?P<body>.*)$",
513    flags=(re.X | re.S),
514)
515
516LV_DEBUG_RE = re.compile(
517    r"^\s*\'(?P<func>[\w.$-]+?)\'[^\n]*" r"\s*\n(?P<body>.*)$", flags=(re.X | re.S)
518)
519
520IR_FUNCTION_RE = re.compile(r'^\s*define\s+(?:internal\s+)?[^@]*@"?([\w.$-]+)"?\s*\(')
521TRIPLE_IR_RE = re.compile(r'^\s*target\s+triple\s*=\s*"([^"]+)"$')
522TRIPLE_ARG_RE = re.compile(r"-mtriple[= ]([^ ]+)")
523MARCH_ARG_RE = re.compile(r"-march[= ]([^ ]+)")
524DEBUG_ONLY_ARG_RE = re.compile(r"-debug-only[= ]([^ ]+)")
525
526SCRUB_LEADING_WHITESPACE_RE = re.compile(r"^(\s+)")
527SCRUB_WHITESPACE_RE = re.compile(r"(?!^(|  \w))[ \t]+", flags=re.M)
528SCRUB_PRESERVE_LEADING_WHITESPACE_RE = re.compile(r"((?!^)[ \t]*(\S))[ \t]+")
529SCRUB_TRAILING_WHITESPACE_RE = re.compile(r"[ \t]+$", flags=re.M)
530SCRUB_TRAILING_WHITESPACE_TEST_RE = SCRUB_TRAILING_WHITESPACE_RE
531SCRUB_TRAILING_WHITESPACE_AND_ATTRIBUTES_RE = re.compile(
532    r"([ \t]|(#[0-9]+))+$", flags=re.M
533)
534SCRUB_KILL_COMMENT_RE = re.compile(r"^ *#+ +kill:.*\n")
535SCRUB_LOOP_COMMENT_RE = re.compile(
536    r"# =>This Inner Loop Header:.*|# in Loop:.*", flags=re.M
537)
538SCRUB_TAILING_COMMENT_TOKEN_RE = re.compile(r"(?<=\S)+[ \t]*#$", flags=re.M)
539
540SEPARATOR = "."
541
542
543def error(msg, test_file=None):
544    if test_file:
545        msg = "{}: {}".format(msg, test_file)
546    print("ERROR: {}".format(msg), file=sys.stderr)
547
548
549def warn(msg, test_file=None):
550    if test_file:
551        msg = "{}: {}".format(msg, test_file)
552    print("WARNING: {}".format(msg), file=sys.stderr)
553
554
555def debug(*args, **kwargs):
556    # Python2 does not allow def debug(*args, file=sys.stderr, **kwargs):
557    if "file" not in kwargs:
558        kwargs["file"] = sys.stderr
559    if _verbose:
560        print(*args, **kwargs)
561
562
563def find_run_lines(test, lines):
564    debug("Scanning for RUN lines in test file:", test)
565    raw_lines = [m.group(1) for m in [RUN_LINE_RE.match(l) for l in lines] if m]
566    run_lines = [raw_lines[0]] if len(raw_lines) > 0 else []
567    for l in raw_lines[1:]:
568        if run_lines[-1].endswith("\\"):
569            run_lines[-1] = run_lines[-1].rstrip("\\") + " " + l
570        else:
571            run_lines.append(l)
572    debug("Found {} RUN lines in {}:".format(len(run_lines), test))
573    for l in run_lines:
574        debug("  RUN: {}".format(l))
575    return run_lines
576
577
578def get_triple_from_march(march):
579    triples = {
580        "amdgcn": "amdgcn",
581        "r600": "r600",
582        "mips": "mips",
583        "sparc": "sparc",
584        "hexagon": "hexagon",
585        "ve": "ve",
586    }
587    for prefix, triple in triples.items():
588        if march.startswith(prefix):
589            return triple
590    print("Cannot find a triple. Assume 'x86'", file=sys.stderr)
591    return "x86"
592
593
594def apply_filters(line, filters):
595    has_filter = False
596    for f in filters:
597        if not f.is_filter_out:
598            has_filter = True
599        if f.search(line):
600            return False if f.is_filter_out else True
601    # If we only used filter-out, keep the line, otherwise discard it since no
602    # filter matched.
603    return False if has_filter else True
604
605
606def do_filter(body, filters):
607    return (
608        body
609        if not filters
610        else "\n".join(
611            filter(lambda line: apply_filters(line, filters), body.splitlines())
612        )
613    )
614
615
616def scrub_body(body):
617    # Scrub runs of whitespace out of the assembly, but leave the leading
618    # whitespace in place.
619    body = SCRUB_PRESERVE_LEADING_WHITESPACE_RE.sub(lambda m: m.group(2) + " ", body)
620
621    # Expand the tabs used for indentation.
622    body = str.expandtabs(body, 2)
623    # Strip trailing whitespace.
624    body = SCRUB_TRAILING_WHITESPACE_TEST_RE.sub(r"", body)
625    return body
626
627
628def do_scrub(body, scrubber, scrubber_args, extra):
629    if scrubber_args:
630        local_args = copy.deepcopy(scrubber_args)
631        local_args[0].extra_scrub = extra
632        return scrubber(body, *local_args)
633    return scrubber(body, *scrubber_args)
634
635
636# Build up a dictionary of all the function bodies.
637class function_body(object):
638    def __init__(
639        self,
640        string,
641        extra,
642        funcdef_attrs_and_ret,
643        args_and_sig,
644        attrs,
645        func_name_separator,
646    ):
647        self.scrub = string
648        self.extrascrub = extra
649        self.funcdef_attrs_and_ret = funcdef_attrs_and_ret
650        self.args_and_sig = args_and_sig
651        self.attrs = attrs
652        self.func_name_separator = func_name_separator
653
654    def is_same_except_arg_names(
655        self, extrascrub, funcdef_attrs_and_ret, args_and_sig, attrs, is_backend
656    ):
657        arg_names = set()
658
659        def drop_arg_names(match):
660            arg_names.add(match.group(variable_group_in_ir_value_match))
661            if match.group(attribute_group_in_ir_value_match):
662                attr = match.group(attribute_group_in_ir_value_match)
663            else:
664                attr = ""
665            return match.group(1) + attr + match.group(match.lastindex)
666
667        def repl_arg_names(match):
668            if (
669                match.group(variable_group_in_ir_value_match) is not None
670                and match.group(variable_group_in_ir_value_match) in arg_names
671            ):
672                return match.group(1) + match.group(match.lastindex)
673            return match.group(1) + match.group(2) + match.group(match.lastindex)
674
675        if self.funcdef_attrs_and_ret != funcdef_attrs_and_ret:
676            return False
677        if self.attrs != attrs:
678            return False
679        ans0 = IR_VALUE_RE.sub(drop_arg_names, self.args_and_sig)
680        ans1 = IR_VALUE_RE.sub(drop_arg_names, args_and_sig)
681        if ans0 != ans1:
682            return False
683        if is_backend:
684            # Check without replacements, the replacements are not applied to the
685            # body for backend checks.
686            return self.extrascrub == extrascrub
687
688        es0 = IR_VALUE_RE.sub(repl_arg_names, self.extrascrub)
689        es1 = IR_VALUE_RE.sub(repl_arg_names, extrascrub)
690        es0 = SCRUB_IR_COMMENT_RE.sub(r"", es0)
691        es1 = SCRUB_IR_COMMENT_RE.sub(r"", es1)
692        return es0 == es1
693
694    def __str__(self):
695        return self.scrub
696
697
698class FunctionTestBuilder:
699    def __init__(self, run_list, flags, scrubber_args, path):
700        self._verbose = flags.verbose
701        self._record_args = flags.function_signature
702        self._check_attributes = flags.check_attributes
703        # Strip double-quotes if input was read by UTC_ARGS
704        self._filters = (
705            list(
706                map(
707                    lambda f: Filter(
708                        re.compile(f.pattern().strip('"'), f.flags()), f.is_filter_out
709                    ),
710                    flags.filters,
711                )
712            )
713            if flags.filters
714            else []
715        )
716        self._scrubber_args = scrubber_args
717        self._path = path
718        # Strip double-quotes if input was read by UTC_ARGS
719        self._replace_value_regex = list(
720            map(lambda x: x.strip('"'), flags.replace_value_regex)
721        )
722        self._func_dict = {}
723        self._func_order = {}
724        self._global_var_dict = {}
725        self._processed_prefixes = set()
726        for tuple in run_list:
727            for prefix in tuple[0]:
728                self._func_dict.update({prefix: dict()})
729                self._func_order.update({prefix: []})
730                self._global_var_dict.update({prefix: dict()})
731
732    def finish_and_get_func_dict(self):
733        for prefix in self.get_failed_prefixes():
734            warn(
735                "Prefix %s had conflicting output from different RUN lines for all functions in test %s"
736                % (
737                    prefix,
738                    self._path,
739                )
740            )
741        return self._func_dict
742
743    def func_order(self):
744        return self._func_order
745
746    def global_var_dict(self):
747        return self._global_var_dict
748
749    def is_filtered(self):
750        return bool(self._filters)
751
752    def process_run_line(
753        self, function_re, scrubber, raw_tool_output, prefixes, is_backend
754    ):
755        build_global_values_dictionary(self._global_var_dict, raw_tool_output, prefixes)
756        for m in function_re.finditer(raw_tool_output):
757            if not m:
758                continue
759            func = m.group("func")
760            body = m.group("body")
761            # func_name_separator is the string that is placed right after function name at the
762            # beginning of assembly function definition. In most assemblies, that is just a
763            # colon: `foo:`. But, for example, in nvptx it is a brace: `foo(`. If is_backend is
764            # False, just assume that separator is an empty string.
765            if is_backend:
766                # Use ':' as default separator.
767                func_name_separator = (
768                    m.group("func_name_separator")
769                    if "func_name_separator" in m.groupdict()
770                    else ":"
771                )
772            else:
773                func_name_separator = ""
774            attrs = m.group("attrs") if self._check_attributes else ""
775            funcdef_attrs_and_ret = (
776                m.group("funcdef_attrs_and_ret") if self._record_args else ""
777            )
778            # Determine if we print arguments, the opening brace, or nothing after the
779            # function name
780            if self._record_args and "args_and_sig" in m.groupdict():
781                args_and_sig = scrub_body(m.group("args_and_sig").strip())
782            elif "args_and_sig" in m.groupdict():
783                args_and_sig = "("
784            else:
785                args_and_sig = ""
786            filtered_body = do_filter(body, self._filters)
787            scrubbed_body = do_scrub(
788                filtered_body, scrubber, self._scrubber_args, extra=False
789            )
790            scrubbed_extra = do_scrub(
791                filtered_body, scrubber, self._scrubber_args, extra=True
792            )
793            if "analysis" in m.groupdict():
794                analysis = m.group("analysis")
795                if analysis not in SUPPORTED_ANALYSES:
796                    warn("Unsupported analysis mode: %r!" % (analysis,))
797            if func.startswith("stress"):
798                # We only use the last line of the function body for stress tests.
799                scrubbed_body = "\n".join(scrubbed_body.splitlines()[-1:])
800            if self._verbose:
801                print("Processing function: " + func, file=sys.stderr)
802                for l in scrubbed_body.splitlines():
803                    print("  " + l, file=sys.stderr)
804            for prefix in prefixes:
805                # Replace function names matching the regex.
806                for regex in self._replace_value_regex:
807                    # Pattern that matches capture groups in the regex in leftmost order.
808                    group_regex = re.compile(r"\(.*?\)")
809                    # Replace function name with regex.
810                    match = re.match(regex, func)
811                    if match:
812                        func_repl = regex
813                        # Replace any capture groups with their matched strings.
814                        for g in match.groups():
815                            func_repl = group_regex.sub(
816                                re.escape(g), func_repl, count=1
817                            )
818                        func = re.sub(func_repl, "{{" + func_repl + "}}", func)
819
820                    # Replace all calls to regex matching functions.
821                    matches = re.finditer(regex, scrubbed_body)
822                    for match in matches:
823                        func_repl = regex
824                        # Replace any capture groups with their matched strings.
825                        for g in match.groups():
826                            func_repl = group_regex.sub(
827                                re.escape(g), func_repl, count=1
828                            )
829                        # Substitute function call names that match the regex with the same
830                        # capture groups set.
831                        scrubbed_body = re.sub(
832                            func_repl, "{{" + func_repl + "}}", scrubbed_body
833                        )
834
835                if func in self._func_dict[prefix]:
836                    if self._func_dict[prefix][func] is not None and (
837                        str(self._func_dict[prefix][func]) != scrubbed_body
838                        or self._func_dict[prefix][func].args_and_sig != args_and_sig
839                        or self._func_dict[prefix][func].attrs != attrs
840                        or self._func_dict[prefix][func].funcdef_attrs_and_ret
841                        != funcdef_attrs_and_ret
842                    ):
843                        if self._func_dict[prefix][func].is_same_except_arg_names(
844                            scrubbed_extra,
845                            funcdef_attrs_and_ret,
846                            args_and_sig,
847                            attrs,
848                            is_backend,
849                        ):
850                            self._func_dict[prefix][func].scrub = scrubbed_extra
851                            self._func_dict[prefix][func].args_and_sig = args_and_sig
852                        else:
853                            # This means a previous RUN line produced a body for this function
854                            # that is different from the one produced by this current RUN line,
855                            # so the body can't be common across RUN lines. We use None to
856                            # indicate that.
857                            self._func_dict[prefix][func] = None
858                else:
859                    if prefix not in self._processed_prefixes:
860                        self._func_dict[prefix][func] = function_body(
861                            scrubbed_body,
862                            scrubbed_extra,
863                            funcdef_attrs_and_ret,
864                            args_and_sig,
865                            attrs,
866                            func_name_separator,
867                        )
868                        self._func_order[prefix].append(func)
869                    else:
870                        # An earlier RUN line used this check prefixes but didn't produce
871                        # a body for this function. This happens in Clang tests that use
872                        # preprocesser directives to exclude individual functions from some
873                        # RUN lines.
874                        self._func_dict[prefix][func] = None
875
876    def processed_prefixes(self, prefixes):
877        """
878        Mark a set of prefixes as having had at least one applicable RUN line fully
879        processed. This is used to filter out function bodies that don't have
880        outputs for all RUN lines.
881        """
882        self._processed_prefixes.update(prefixes)
883
884    def get_failed_prefixes(self):
885        # This returns the list of those prefixes that failed to match any function,
886        # because there were conflicting bodies produced by different RUN lines, in
887        # all instances of the prefix.
888        for prefix in self._func_dict:
889            if self._func_dict[prefix] and (
890                not [
891                    fct
892                    for fct in self._func_dict[prefix]
893                    if self._func_dict[prefix][fct] is not None
894                ]
895            ):
896                yield prefix
897
898
899##### Generator of LLVM IR CHECK lines
900
901SCRUB_IR_COMMENT_RE = re.compile(r"\s*;.*")
902
903# TODO: We should also derive check lines for global, debug, loop declarations, etc..
904
905
906class NamelessValue:
907    def __init__(
908        self,
909        check_prefix,
910        check_key,
911        ir_prefix,
912        ir_regexp,
913        global_ir_rhs_regexp,
914        *,
915        is_before_functions=False,
916        is_number=False,
917        replace_number_with_counter=False,
918        match_literally=False,
919        interlaced_with_previous=False
920    ):
921        self.check_prefix = check_prefix
922        self.check_key = check_key
923        self.ir_prefix = ir_prefix
924        self.ir_regexp = ir_regexp
925        self.global_ir_rhs_regexp = global_ir_rhs_regexp
926        self.is_before_functions = is_before_functions
927        self.is_number = is_number
928        # Some variable numbers (e.g. MCINST1234) will change based on unrelated
929        # modifications to LLVM, replace those with an incrementing counter.
930        self.replace_number_with_counter = replace_number_with_counter
931        self.match_literally = match_literally
932        self.interlaced_with_previous = interlaced_with_previous
933        self.variable_mapping = {}
934
935    # Return true if this kind of IR value is "local", basically if it matches '%{{.*}}'.
936    def is_local_def_ir_value_match(self, match):
937        return self.ir_prefix == "%"
938
939    # Return true if this kind of IR value is "global", basically if it matches '#{{.*}}'.
940    def is_global_scope_ir_value_match(self, match):
941        return self.global_ir_rhs_regexp is not None
942
943    # Return the IR prefix and check prefix we use for this kind or IR value,
944    # e.g., (%, TMP) for locals. If the IR prefix is a regex, return the prefix
945    # used in the IR output
946    def get_ir_prefix_from_ir_value_match(self, match):
947        return re.search(self.ir_prefix, match[0])[0], self.check_prefix
948
949    # Return the IR regexp we use for this kind or IR value, e.g., [\w.-]+? for locals
950    def get_ir_regex_from_ir_value_re_match(self, match):
951        # for backwards compatibility we check locals with '.*'
952        if self.is_local_def_ir_value_match(match):
953            return ".*"
954        return self.ir_regexp
955
956    # Create a FileCheck variable name based on an IR name.
957    def get_value_name(self, var: str, check_prefix: str):
958        var = var.replace("!", "")
959        if self.replace_number_with_counter:
960            assert var
961            replacement = self.variable_mapping.get(var, None)
962            if replacement is None:
963                # Replace variable with an incrementing counter
964                replacement = str(len(self.variable_mapping) + 1)
965                self.variable_mapping[var] = replacement
966            var = replacement
967        # This is a nameless value, prepend check_prefix.
968        if var.isdigit():
969            var = check_prefix + var
970        else:
971            # This is a named value that clashes with the check_prefix, prepend with
972            # _prefix_filecheck_ir_name, if it has been defined.
973            if (
974                may_clash_with_default_check_prefix_name(check_prefix, var)
975                and _prefix_filecheck_ir_name
976            ):
977                var = _prefix_filecheck_ir_name + var
978        var = var.replace(".", "_")
979        var = var.replace("-", "_")
980        return var.upper()
981
982    # Create a FileCheck variable from regex.
983    def get_value_definition(self, var, match):
984        # for backwards compatibility we check locals with '.*'
985        varname = self.get_value_name(var, self.check_prefix)
986        prefix = self.get_ir_prefix_from_ir_value_match(match)[0]
987        if self.is_number:
988            regex = ""  # always capture a number in the default format
989            capture_start = "[[#"
990        else:
991            regex = self.get_ir_regex_from_ir_value_re_match(match)
992            capture_start = "[["
993        if self.is_local_def_ir_value_match(match):
994            return capture_start + varname + ":" + prefix + regex + "]]"
995        return prefix + capture_start + varname + ":" + regex + "]]"
996
997    # Use a FileCheck variable.
998    def get_value_use(self, var, match, var_prefix=None):
999        if var_prefix is None:
1000            var_prefix = self.check_prefix
1001        capture_start = "[[#" if self.is_number else "[["
1002        if self.is_local_def_ir_value_match(match):
1003            return capture_start + self.get_value_name(var, var_prefix) + "]]"
1004        prefix = self.get_ir_prefix_from_ir_value_match(match)[0]
1005        return prefix + capture_start + self.get_value_name(var, var_prefix) + "]]"
1006
1007
1008# Description of the different "unnamed" values we match in the IR, e.g.,
1009# (local) ssa values, (debug) metadata, etc.
1010ir_nameless_values = [
1011    #            check_prefix   check_key  ir_prefix           ir_regexp                global_ir_rhs_regexp
1012    NamelessValue(r"TMP", "%", r"%", r"[\w$.-]+?", None),
1013    NamelessValue(r"ATTR", "#", r"#", r"[0-9]+", None),
1014    NamelessValue(r"ATTR", "#", r"attributes #", r"[0-9]+", r"{[^}]*}"),
1015    NamelessValue(r"GLOB", "@", r"@", r"[0-9]+", None),
1016    NamelessValue(r"GLOB", "@", r"@", r"[0-9]+", r".+", is_before_functions=True),
1017    NamelessValue(
1018        r"GLOBNAMED",
1019        "@",
1020        r"@",
1021        r"[a-zA-Z0-9_$\"\\.-]*[a-zA-Z_$\"\\.-][a-zA-Z0-9_$\"\\.-]*",
1022        r".+",
1023        is_before_functions=True,
1024        match_literally=True,
1025        interlaced_with_previous=True,
1026    ),
1027    NamelessValue(r"DBG", "!", r"!dbg ", r"![0-9]+", None),
1028    NamelessValue(r"DIASSIGNID", "!", r"!DIAssignID ", r"![0-9]+", None),
1029    NamelessValue(r"PROF", "!", r"!prof ", r"![0-9]+", None),
1030    NamelessValue(r"TBAA", "!", r"!tbaa ", r"![0-9]+", None),
1031    NamelessValue(r"TBAA_STRUCT", "!", r"!tbaa.struct ", r"![0-9]+", None),
1032    NamelessValue(r"RNG", "!", r"!range ", r"![0-9]+", None),
1033    NamelessValue(r"LOOP", "!", r"!llvm.loop ", r"![0-9]+", None),
1034    NamelessValue(r"META", "!", r"metadata ", r"![0-9]+", None),
1035    NamelessValue(r"META", "!", r"", r"![0-9]+", r"(?:distinct |)!.*"),
1036    NamelessValue(r"ACC_GRP", "!", r"!llvm.access.group ", r"![0-9]+", None),
1037    NamelessValue(r"META", "!", r"![a-z.]+ ", r"![0-9]+", None),
1038]
1039
1040global_nameless_values = [
1041    nameless_value
1042    for nameless_value in ir_nameless_values
1043    if nameless_value.global_ir_rhs_regexp is not None
1044]
1045# global variable names should be matched literally
1046global_nameless_values_w_unstable_ids = [
1047    nameless_value
1048    for nameless_value in global_nameless_values
1049    if not nameless_value.match_literally
1050]
1051
1052asm_nameless_values = [
1053    NamelessValue(
1054        r"MCINST",
1055        "Inst#",
1056        "<MCInst #",
1057        r"\d+",
1058        r".+",
1059        is_number=True,
1060        replace_number_with_counter=True,
1061    ),
1062    NamelessValue(
1063        r"MCREG",
1064        "Reg:",
1065        "<MCOperand Reg:",
1066        r"\d+",
1067        r".+",
1068        is_number=True,
1069        replace_number_with_counter=True,
1070    ),
1071]
1072
1073analyze_nameless_values = [
1074    NamelessValue(
1075        r"GRP",
1076        "#",
1077        r"",
1078        r"0x[0-9a-f]+",
1079        None,
1080        replace_number_with_counter=True,
1081    ),
1082]
1083
1084
1085def createOrRegexp(old, new):
1086    if not old:
1087        return new
1088    if not new:
1089        return old
1090    return old + "|" + new
1091
1092
1093def createPrefixMatch(prefix_str, prefix_re):
1094    return "(?:" + prefix_str + "(" + prefix_re + "))"
1095
1096
1097# Build the regexp that matches an "IR value". This can be a local variable,
1098# argument, global, or metadata, anything that is "named". It is important that
1099# the PREFIX and SUFFIX below only contain a single group, if that changes
1100# other locations will need adjustment as well.
1101IR_VALUE_REGEXP_PREFIX = r"(\s*)"
1102IR_VALUE_REGEXP_STRING = r""
1103for nameless_value in ir_nameless_values:
1104    match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp)
1105    if nameless_value.global_ir_rhs_regexp is not None:
1106        match = "^" + match
1107    IR_VALUE_REGEXP_STRING = createOrRegexp(IR_VALUE_REGEXP_STRING, match)
1108IR_VALUE_REGEXP_SUFFIX = r"([,\s\(\)\}]|\Z)"
1109IR_VALUE_RE = re.compile(
1110    IR_VALUE_REGEXP_PREFIX
1111    + r"("
1112    + IR_VALUE_REGEXP_STRING
1113    + r")"
1114    + IR_VALUE_REGEXP_SUFFIX
1115)
1116
1117GLOBAL_VALUE_REGEXP_STRING = r""
1118for nameless_value in global_nameless_values_w_unstable_ids:
1119    match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp)
1120    GLOBAL_VALUE_REGEXP_STRING = createOrRegexp(GLOBAL_VALUE_REGEXP_STRING, match)
1121GLOBAL_VALUE_RE = re.compile(
1122    IR_VALUE_REGEXP_PREFIX
1123    + r"("
1124    + GLOBAL_VALUE_REGEXP_STRING
1125    + r")"
1126    + IR_VALUE_REGEXP_SUFFIX
1127)
1128
1129# Build the regexp that matches an "ASM value" (currently only for --asm-show-inst comments).
1130ASM_VALUE_REGEXP_STRING = ""
1131for nameless_value in asm_nameless_values:
1132    match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp)
1133    ASM_VALUE_REGEXP_STRING = createOrRegexp(ASM_VALUE_REGEXP_STRING, match)
1134ASM_VALUE_REGEXP_SUFFIX = r"([>\s]|\Z)"
1135ASM_VALUE_RE = re.compile(
1136    r"((?:#|//)\s*)" + "(" + ASM_VALUE_REGEXP_STRING + ")" + ASM_VALUE_REGEXP_SUFFIX
1137)
1138
1139ANALYZE_VALUE_REGEXP_PREFIX = r"(\s*)"
1140ANALYZE_VALUE_REGEXP_STRING = r""
1141for nameless_value in analyze_nameless_values:
1142    match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp)
1143    ANALYZE_VALUE_REGEXP_STRING = createOrRegexp(ANALYZE_VALUE_REGEXP_STRING, match)
1144ANALYZE_VALUE_REGEXP_SUFFIX = r"(\)?:)"
1145ANALYZE_VALUE_RE = re.compile(
1146    ANALYZE_VALUE_REGEXP_PREFIX
1147    + r"("
1148    + ANALYZE_VALUE_REGEXP_STRING
1149    + r")"
1150    + ANALYZE_VALUE_REGEXP_SUFFIX
1151)
1152
1153# The entire match is group 0, the prefix has one group (=1), the entire
1154# IR_VALUE_REGEXP_STRING is one group (=2), and then the nameless values start.
1155first_nameless_group_in_ir_value_match = 3
1156
1157# constants for the group id of special matches
1158variable_group_in_ir_value_match = 3
1159attribute_group_in_ir_value_match = 4
1160
1161
1162# Check a match for IR_VALUE_RE and inspect it to determine if it was a local
1163# value, %..., global @..., debug number !dbg !..., etc. See the PREFIXES above.
1164def get_idx_from_ir_value_match(match):
1165    for i in range(first_nameless_group_in_ir_value_match, match.lastindex):
1166        if match.group(i) is not None:
1167            return i - first_nameless_group_in_ir_value_match
1168    error("Unable to identify the kind of IR value from the match!")
1169    return 0
1170
1171
1172# See get_idx_from_ir_value_match
1173def get_name_from_ir_value_match(match):
1174    return match.group(
1175        get_idx_from_ir_value_match(match) + first_nameless_group_in_ir_value_match
1176    )
1177
1178
1179def get_nameless_value_from_match(match, nameless_values) -> NamelessValue:
1180    return nameless_values[get_idx_from_ir_value_match(match)]
1181
1182
1183# Return true if var clashes with the scripted FileCheck check_prefix.
1184def may_clash_with_default_check_prefix_name(check_prefix, var):
1185    return check_prefix and re.match(
1186        r"^" + check_prefix + r"[0-9]+?$", var, re.IGNORECASE
1187    )
1188
1189
1190def generalize_check_lines_common(
1191    lines,
1192    is_analyze,
1193    vars_seen,
1194    global_vars_seen,
1195    nameless_values,
1196    nameless_value_regex,
1197    is_asm,
1198    preserve_names,
1199):
1200    # This gets called for each match that occurs in
1201    # a line. We transform variables we haven't seen
1202    # into defs, and variables we have seen into uses.
1203    def transform_line_vars(match):
1204        var = get_name_from_ir_value_match(match)
1205        nameless_value = get_nameless_value_from_match(match, nameless_values)
1206        if may_clash_with_default_check_prefix_name(nameless_value.check_prefix, var):
1207            warn(
1208                "Change IR value name '%s' or use --prefix-filecheck-ir-name to prevent possible conflict"
1209                " with scripted FileCheck name." % (var,)
1210            )
1211        key = (var, nameless_value.check_key)
1212        is_local_def = nameless_value.is_local_def_ir_value_match(match)
1213        if is_local_def and key in vars_seen:
1214            rv = nameless_value.get_value_use(var, match)
1215        elif not is_local_def and key in global_vars_seen:
1216            # We could have seen a different prefix for the global variables first,
1217            # ensure we use that one instead of the prefix for the current match.
1218            rv = nameless_value.get_value_use(var, match, global_vars_seen[key])
1219        else:
1220            if is_local_def:
1221                vars_seen.add(key)
1222            else:
1223                global_vars_seen[key] = nameless_value.check_prefix
1224            rv = nameless_value.get_value_definition(var, match)
1225        # re.sub replaces the entire regex match
1226        # with whatever you return, so we have
1227        # to make sure to hand it back everything
1228        # including the commas and spaces.
1229        return match.group(1) + rv + match.group(match.lastindex)
1230
1231    lines_with_def = []
1232    multiple_braces_re = re.compile(r"({{+)|(}}+)")
1233    def escape_braces(match_obj):
1234        return '{{' + re.escape(match_obj.group(0)) + '}}'
1235
1236    for i, line in enumerate(lines):
1237        if not is_asm and not is_analyze:
1238            # An IR variable named '%.' matches the FileCheck regex string.
1239            line = line.replace("%.", "%dot")
1240            for regex in _global_hex_value_regex:
1241                if re.match("^@" + regex + " = ", line):
1242                    line = re.sub(
1243                        r"\bi([0-9]+) ([0-9]+)",
1244                        lambda m: "i"
1245                        + m.group(1)
1246                        + " [[#"
1247                        + hex(int(m.group(2)))
1248                        + "]]",
1249                        line,
1250                    )
1251                    break
1252            # Ignore any comments, since the check lines will too.
1253            scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r"", line)
1254            lines[i] = scrubbed_line
1255        if not preserve_names:
1256            # It can happen that two matches are back-to-back and for some reason sub
1257            # will not replace both of them. For now we work around this by
1258            # substituting until there is no more match.
1259            changed = True
1260            while changed:
1261                (lines[i], changed) = nameless_value_regex.subn(
1262                    transform_line_vars, lines[i], count=1
1263                )
1264        if is_analyze:
1265            # Escape multiple {{ or }} as {{}} denotes a FileCheck regex.
1266            scrubbed_line = multiple_braces_re.sub(escape_braces, lines[i])
1267            lines[i] = scrubbed_line
1268    return lines
1269
1270
1271# Replace IR value defs and uses with FileCheck variables.
1272def generalize_check_lines(
1273    lines, is_analyze, vars_seen, global_vars_seen, preserve_names
1274):
1275    return generalize_check_lines_common(
1276        lines,
1277        is_analyze,
1278        vars_seen,
1279        global_vars_seen,
1280        ir_nameless_values,
1281        IR_VALUE_RE,
1282        False,
1283        preserve_names,
1284    )
1285
1286
1287def generalize_global_check_line(line, preserve_names, global_vars_seen):
1288    [new_line] = generalize_check_lines_common(
1289        [line],
1290        False,
1291        set(),
1292        global_vars_seen,
1293        global_nameless_values_w_unstable_ids,
1294        GLOBAL_VALUE_RE,
1295        False,
1296        preserve_names,
1297    )
1298    return new_line
1299
1300
1301def generalize_asm_check_lines(lines, vars_seen, global_vars_seen):
1302    return generalize_check_lines_common(
1303        lines,
1304        False,
1305        vars_seen,
1306        global_vars_seen,
1307        asm_nameless_values,
1308        ASM_VALUE_RE,
1309        True,
1310        False,
1311    )
1312
1313
1314def generalize_analyze_check_lines(lines, vars_seen, global_vars_seen):
1315    return generalize_check_lines_common(
1316        lines,
1317        True,
1318        vars_seen,
1319        global_vars_seen,
1320        analyze_nameless_values,
1321        ANALYZE_VALUE_RE,
1322        False,
1323        False,
1324    )
1325
1326
1327def add_checks(
1328    output_lines,
1329    comment_marker,
1330    prefix_list,
1331    func_dict,
1332    func_name,
1333    check_label_format,
1334    is_backend,
1335    is_analyze,
1336    version,
1337    global_vars_seen_dict,
1338    is_filtered,
1339    preserve_names=False,
1340):
1341    # prefix_exclusions are prefixes we cannot use to print the function because it doesn't exist in run lines that use these prefixes as well.
1342    prefix_exclusions = set()
1343    printed_prefixes = []
1344    for p in prefix_list:
1345        checkprefixes = p[0]
1346        # If not all checkprefixes of this run line produced the function we cannot check for it as it does not
1347        # exist for this run line. A subset of the check prefixes might know about the function but only because
1348        # other run lines created it.
1349        if any(
1350            map(
1351                lambda checkprefix: func_name not in func_dict[checkprefix],
1352                checkprefixes,
1353            )
1354        ):
1355            prefix_exclusions |= set(checkprefixes)
1356            continue
1357
1358    # prefix_exclusions is constructed, we can now emit the output
1359    for p in prefix_list:
1360        global_vars_seen = {}
1361        checkprefixes = p[0]
1362        for checkprefix in checkprefixes:
1363            if checkprefix in global_vars_seen_dict:
1364                global_vars_seen.update(global_vars_seen_dict[checkprefix])
1365            else:
1366                global_vars_seen_dict[checkprefix] = {}
1367            if checkprefix in printed_prefixes:
1368                break
1369
1370            # Check if the prefix is excluded.
1371            if checkprefix in prefix_exclusions:
1372                continue
1373
1374            # If we do not have output for this prefix we skip it.
1375            if not func_dict[checkprefix][func_name]:
1376                continue
1377
1378            # Add some space between different check prefixes, but not after the last
1379            # check line (before the test code).
1380            if is_backend:
1381                if len(printed_prefixes) != 0:
1382                    output_lines.append(comment_marker)
1383
1384            if checkprefix not in global_vars_seen_dict:
1385                global_vars_seen_dict[checkprefix] = {}
1386
1387            global_vars_seen_before = [key for key in global_vars_seen.keys()]
1388
1389            vars_seen = set()
1390            printed_prefixes.append(checkprefix)
1391            attrs = str(func_dict[checkprefix][func_name].attrs)
1392            attrs = "" if attrs == "None" else attrs
1393            if version > 1:
1394                funcdef_attrs_and_ret = func_dict[checkprefix][
1395                    func_name
1396                ].funcdef_attrs_and_ret
1397            else:
1398                funcdef_attrs_and_ret = ""
1399
1400            if attrs:
1401                output_lines.append(
1402                    "%s %s: Function Attrs: %s" % (comment_marker, checkprefix, attrs)
1403                )
1404            args_and_sig = str(func_dict[checkprefix][func_name].args_and_sig)
1405            if args_and_sig:
1406                args_and_sig = generalize_check_lines(
1407                    [args_and_sig],
1408                    is_analyze,
1409                    vars_seen,
1410                    global_vars_seen,
1411                    preserve_names,
1412                )[0]
1413            func_name_separator = func_dict[checkprefix][func_name].func_name_separator
1414            if "[[" in args_and_sig:
1415                # Captures in label lines are not supported, thus split into a -LABEL
1416                # and a separate -SAME line that contains the arguments with captures.
1417                args_and_sig_prefix = ""
1418                if version >= 3 and args_and_sig.startswith("("):
1419                    # Ensure the "(" separating function name and arguments is in the
1420                    # label line. This is required in case of function names that are
1421                    # prefixes of each other. Otherwise, the label line for "foo" might
1422                    # incorrectly match on "foo.specialized".
1423                    args_and_sig_prefix = args_and_sig[0]
1424                    args_and_sig = args_and_sig[1:]
1425
1426                # Removing args_and_sig from the label match line requires
1427                # func_name_separator to be empty. Otherwise, the match will not work.
1428                assert func_name_separator == ""
1429                output_lines.append(
1430                    check_label_format
1431                    % (
1432                        checkprefix,
1433                        funcdef_attrs_and_ret,
1434                        func_name,
1435                        args_and_sig_prefix,
1436                        func_name_separator,
1437                    )
1438                )
1439                output_lines.append(
1440                    "%s %s-SAME: %s" % (comment_marker, checkprefix, args_and_sig)
1441                )
1442            else:
1443                output_lines.append(
1444                    check_label_format
1445                    % (
1446                        checkprefix,
1447                        funcdef_attrs_and_ret,
1448                        func_name,
1449                        args_and_sig,
1450                        func_name_separator,
1451                    )
1452                )
1453            func_body = str(func_dict[checkprefix][func_name]).splitlines()
1454            if not func_body:
1455                # We have filtered everything.
1456                continue
1457
1458            # For ASM output, just emit the check lines.
1459            if is_backend:
1460                body_start = 1
1461                if is_filtered:
1462                    # For filtered output we don't add "-NEXT" so don't add extra spaces
1463                    # before the first line.
1464                    body_start = 0
1465                else:
1466                    output_lines.append(
1467                        "%s %s:       %s" % (comment_marker, checkprefix, func_body[0])
1468                    )
1469                func_lines = generalize_asm_check_lines(
1470                    func_body[body_start:], vars_seen, global_vars_seen
1471                )
1472                for func_line in func_lines:
1473                    if func_line.strip() == "":
1474                        output_lines.append(
1475                            "%s %s-EMPTY:" % (comment_marker, checkprefix)
1476                        )
1477                    else:
1478                        check_suffix = "-NEXT" if not is_filtered else ""
1479                        output_lines.append(
1480                            "%s %s%s:  %s"
1481                            % (comment_marker, checkprefix, check_suffix, func_line)
1482                        )
1483                # Remember new global variables we have not seen before
1484                for key in global_vars_seen:
1485                    if key not in global_vars_seen_before:
1486                        global_vars_seen_dict[checkprefix][key] = global_vars_seen[key]
1487                break
1488            # For analyze output, generalize the output, and emit CHECK-EMPTY lines as well.
1489            elif is_analyze:
1490                func_body = generalize_analyze_check_lines(
1491                    func_body, vars_seen, global_vars_seen
1492                )
1493                for func_line in func_body:
1494                    if func_line.strip() == "":
1495                        output_lines.append(
1496                            "{} {}-EMPTY:".format(comment_marker, checkprefix)
1497                        )
1498                    else:
1499                        check_suffix = "-NEXT" if not is_filtered else ""
1500                        output_lines.append(
1501                            "{} {}{}:  {}".format(
1502                                comment_marker, checkprefix, check_suffix, func_line
1503                            )
1504                        )
1505
1506                # Add space between different check prefixes and also before the first
1507                # line of code in the test function.
1508                output_lines.append(comment_marker)
1509
1510                # Remember new global variables we have not seen before
1511                for key in global_vars_seen:
1512                    if key not in global_vars_seen_before:
1513                        global_vars_seen_dict[checkprefix][key] = global_vars_seen[key]
1514                break
1515            # For IR output, change all defs to FileCheck variables, so we're immune
1516            # to variable naming fashions.
1517            else:
1518                func_body = generalize_check_lines(
1519                    func_body, False, vars_seen, global_vars_seen, preserve_names
1520                )
1521
1522                # This could be selectively enabled with an optional invocation argument.
1523                # Disabled for now: better to check everything. Be safe rather than sorry.
1524
1525                # Handle the first line of the function body as a special case because
1526                # it's often just noise (a useless asm comment or entry label).
1527                # if func_body[0].startswith("#") or func_body[0].startswith("entry:"):
1528                #  is_blank_line = True
1529                # else:
1530                #  output_lines.append('%s %s:       %s' % (comment_marker, checkprefix, func_body[0]))
1531                #  is_blank_line = False
1532
1533                is_blank_line = False
1534
1535                for func_line in func_body:
1536                    if func_line.strip() == "":
1537                        is_blank_line = True
1538                        continue
1539                    # Do not waste time checking IR comments.
1540                    func_line = SCRUB_IR_COMMENT_RE.sub(r"", func_line)
1541
1542                    # Skip blank lines instead of checking them.
1543                    if is_blank_line:
1544                        output_lines.append(
1545                            "{} {}:       {}".format(
1546                                comment_marker, checkprefix, func_line
1547                            )
1548                        )
1549                    else:
1550                        check_suffix = "-NEXT" if not is_filtered else ""
1551                        output_lines.append(
1552                            "{} {}{}:  {}".format(
1553                                comment_marker, checkprefix, check_suffix, func_line
1554                            )
1555                        )
1556                    is_blank_line = False
1557
1558                # Add space between different check prefixes and also before the first
1559                # line of code in the test function.
1560                output_lines.append(comment_marker)
1561
1562                # Remember new global variables we have not seen before
1563                for key in global_vars_seen:
1564                    if key not in global_vars_seen_before:
1565                        global_vars_seen_dict[checkprefix][key] = global_vars_seen[key]
1566                break
1567    return printed_prefixes
1568
1569
1570def add_ir_checks(
1571    output_lines,
1572    comment_marker,
1573    prefix_list,
1574    func_dict,
1575    func_name,
1576    preserve_names,
1577    function_sig,
1578    version,
1579    global_vars_seen_dict,
1580    is_filtered,
1581):
1582    # Label format is based on IR string.
1583    if function_sig and version > 1:
1584        function_def_regex = "define %s"
1585    elif function_sig:
1586        function_def_regex = "define {{[^@]+}}%s"
1587    else:
1588        function_def_regex = "%s"
1589    check_label_format = "{} %s-LABEL: {}@%s%s%s".format(
1590        comment_marker, function_def_regex
1591    )
1592    return add_checks(
1593        output_lines,
1594        comment_marker,
1595        prefix_list,
1596        func_dict,
1597        func_name,
1598        check_label_format,
1599        False,
1600        False,
1601        version,
1602        global_vars_seen_dict,
1603        is_filtered,
1604        preserve_names,
1605    )
1606
1607
1608def add_analyze_checks(
1609    output_lines, comment_marker, prefix_list, func_dict, func_name, is_filtered
1610):
1611    check_label_format = "{} %s-LABEL: '%s%s%s%s'".format(comment_marker)
1612    global_vars_seen_dict = {}
1613    return add_checks(
1614        output_lines,
1615        comment_marker,
1616        prefix_list,
1617        func_dict,
1618        func_name,
1619        check_label_format,
1620        False,
1621        True,
1622        1,
1623        global_vars_seen_dict,
1624        is_filtered,
1625    )
1626
1627
1628def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes):
1629    for nameless_value in itertools.chain(global_nameless_values, asm_nameless_values):
1630        if nameless_value.global_ir_rhs_regexp is None:
1631            continue
1632
1633        lhs_re_str = nameless_value.ir_prefix + nameless_value.ir_regexp
1634        rhs_re_str = nameless_value.global_ir_rhs_regexp
1635
1636        global_ir_value_re_str = r"^" + lhs_re_str + r"\s=\s" + rhs_re_str + r"$"
1637        global_ir_value_re = re.compile(global_ir_value_re_str, flags=(re.M))
1638        lines = []
1639        for m in global_ir_value_re.finditer(raw_tool_output):
1640            # Attach the substring's start index so that CHECK lines
1641            # can be sorted properly even if they are matched by different nameless values.
1642            # This is relevant for GLOB and GLOBNAMED since they may appear interlaced.
1643            lines.append((m.start(), m.group(0)))
1644
1645        for prefix in prefixes:
1646            if glob_val_dict[prefix] is None:
1647                continue
1648            if nameless_value.check_prefix in glob_val_dict[prefix]:
1649                if lines == glob_val_dict[prefix][nameless_value.check_prefix]:
1650                    continue
1651                if prefix == prefixes[-1]:
1652                    warn("Found conflicting asm under the same prefix: %r!" % (prefix,))
1653                else:
1654                    glob_val_dict[prefix][nameless_value.check_prefix] = None
1655                    continue
1656            glob_val_dict[prefix][nameless_value.check_prefix] = lines
1657
1658
1659def filter_globals_according_to_preference(
1660    global_val_lines_w_index, global_vars_seen, nameless_value, global_check_setting
1661):
1662    if global_check_setting == "none":
1663        return []
1664    if global_check_setting == "all":
1665        return global_val_lines_w_index
1666    assert global_check_setting == "smart"
1667
1668    if nameless_value.check_key == "#":
1669        # attribute sets are usually better checked by --check-attributes
1670        return []
1671
1672    def extract(line, nv):
1673        p = (
1674            "^"
1675            + nv.ir_prefix
1676            + "("
1677            + nv.ir_regexp
1678            + ") = ("
1679            + nv.global_ir_rhs_regexp
1680            + ")"
1681        )
1682        match = re.match(p, line)
1683        return (match.group(1), re.findall(nv.ir_regexp, match.group(2)))
1684
1685    transitively_visible = set()
1686    contains_refs_to = {}
1687
1688    def add(var):
1689        nonlocal transitively_visible
1690        nonlocal contains_refs_to
1691        if var in transitively_visible:
1692            return
1693        transitively_visible.add(var)
1694        if not var in contains_refs_to:
1695            return
1696        for x in contains_refs_to[var]:
1697            add(x)
1698
1699    for i, line in global_val_lines_w_index:
1700        (var, refs) = extract(line, nameless_value)
1701        contains_refs_to[var] = refs
1702    for var, check_key in global_vars_seen:
1703        if check_key != nameless_value.check_key:
1704            continue
1705        add(var)
1706    return [
1707        (i, line)
1708        for i, line in global_val_lines_w_index
1709        if extract(line, nameless_value)[0] in transitively_visible
1710    ]
1711
1712
1713METADATA_FILTERS = [
1714    (
1715        r"(?<=\")(.+ )?(\w+ version )[\d.]+(?:[^\" ]*)(?: \([^)]+\))?",
1716        r"{{.*}}\2{{.*}}",
1717    ),  # preface with glob also, to capture optional CLANG_VENDOR
1718    (r'(!DIFile\(filename: ".+", directory: )".+"', r"\1{{.*}}"),
1719]
1720METADATA_FILTERS_RE = [(re.compile(f), r) for (f, r) in METADATA_FILTERS]
1721
1722
1723def filter_unstable_metadata(line):
1724    for f, replacement in METADATA_FILTERS_RE:
1725        line = f.sub(replacement, line)
1726    return line
1727
1728
1729def flush_current_checks(output_lines, new_lines_w_index, comment_marker):
1730    if not new_lines_w_index:
1731        return
1732    output_lines.append(comment_marker + SEPARATOR)
1733    new_lines_w_index.sort()
1734    for _, line in new_lines_w_index:
1735        output_lines.append(line)
1736    new_lines_w_index.clear()
1737
1738
1739def add_global_checks(
1740    glob_val_dict,
1741    comment_marker,
1742    prefix_list,
1743    output_lines,
1744    global_vars_seen_dict,
1745    preserve_names,
1746    is_before_functions,
1747    global_check_setting,
1748):
1749    printed_prefixes = set()
1750    output_lines_loc = {}  # Allows GLOB and GLOBNAMED to be sorted correctly
1751    for nameless_value in global_nameless_values:
1752        if nameless_value.is_before_functions != is_before_functions:
1753            continue
1754        for p in prefix_list:
1755            global_vars_seen = {}
1756            checkprefixes = p[0]
1757            if checkprefixes is None:
1758                continue
1759            for checkprefix in checkprefixes:
1760                if checkprefix in global_vars_seen_dict:
1761                    global_vars_seen.update(global_vars_seen_dict[checkprefix])
1762                else:
1763                    global_vars_seen_dict[checkprefix] = {}
1764                if (checkprefix, nameless_value.check_prefix) in printed_prefixes:
1765                    break
1766                if not glob_val_dict[checkprefix]:
1767                    continue
1768                if nameless_value.check_prefix not in glob_val_dict[checkprefix]:
1769                    continue
1770                if not glob_val_dict[checkprefix][nameless_value.check_prefix]:
1771                    continue
1772
1773                check_lines = []
1774                global_vars_seen_before = [key for key in global_vars_seen.keys()]
1775                lines_w_index = glob_val_dict[checkprefix][nameless_value.check_prefix]
1776                lines_w_index = filter_globals_according_to_preference(
1777                    lines_w_index,
1778                    global_vars_seen_before,
1779                    nameless_value,
1780                    global_check_setting,
1781                )
1782                for i, line in lines_w_index:
1783                    if _global_value_regex:
1784                        matched = False
1785                        for regex in _global_value_regex:
1786                            if re.match("^@" + regex + " = ", line) or re.match(
1787                                "^!" + regex + " = ", line
1788                            ):
1789                                matched = True
1790                                break
1791                        if not matched:
1792                            continue
1793                    new_line = generalize_global_check_line(
1794                        line, preserve_names, global_vars_seen
1795                    )
1796                    new_line = filter_unstable_metadata(new_line)
1797                    check_line = "%s %s: %s" % (comment_marker, checkprefix, new_line)
1798                    check_lines.append((i, check_line))
1799                if not check_lines:
1800                    continue
1801
1802                if not checkprefix in output_lines_loc:
1803                    output_lines_loc[checkprefix] = []
1804                if not nameless_value.interlaced_with_previous:
1805                    flush_current_checks(
1806                        output_lines, output_lines_loc[checkprefix], comment_marker
1807                    )
1808                for check_line in check_lines:
1809                    output_lines_loc[checkprefix].append(check_line)
1810
1811                printed_prefixes.add((checkprefix, nameless_value.check_prefix))
1812
1813                # Remembe new global variables we have not seen before
1814                for key in global_vars_seen:
1815                    if key not in global_vars_seen_before:
1816                        global_vars_seen_dict[checkprefix][key] = global_vars_seen[key]
1817                break
1818
1819    if printed_prefixes:
1820        for p in prefix_list:
1821            if p[0] is None:
1822                continue
1823            for checkprefix in p[0]:
1824                if checkprefix not in output_lines_loc:
1825                    continue
1826                flush_current_checks(
1827                    output_lines, output_lines_loc[checkprefix], comment_marker
1828                )
1829                break
1830        output_lines.append(comment_marker + SEPARATOR)
1831    return printed_prefixes
1832
1833
1834def check_prefix(prefix):
1835    if not PREFIX_RE.match(prefix):
1836        hint = ""
1837        if "," in prefix:
1838            hint = " Did you mean '--check-prefixes=" + prefix + "'?"
1839        warn(
1840            (
1841                "Supplied prefix '%s' is invalid. Prefix must contain only alphanumeric characters, hyphens and underscores."
1842                + hint
1843            )
1844            % (prefix)
1845        )
1846
1847
1848def get_check_prefixes(filecheck_cmd):
1849    check_prefixes = [
1850        item
1851        for m in CHECK_PREFIX_RE.finditer(filecheck_cmd)
1852        for item in m.group(1).split(",")
1853    ]
1854    if not check_prefixes:
1855        check_prefixes = ["CHECK"]
1856    return check_prefixes
1857
1858
1859def verify_filecheck_prefixes(fc_cmd):
1860    fc_cmd_parts = fc_cmd.split()
1861    for part in fc_cmd_parts:
1862        if "check-prefix=" in part:
1863            prefix = part.split("=", 1)[1]
1864            check_prefix(prefix)
1865        elif "check-prefixes=" in part:
1866            prefixes = part.split("=", 1)[1].split(",")
1867            for prefix in prefixes:
1868                check_prefix(prefix)
1869                if prefixes.count(prefix) > 1:
1870                    warn(
1871                        "Supplied prefix '%s' is not unique in the prefix list."
1872                        % (prefix,)
1873                    )
1874
1875
1876def get_autogennote_suffix(parser, args):
1877    autogenerated_note_args = ""
1878    for action in parser._actions:
1879        if not hasattr(args, action.dest):
1880            continue  # Ignore options such as --help that aren't included in args
1881        # Ignore parameters such as paths to the binary or the list of tests
1882        if action.dest in (
1883            "tests",
1884            "update_only",
1885            "tool_binary",
1886            "opt_binary",
1887            "llc_binary",
1888            "clang",
1889            "opt",
1890            "llvm_bin",
1891            "verbose",
1892            "force_update",
1893        ):
1894            continue
1895        value = getattr(args, action.dest)
1896        if action.dest == "check_globals":
1897            default_value = "none" if args.version < 4 else "smart"
1898            if value == default_value:
1899                continue
1900            autogenerated_note_args += action.option_strings[0] + " "
1901            if args.version < 4 and value == "all":
1902                continue
1903            autogenerated_note_args += "%s " % value
1904            continue
1905        if action.const is not None:  # action stores a constant (usually True/False)
1906            # Skip actions with different constant values (this happens with boolean
1907            # --foo/--no-foo options)
1908            if value != action.const:
1909                continue
1910        if parser.get_default(action.dest) == value:
1911            continue  # Don't add default values
1912        if action.dest == "function_signature" and args.version >= 2:
1913            continue  # Enabled by default in version 2
1914        if action.dest == "filters":
1915            # Create a separate option for each filter element.  The value is a list
1916            # of Filter objects.
1917            for elem in value:
1918                opt_name = "filter-out" if elem.is_filter_out else "filter"
1919                opt_value = elem.pattern()
1920                new_arg = '--%s "%s" ' % (opt_name, opt_value.strip('"'))
1921                if new_arg not in autogenerated_note_args:
1922                    autogenerated_note_args += new_arg
1923        else:
1924            autogenerated_note_args += action.option_strings[0] + " "
1925            if action.const is None:  # action takes a parameter
1926                if action.nargs == "+":
1927                    value = " ".join(map(lambda v: '"' + v.strip('"') + '"', value))
1928                autogenerated_note_args += "%s " % value
1929    if autogenerated_note_args:
1930        autogenerated_note_args = " %s %s" % (
1931            UTC_ARGS_KEY,
1932            autogenerated_note_args[:-1],
1933        )
1934    return autogenerated_note_args
1935
1936
1937def check_for_command(line, parser, args, argv, argparse_callback):
1938    cmd_m = UTC_ARGS_CMD.match(line)
1939    if cmd_m:
1940        for option in shlex.split(cmd_m.group("cmd").strip()):
1941            if option:
1942                argv.append(option)
1943        args = parse_args(parser, filter(lambda arg: arg not in args.tests, argv))
1944        if argparse_callback is not None:
1945            argparse_callback(args)
1946    return args, argv
1947
1948
1949def find_arg_in_test(test_info, get_arg_to_check, arg_string, is_global):
1950    result = get_arg_to_check(test_info.args)
1951    if not result and is_global:
1952        # See if this has been specified via UTC_ARGS.  This is a "global" option
1953        # that affects the entire generation of test checks.  If it exists anywhere
1954        # in the test, apply it to everything.
1955        saw_line = False
1956        for line_info in test_info.ro_iterlines():
1957            line = line_info.line
1958            if not line.startswith(";") and line.strip() != "":
1959                saw_line = True
1960            result = get_arg_to_check(line_info.args)
1961            if result:
1962                if warn and saw_line:
1963                    # We saw the option after already reading some test input lines.
1964                    # Warn about it.
1965                    print(
1966                        "WARNING: Found {} in line following test start: ".format(
1967                            arg_string
1968                        )
1969                        + line,
1970                        file=sys.stderr,
1971                    )
1972                    print(
1973                        "WARNING: Consider moving {} to top of file".format(arg_string),
1974                        file=sys.stderr,
1975                    )
1976                break
1977    return result
1978
1979
1980def dump_input_lines(output_lines, test_info, prefix_set, comment_string):
1981    for input_line_info in test_info.iterlines(output_lines):
1982        line = input_line_info.line
1983        args = input_line_info.args
1984        if line.strip() == comment_string:
1985            continue
1986        if line.strip() == comment_string + SEPARATOR:
1987            continue
1988        if line.lstrip().startswith(comment_string):
1989            m = CHECK_RE.match(line)
1990            if m and m.group(1) in prefix_set:
1991                continue
1992        output_lines.append(line.rstrip("\n"))
1993
1994
1995def add_checks_at_end(
1996    output_lines, prefix_list, func_order, comment_string, check_generator
1997):
1998    added = set()
1999    generated_prefixes = set()
2000    for prefix in prefix_list:
2001        prefixes = prefix[0]
2002        tool_args = prefix[1]
2003        for prefix in prefixes:
2004            for func in func_order[prefix]:
2005                # The func order can contain the same functions multiple times.
2006                # If we see one again we are done.
2007                if (func, prefix) in added:
2008                    continue
2009                if added:
2010                    output_lines.append(comment_string)
2011
2012                # The add_*_checks routines expect a run list whose items are
2013                # tuples that have a list of prefixes as their first element and
2014                # tool command args string as their second element.  They output
2015                # checks for each prefix in the list of prefixes.  By doing so, it
2016                # implicitly assumes that for each function every run line will
2017                # generate something for that function.  That is not the case for
2018                # generated functions as some run lines might not generate them
2019                # (e.g. -fopenmp vs. no -fopenmp).
2020                #
2021                # Therefore, pass just the prefix we're interested in.  This has
2022                # the effect of generating all of the checks for functions of a
2023                # single prefix before moving on to the next prefix.  So checks
2024                # are ordered by prefix instead of by function as in "normal"
2025                # mode.
2026                for generated_prefix in check_generator(
2027                    output_lines, [([prefix], tool_args)], func
2028                ):
2029                    added.add((func, generated_prefix))
2030                    generated_prefixes.add(generated_prefix)
2031    return generated_prefixes
2032