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.py218
1 files changed, 197 insertions, 21 deletions
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index 88b2ccc2..d1fd884 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -26,8 +26,10 @@ Version changelog:
type/attributes.
3: Opening parenthesis of function args is kept on the first LABEL line
in case arguments are split to a separate SAME line.
+4: --check-globals now has a third option ('smart'). The others are now called
+ 'none' and 'all'. 'smart' is the default.
"""
-DEFAULT_VERSION = 3
+DEFAULT_VERSION = 4
class Regex(object):
@@ -228,6 +230,8 @@ def parse_args(parser, argv):
_verbose = args.verbose
_global_value_regex = args.global_value_regex
_global_hex_value_regex = args.global_hex_value_regex
+ if "check_globals" in args and args.check_globals == "default":
+ args.check_globals = "none" if args.version < 4 else "smart"
return args
@@ -332,8 +336,8 @@ def itertests(
input_lines = [l.rstrip() for l in f]
first_line = input_lines[0] if input_lines else ""
if UTC_AVOID in first_line:
- warn("Skipping test that must not be autogenerated: " + test)
- continue
+ warn("Skipping test that must not be autogenerated: " + test)
+ continue
is_regenerate = UTC_ADVERT in first_line
# If we're generating a new test, set the default version to the latest.
@@ -896,7 +900,9 @@ class NamelessValue:
*,
is_before_functions=False,
is_number=False,
- replace_number_with_counter=False
+ replace_number_with_counter=False,
+ match_literally=False,
+ interlaced_with_previous=False
):
self.check_prefix = check_prefix
self.check_key = check_key
@@ -908,6 +914,8 @@ class NamelessValue:
# Some variable numbers (e.g. MCINST1234) will change based on unrelated
# modifications to LLVM, replace those with an incrementing counter.
self.replace_number_with_counter = replace_number_with_counter
+ self.match_literally = match_literally
+ self.interlaced_with_previous = interlaced_with_previous
self.variable_mapping = {}
# Return true if this kind of IR value is "local", basically if it matches '%{{.*}}'.
@@ -919,9 +927,10 @@ class NamelessValue:
return self.global_ir_rhs_regexp is not None
# Return the IR prefix and check prefix we use for this kind or IR value,
- # e.g., (%, TMP) for locals.
+ # e.g., (%, TMP) for locals. If the IR prefix is a regex, return the prefix
+ # used in the IR output
def get_ir_prefix_from_ir_value_match(self, match):
- return self.ir_prefix, self.check_prefix
+ return re.search(self.ir_prefix, match[0])[0], self.check_prefix
# Return the IR regexp we use for this kind or IR value, e.g., [\w.-]+? for locals
def get_ir_regex_from_ir_value_re_match(self, match):
@@ -990,8 +999,16 @@ ir_nameless_values = [
NamelessValue(r"ATTR", "#", r"#", r"[0-9]+", None),
NamelessValue(r"ATTR", "#", r"attributes #", r"[0-9]+", r"{[^}]*}"),
NamelessValue(r"GLOB", "@", r"@", r"[0-9]+", None),
+ NamelessValue(r"GLOB", "@", r"@", r"[0-9]+", r".+", is_before_functions=True),
NamelessValue(
- r"GLOB", "@", r"@", r'[a-zA-Z0-9_$"\\.-]+', r".+", is_before_functions=True
+ r"GLOBNAMED",
+ "@",
+ r"@",
+ r"[a-zA-Z0-9_$\"\\.-]*[a-zA-Z_$\"\\.-][a-zA-Z0-9_$\"\\.-]*",
+ r".+",
+ is_before_functions=True,
+ match_literally=True,
+ interlaced_with_previous=True,
),
NamelessValue(r"DBG", "!", r"!dbg ", r"![0-9]+", None),
NamelessValue(r"DIASSIGNID", "!", r"!DIAssignID ", r"![0-9]+", None),
@@ -1003,6 +1020,19 @@ ir_nameless_values = [
NamelessValue(r"META", "!", r"metadata ", r"![0-9]+", None),
NamelessValue(r"META", "!", r"", r"![0-9]+", r"(?:distinct |)!.*"),
NamelessValue(r"ACC_GRP", "!", r"!llvm.access.group ", r"![0-9]+", None),
+ NamelessValue(r"META", "!", r"![a-z.]+ ", r"![0-9]+", None),
+]
+
+global_nameless_values = [
+ nameless_value
+ for nameless_value in ir_nameless_values
+ if nameless_value.global_ir_rhs_regexp is not None
+]
+# global variable names should be matched literally
+global_nameless_values_w_unstable_ids = [
+ nameless_value
+ for nameless_value in global_nameless_values
+ if not nameless_value.match_literally
]
asm_nameless_values = [
@@ -1037,6 +1067,7 @@ analyze_nameless_values = [
),
]
+
def createOrRegexp(old, new):
if not old:
return new
@@ -1060,7 +1091,7 @@ for nameless_value in ir_nameless_values:
if nameless_value.global_ir_rhs_regexp is not None:
match = "^" + match
IR_VALUE_REGEXP_STRING = createOrRegexp(IR_VALUE_REGEXP_STRING, match)
-IR_VALUE_REGEXP_SUFFIX = r"([,\s\(\)]|\Z)"
+IR_VALUE_REGEXP_SUFFIX = r"([,\s\(\)\}]|\Z)"
IR_VALUE_RE = re.compile(
IR_VALUE_REGEXP_PREFIX
+ r"("
@@ -1069,6 +1100,18 @@ IR_VALUE_RE = re.compile(
+ IR_VALUE_REGEXP_SUFFIX
)
+GLOBAL_VALUE_REGEXP_STRING = r""
+for nameless_value in global_nameless_values_w_unstable_ids:
+ match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp)
+ GLOBAL_VALUE_REGEXP_STRING = createOrRegexp(GLOBAL_VALUE_REGEXP_STRING, match)
+GLOBAL_VALUE_RE = re.compile(
+ IR_VALUE_REGEXP_PREFIX
+ + r"("
+ + GLOBAL_VALUE_REGEXP_STRING
+ + r")"
+ + IR_VALUE_REGEXP_SUFFIX
+)
+
# Build the regexp that matches an "ASM value" (currently only for --asm-show-inst comments).
ASM_VALUE_REGEXP_STRING = ""
for nameless_value in asm_nameless_values:
@@ -1101,6 +1144,7 @@ first_nameless_group_in_ir_value_match = 3
variable_group_in_ir_value_match = 3
attribute_group_in_ir_value_match = 4
+
# Check a match for IR_VALUE_RE and inspect it to determine if it was a local
# value, %..., global @..., debug number !dbg !..., etc. See the PREFIXES above.
def get_idx_from_ir_value_match(match):
@@ -1226,6 +1270,20 @@ def generalize_check_lines(
)
+def generalize_global_check_line(line, preserve_names, global_vars_seen):
+ [new_line] = generalize_check_lines_common(
+ [line],
+ False,
+ set(),
+ global_vars_seen,
+ global_nameless_values_w_unstable_ids,
+ GLOBAL_VALUE_RE,
+ False,
+ preserve_names,
+ )
+ return new_line
+
+
def generalize_asm_check_lines(lines, vars_seen, global_vars_seen):
return generalize_check_lines_common(
lines,
@@ -1251,6 +1309,7 @@ def generalize_analyze_check_lines(lines, vars_seen, global_vars_seen):
False,
)
+
def add_checks(
output_lines,
comment_marker,
@@ -1553,7 +1612,7 @@ def add_analyze_checks(
def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes):
- for nameless_value in itertools.chain(ir_nameless_values, asm_nameless_values):
+ for nameless_value in itertools.chain(global_nameless_values, asm_nameless_values):
if nameless_value.global_ir_rhs_regexp is None:
continue
@@ -1564,7 +1623,10 @@ def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes):
global_ir_value_re = re.compile(global_ir_value_re_str, flags=(re.M))
lines = []
for m in global_ir_value_re.finditer(raw_tool_output):
- lines.append(m.group(0))
+ # Attach the substring's start index so that CHECK lines
+ # can be sorted properly even if they are matched by different nameless values.
+ # This is relevant for GLOB and GLOBNAMED since they may appear interlaced.
+ lines.append((m.start(), m.group(0)))
for prefix in prefixes:
if glob_val_dict[prefix] is None:
@@ -1580,6 +1642,86 @@ def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes):
glob_val_dict[prefix][nameless_value.check_prefix] = lines
+def filter_globals_according_to_preference(
+ global_val_lines_w_index, global_vars_seen, nameless_value, global_check_setting
+):
+ if global_check_setting == "none":
+ return []
+ if global_check_setting == "all":
+ return global_val_lines_w_index
+ assert global_check_setting == "smart"
+
+ if nameless_value.check_key == "#":
+ # attribute sets are usually better checked by --check-attributes
+ return []
+
+ def extract(line, nv):
+ p = (
+ "^"
+ + nv.ir_prefix
+ + "("
+ + nv.ir_regexp
+ + ") = ("
+ + nv.global_ir_rhs_regexp
+ + ")"
+ )
+ match = re.match(p, line)
+ return (match.group(1), re.findall(nv.ir_regexp, match.group(2)))
+
+ transitively_visible = set()
+ contains_refs_to = {}
+
+ def add(var):
+ nonlocal transitively_visible
+ nonlocal contains_refs_to
+ if var in transitively_visible:
+ return
+ transitively_visible.add(var)
+ if not var in contains_refs_to:
+ return
+ for x in contains_refs_to[var]:
+ add(x)
+
+ for i, line in global_val_lines_w_index:
+ (var, refs) = extract(line, nameless_value)
+ contains_refs_to[var] = refs
+ for var, check_key in global_vars_seen:
+ if check_key != nameless_value.check_key:
+ continue
+ add(var)
+ return [
+ (i, line)
+ for i, line in global_val_lines_w_index
+ if extract(line, nameless_value)[0] in transitively_visible
+ ]
+
+
+METADATA_FILTERS = [
+ (
+ r"(?<=\")(\w+ )?(\w+ version )[\d.]+(?: \([^)]+\))?",
+ r"{{.*}}\2{{.*}}",
+ ), # preface with glob also, to capture optional CLANG_VENDOR
+ (r'(!DIFile\(filename: ".+", directory: )".+"', r"\1{{.*}}"),
+]
+METADATA_FILTERS_RE = [(re.compile(f), r) for (f, r) in METADATA_FILTERS]
+
+
+def filter_unstable_metadata(line):
+ for f, replacement in METADATA_FILTERS_RE:
+ line = f.sub(replacement, line)
+ return line
+
+
+def flush_current_checks(output_lines, new_lines_w_index, comment_marker):
+ if not new_lines_w_index:
+ return
+ output_lines.append(comment_marker + SEPARATOR)
+ new_lines_w_index.sort()
+ for _, line in new_lines_w_index:
+ output_lines.append(line)
+ new_lines_w_index.clear()
+
+
def add_global_checks(
glob_val_dict,
comment_marker,
@@ -1588,11 +1730,11 @@ def add_global_checks(
global_vars_seen_dict,
preserve_names,
is_before_functions,
+ global_check_setting,
):
printed_prefixes = set()
- for nameless_value in ir_nameless_values:
- if nameless_value.global_ir_rhs_regexp is None:
- continue
+ output_lines_loc = {} # Allows GLOB and GLOBNAMED to be sorted correctly
+ for nameless_value in global_nameless_values:
if nameless_value.is_before_functions != is_before_functions:
continue
for p in prefix_list:
@@ -1616,26 +1758,41 @@ def add_global_checks(
check_lines = []
global_vars_seen_before = [key for key in global_vars_seen.keys()]
- for line in glob_val_dict[checkprefix][nameless_value.check_prefix]:
+ lines_w_index = glob_val_dict[checkprefix][nameless_value.check_prefix]
+ lines_w_index = filter_globals_according_to_preference(
+ lines_w_index,
+ global_vars_seen_before,
+ nameless_value,
+ global_check_setting,
+ )
+ for i, line in lines_w_index:
if _global_value_regex:
matched = False
for regex in _global_value_regex:
- if re.match("^@" + regex + " = ", line):
+ if re.match("^@" + regex + " = ", line) or re.match(
+ "^!" + regex + " = ", line
+ ):
matched = True
break
if not matched:
continue
- tmp = generalize_check_lines(
- [line], False, set(), global_vars_seen, preserve_names
+ new_line = generalize_global_check_line(
+ line, preserve_names, global_vars_seen
)
- check_line = "%s %s: %s" % (comment_marker, checkprefix, tmp[0])
- check_lines.append(check_line)
+ new_line = filter_unstable_metadata(new_line)
+ check_line = "%s %s: %s" % (comment_marker, checkprefix, new_line)
+ check_lines.append((i, check_line))
if not check_lines:
continue
- output_lines.append(comment_marker + SEPARATOR)
+ if not checkprefix in output_lines_loc:
+ output_lines_loc[checkprefix] = []
+ if not nameless_value.interlaced_with_previous:
+ flush_current_checks(
+ output_lines, output_lines_loc[checkprefix], comment_marker
+ )
for check_line in check_lines:
- output_lines.append(check_line)
+ output_lines_loc[checkprefix].append(check_line)
printed_prefixes.add((checkprefix, nameless_value.check_prefix))
@@ -1646,6 +1803,16 @@ def add_global_checks(
break
if printed_prefixes:
+ for p in prefix_list:
+ if p[0] is None:
+ continue
+ for checkprefix in p[0]:
+ if checkprefix not in output_lines_loc:
+ continue
+ flush_current_checks(
+ output_lines, output_lines_loc[checkprefix], comment_marker
+ )
+ break
output_lines.append(comment_marker + SEPARATOR)
return printed_prefixes
@@ -1712,6 +1879,15 @@ def get_autogennote_suffix(parser, args):
):
continue
value = getattr(args, action.dest)
+ if action.dest == "check_globals":
+ default_value = "none" if args.version < 4 else "smart"
+ if value == default_value:
+ continue
+ autogenerated_note_args += action.option_strings[0] + " "
+ if args.version < 4 and value == "all":
+ continue
+ autogenerated_note_args += "%s " % value
+ continue
if action.const is not None: # action stores a constant (usually True/False)
# Skip actions with different constant values (this happens with boolean
# --foo/--no-foo options)