aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils/UpdateTestChecks/common.py
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/utils/UpdateTestChecks/common.py')
-rw-r--r--llvm/utils/UpdateTestChecks/common.py172
1 files changed, 157 insertions, 15 deletions
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index 269b429..dbcff53 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -1,5 +1,6 @@
from __future__ import print_function
+import argparse
import copy
import glob
import os
@@ -13,7 +14,99 @@ import sys
_verbose = False
_prefix_filecheck_ir_name = ''
+class Regex(object):
+ """Wrap a compiled regular expression object to allow deep copy of a regexp.
+ This is required for the deep copy done in do_scrub.
+
+ """
+ def __init__(self, regex):
+ self.regex = regex
+
+ def __deepcopy__(self, memo):
+ result = copy.copy(self)
+ result.regex = self.regex
+ return result
+
+ def search(self, line):
+ return self.regex.search(line)
+
+ def sub(self, repl, line):
+ return self.regex.sub(repl, line)
+
+ def pattern(self):
+ return self.regex.pattern
+
+ def flags(self):
+ return self.regex.flags
+
+class Filter(Regex):
+ """Augment a Regex object with a flag indicating whether a match should be
+ added (!is_filter_out) or removed (is_filter_out) from the generated checks.
+
+ """
+ def __init__(self, regex, is_filter_out):
+ super(Filter, self).__init__(regex)
+ self.is_filter_out = is_filter_out
+
+ def __deepcopy__(self, memo):
+ result = copy.deepcopy(super(Filter, self), memo)
+ result.is_filter_out = copy.deepcopy(self.is_filter_out, memo)
+ return result
+
def parse_commandline_args(parser):
+ class RegexAction(argparse.Action):
+ """Add a regular expression option value to a list of regular expressions.
+ This compiles the expression, wraps it in a Regex and adds it to the option
+ value list."""
+ def __init__(self, option_strings, dest, nargs=None, **kwargs):
+ if nargs is not None:
+ raise ValueError('nargs not allowed')
+ super(RegexAction, self).__init__(option_strings, dest, **kwargs)
+
+ def do_call(self, namespace, values, flags):
+ value_list = getattr(namespace, self.dest)
+ if value_list is None:
+ value_list = []
+
+ try:
+ value_list.append(Regex(re.compile(values, flags)))
+ except re.error as error:
+ raise ValueError('{}: Invalid regular expression \'{}\' ({})'.format(
+ option_string, error.pattern, error.msg))
+
+ setattr(namespace, self.dest, value_list)
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ self.do_call(namespace, values, 0)
+
+ class FilterAction(RegexAction):
+ """Add a filter to a list of filter option values."""
+ def __init__(self, option_strings, dest, nargs=None, **kwargs):
+ super(FilterAction, self).__init__(option_strings, dest, nargs, **kwargs)
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ super(FilterAction, self).__call__(parser, namespace, values, option_string)
+
+ value_list = getattr(namespace, self.dest)
+
+ is_filter_out = ( option_string == '--filter-out' )
+
+ value_list[-1] = Filter(value_list[-1].regex, is_filter_out)
+
+ setattr(namespace, self.dest, value_list)
+
+ filter_group = parser.add_argument_group(
+ 'filtering',
+ """Filters are applied to each output line according to the order given. The
+ first matching filter terminates filter processing for that current line.""")
+
+ filter_group.add_argument('--filter', action=FilterAction, dest='filters',
+ metavar='REGEX',
+ help='Only include lines matching REGEX (may be specified multiple times)')
+ filter_group.add_argument('--filter-out', action=FilterAction, dest='filters',
+ metavar='REGEX',
+ help='Exclude lines matching REGEX')
+
parser.add_argument('--include-generated-funcs', action='store_true',
help='Output checks for functions not in source')
parser.add_argument('-v', '--verbose', action='store_true',
@@ -258,6 +351,21 @@ def find_run_lines(test, lines):
debug(' RUN: {}'.format(l))
return run_lines
+def apply_filters(line, filters):
+ has_filter = False
+ for f in filters:
+ if not f.is_filter_out:
+ has_filter = True
+ if f.search(line):
+ return False if f.is_filter_out else True
+ # If we only used filter-out, keep the line, otherwise discard it since no
+ # filter matched.
+ return False if has_filter else True
+
+def do_filter(body, filters):
+ return body if not filters else '\n'.join(filter(
+ lambda line: apply_filters(line, filters), body.splitlines()))
+
def scrub_body(body):
# Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place.
@@ -320,6 +428,11 @@ class FunctionTestBuilder:
self._verbose = flags.verbose
self._record_args = flags.function_signature
self._check_attributes = flags.check_attributes
+ # Strip double-quotes if input was read by UTC_ARGS
+ self._filters = list(map(lambda f: Filter(re.compile(f.pattern().strip('"'),
+ f.flags()),
+ f.is_filter_out),
+ flags.filters)) if flags.filters else []
self._scrubber_args = scrubber_args
self._path = path
# Strip double-quotes if input was read by UTC_ARGS
@@ -344,6 +457,9 @@ class FunctionTestBuilder:
def global_var_dict(self):
return self._global_var_dict
+ def is_filtered(self):
+ return bool(self._filters)
+
def process_run_line(self, function_re, scrubber, raw_tool_output, prefixes, is_asm):
build_global_values_dictionary(self._global_var_dict, raw_tool_output, prefixes)
for m in function_re.finditer(raw_tool_output):
@@ -360,9 +476,10 @@ class FunctionTestBuilder:
args_and_sig = '('
else:
args_and_sig = ''
- scrubbed_body = do_scrub(body, scrubber, self._scrubber_args,
+ filtered_body = do_filter(body, self._filters)
+ scrubbed_body = do_scrub(filtered_body, scrubber, self._scrubber_args,
extra=False)
- scrubbed_extra = do_scrub(body, scrubber, self._scrubber_args,
+ scrubbed_extra = do_scrub(filtered_body, scrubber, self._scrubber_args,
extra=True)
if 'analysis' in m.groupdict():
analysis = m.group('analysis')
@@ -649,7 +766,7 @@ def generalize_check_lines(lines, is_analyze, vars_seen, global_vars_seen):
return lines
-def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_asm, is_analyze, global_vars_seen_dict):
+def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_asm, is_analyze, global_vars_seen_dict, is_filtered):
# 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.
prefix_exclusions = set()
printed_prefixes = []
@@ -707,15 +824,26 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
else:
output_lines.append(check_label_format % (checkprefix, func_name, args_and_sig))
func_body = str(func_dict[checkprefix][func_name]).splitlines()
+ if not func_body:
+ # We have filtered everything.
+ continue
# For ASM output, just emit the check lines.
if is_asm:
- output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
+ body_start = 1
+ if is_filtered:
+ # For filtered output we don't add "-NEXT" so don't add extra spaces
+ # before the first line.
+ body_start = 0
+ else:
+ output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
for func_line in func_body[1:]:
if func_line.strip() == '':
output_lines.append('%s %s-EMPTY:' % (comment_marker, checkprefix))
else:
- output_lines.append('%s %s-NEXT: %s' % (comment_marker, checkprefix, func_line))
+ check_suffix = '-NEXT' if not is_filtered else ''
+ output_lines.append('%s %s%s: %s' % (comment_marker, checkprefix,
+ check_suffix, func_line))
break
# For IR output, change all defs to FileCheck variables, so we're immune
@@ -747,8 +875,9 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
output_lines.append('{} {}: {}'.format(
comment_marker, checkprefix, func_line))
else:
- output_lines.append('{} {}-NEXT: {}'.format(
- comment_marker, checkprefix, func_line))
+ check_suffix = '-NEXT' if not is_filtered else ''
+ output_lines.append('{} {}{}: {}'.format(
+ comment_marker, checkprefix, check_suffix, func_line))
is_blank_line = False
# Add space between different check prefixes and also before the first
@@ -762,18 +891,21 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
break
def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict,
- func_name, preserve_names, function_sig, global_vars_seen_dict):
+ func_name, preserve_names, function_sig,
+ global_vars_seen_dict, is_filtered):
# Label format is based on IR string.
function_def_regex = 'define {{[^@]+}}' if function_sig else ''
check_label_format = '{} %s-LABEL: {}@%s%s'.format(comment_marker, function_def_regex)
add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
- check_label_format, False, preserve_names, global_vars_seen_dict)
+ check_label_format, False, preserve_names, global_vars_seen_dict,
+ is_filtered)
def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, func_name):
check_label_format = '{} %s-LABEL: \'%s%s\''.format(comment_marker)
global_vars_seen_dict = {}
add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
- check_label_format, False, True, global_vars_seen_dict)
+ check_label_format, False, True, global_vars_seen_dict,
+ is_filtered = False)
def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes):
for nameless_value in nameless_values:
@@ -901,11 +1033,21 @@ def get_autogennote_suffix(parser, args):
continue
if parser.get_default(action.dest) == value:
continue # Don't add default values
- autogenerated_note_args += action.option_strings[0] + ' '
- if action.const is None: # action takes a parameter
- if action.nargs == '+':
- value = ' '.join(map(lambda v: '"' + v.strip('"') + '"', value))
- autogenerated_note_args += '%s ' % value
+ if action.dest == 'filters':
+ # Create a separate option for each filter element. The value is a list
+ # of Filter objects.
+ for elem in value:
+ opt_name = 'filter-out' if elem.is_filter_out else 'filter'
+ opt_value = elem.pattern()
+ new_arg = '--%s "%s" ' % (opt_name, opt_value.strip('"'))
+ if new_arg not in autogenerated_note_args:
+ autogenerated_note_args += new_arg
+ else:
+ autogenerated_note_args += action.option_strings[0] + ' '
+ if action.const is None: # action takes a parameter
+ if action.nargs == '+':
+ value = ' '.join(map(lambda v: '"' + v.strip('"') + '"', value))
+ autogenerated_note_args += '%s ' % value
if autogenerated_note_args:
autogenerated_note_args = ' %s %s' % (UTC_ARGS_KEY, autogenerated_note_args[:-1])
return autogenerated_note_args