aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils/UpdateTestChecks/common.py
diff options
context:
space:
mode:
authorAlex Richardson <alexrichardson@google.com>2022-05-12 10:21:55 +0000
committerAlex Richardson <alexrichardson@google.com>2022-05-14 14:15:37 +0000
commit37a68497f1176cab3b2100c3bcb861e063cfaead (patch)
treefab2f872a3d00e531d9778acbc6db76f26f0e60b /llvm/utils/UpdateTestChecks/common.py
parentf421659286af9ae2d4f01cd1f829832fb5c2bc8b (diff)
downloadllvm-37a68497f1176cab3b2100c3bcb861e063cfaead.zip
llvm-37a68497f1176cab3b2100c3bcb861e063cfaead.tar.gz
llvm-37a68497f1176cab3b2100c3bcb861e063cfaead.tar.bz2
[update_llc_test_checks] Use FileCheck captures for MCInst/MCReg output
To avoid test churn when backends add/rename new instructions/registers, it makes sense to use FileCheck captures for the exact MCInst/Reg number. This is motivated by D125307, where I use --asm-show-inst to differentiate the output for multiple instructions with the same mnemonic. This does not quite fix the churn issue yet: While files with the generated checks will be immune to the numbers changing, the update script test still suffers from this problem since the number is encoded in the FileCheck variable name. I plan to address this in a follow-up patch. Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D125307
Diffstat (limited to 'llvm/utils/UpdateTestChecks/common.py')
-rw-r--r--llvm/utils/UpdateTestChecks/common.py97
1 files changed, 70 insertions, 27 deletions
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index d9be97c..c6553b1 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -3,6 +3,7 @@ from __future__ import print_function
import argparse
import copy
import glob
+import itertools
import os
import re
import subprocess
@@ -596,7 +597,7 @@ SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*')
class NamelessValue:
def __init__(self, check_prefix, check_key, ir_prefix, global_ir_prefix, global_ir_prefix_regexp,
- ir_regexp, global_ir_rhs_regexp, is_before_functions):
+ ir_regexp, global_ir_rhs_regexp, is_before_functions, is_number=False):
self.check_prefix = check_prefix
self.check_key = check_key
self.ir_prefix = ir_prefix
@@ -605,6 +606,7 @@ class NamelessValue:
self.ir_regexp = ir_regexp
self.global_ir_rhs_regexp = global_ir_rhs_regexp
self.is_before_functions = is_before_functions
+ self.is_number = is_number
# Return true if this kind of IR value is "local", basically if it matches '%{{.*}}'.
def is_local_def_ir_value_match(self, match):
@@ -635,23 +637,29 @@ class NamelessValue:
# for backwards compatibility we check locals with '.*'
varname = get_value_name(var, self.check_prefix)
prefix = self.get_ir_prefix_from_ir_value_match(match)[0]
- regex = self.get_ir_regex_from_ir_value_re_match(match)
+ if self.is_number:
+ regex = '' # always capture a number in the default format
+ capture_start = '[[#'
+ else:
+ regex = self.get_ir_regex_from_ir_value_re_match(match)
+ capture_start = '[['
if self.is_local_def_ir_value_match(match):
- return '[[' + varname + ':' + prefix + regex + ']]'
- return prefix + '[[' + varname + ':' + regex + ']]'
+ return capture_start + varname + ':' + prefix + regex + ']]'
+ return prefix + capture_start + varname + ':' + regex + ']]'
# Use a FileCheck variable.
def get_value_use(self, var, match, var_prefix=None):
if var_prefix is None:
var_prefix = self.check_prefix
+ capture_start = '[[#' if self.is_number else '[['
if self.is_local_def_ir_value_match(match):
- return '[[' + get_value_name(var, var_prefix) + ']]'
+ return capture_start + get_value_name(var, var_prefix) + ']]'
prefix = self.get_ir_prefix_from_ir_value_match(match)[0]
- return prefix + '[[' + get_value_name(var, var_prefix) + ']]'
+ return prefix + capture_start + get_value_name(var, var_prefix) + ']]'
# Description of the different "unnamed" values we match in the IR, e.g.,
# (local) ssa values, (debug) metadata, etc.
-nameless_values = [
+ir_nameless_values = [
NamelessValue(r'TMP' , '%' , r'%' , None , None , r'[\w$.-]+?' , None , False) ,
NamelessValue(r'ATTR' , '#' , r'#' , None , None , r'[0-9]+' , None , False) ,
NamelessValue(r'ATTR' , '#' , None , r'attributes #' , r'[0-9]+' , None , r'{[^}]*}' , False) ,
@@ -666,6 +674,11 @@ nameless_values = [
NamelessValue(r'META' , '!' , None , r'' , r'![0-9]+' , None , r'(?:distinct |)!.*' , False) ,
]
+asm_nameless_values = [
+ NamelessValue(r'MCINST', 'Inst#', None, '<MCInst #', r'\d+', None, r'.+', False, True),
+ NamelessValue(r'MCREG', 'Reg:', None, '<MCOperand Reg:', r'\d+', None, r'.+', False, True),
+]
+
def createOrRegexp(old, new):
if not old:
return new
@@ -684,7 +697,7 @@ def createPrefixMatch(prefix_str, prefix_re):
# other locations will need adjustment as well.
IR_VALUE_REGEXP_PREFIX = r'(\s*)'
IR_VALUE_REGEXP_STRING = r''
-for nameless_value in nameless_values:
+for nameless_value in ir_nameless_values:
lcl_match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp)
glb_match = createPrefixMatch(nameless_value.global_ir_prefix, nameless_value.global_ir_prefix_regexp)
assert((lcl_match or glb_match) and not (lcl_match and glb_match))
@@ -695,6 +708,15 @@ for nameless_value in nameless_values:
IR_VALUE_REGEXP_SUFFIX = r'([,\s\(\)]|\Z)'
IR_VALUE_RE = re.compile(IR_VALUE_REGEXP_PREFIX + r'(' + IR_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:
+ glb_match = createPrefixMatch(nameless_value.global_ir_prefix, nameless_value.global_ir_prefix_regexp)
+ assert not nameless_value.ir_prefix and not nameless_value.ir_regexp
+ ASM_VALUE_REGEXP_STRING = createOrRegexp(ASM_VALUE_REGEXP_STRING, glb_match)
+ASM_VALUE_REGEXP_SUFFIX = r'([>\s]|\Z)'
+ASM_VALUE_RE = re.compile(r'((?:#|//)\s*)' + '(' + ASM_VALUE_REGEXP_STRING + ')' + ASM_VALUE_REGEXP_SUFFIX)
+
# The entire match is group 0, the prefix has one group (=1), the entire
# IR_VALUE_REGEXP_STRING is one group (=2), and then the nameless values start.
first_nameless_group_in_ir_value_match = 3
@@ -738,7 +760,9 @@ def get_value_name(var, check_prefix):
var = var.replace('-', '_')
return var.upper()
-def generalize_check_lines(lines, is_analyze, vars_seen, global_vars_seen):
+def generalize_check_lines_common(lines, is_analyze, vars_seen,
+ global_vars_seen, nameless_values,
+ nameless_value_regex, is_asm):
# This gets called for each match that occurs in
# a line. We transform variables we haven't seen
# into defs, and variables we have seen into uses.
@@ -771,26 +795,38 @@ def generalize_check_lines(lines, is_analyze, vars_seen, global_vars_seen):
lines_with_def = []
for i, line in enumerate(lines):
- # An IR variable named '%.' matches the FileCheck regex string.
- line = line.replace('%.', '%dot')
- for regex in _global_hex_value_regex:
- if re.match('^@' + regex + ' = ', line):
- line = re.sub(r'\bi([0-9]+) ([0-9]+)',
- lambda m : 'i' + m.group(1) + ' [[#' + hex(int(m.group(2))) + ']]',
- line)
- break
- # Ignore any comments, since the check lines will too.
- scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line)
- lines[i] = scrubbed_line
- if not is_analyze:
+ if not is_asm:
+ # An IR variable named '%.' matches the FileCheck regex string.
+ line = line.replace('%.', '%dot')
+ for regex in _global_hex_value_regex:
+ if re.match('^@' + regex + ' = ', line):
+ line = re.sub(r'\bi([0-9]+) ([0-9]+)',
+ lambda m : 'i' + m.group(1) + ' [[#' + hex(int(m.group(2))) + ']]',
+ line)
+ break
+ # Ignore any comments, since the check lines will too.
+ scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line)
+ lines[i] = scrubbed_line
+ if is_asm or not is_analyze:
# It can happen that two matches are back-to-back and for some reason sub
# will not replace both of them. For now we work around this by
# substituting until there is no more match.
changed = True
while changed:
- (lines[i], changed) = IR_VALUE_RE.subn(transform_line_vars, lines[i], count=1)
+ (lines[i], changed) = nameless_value_regex.subn(transform_line_vars,
+ lines[i], count=1)
return lines
+# Replace IR value defs and uses with FileCheck variables.
+def generalize_check_lines(lines, is_analyze, vars_seen, global_vars_seen):
+ return generalize_check_lines_common(lines, is_analyze, vars_seen,
+ global_vars_seen, ir_nameless_values,
+ IR_VALUE_RE, False)
+
+def generalize_asm_check_lines(lines, vars_seen, global_vars_seen):
+ return generalize_check_lines_common(lines, False, vars_seen,
+ global_vars_seen, asm_nameless_values,
+ ASM_VALUE_RE, True)
def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_backend, 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.
@@ -843,7 +879,8 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
if attrs:
output_lines.append('%s %s: Function Attrs: %s' % (comment_marker, checkprefix, attrs))
args_and_sig = str(func_dict[checkprefix][func_name].args_and_sig)
- args_and_sig = generalize_check_lines([args_and_sig], is_analyze, vars_seen, global_vars_seen)[0]
+ if args_and_sig:
+ args_and_sig = generalize_check_lines([args_and_sig], is_analyze, vars_seen, global_vars_seen)[0]
func_name_separator = func_dict[checkprefix][func_name].func_name_separator
if '[[' in args_and_sig:
output_lines.append(check_label_format % (checkprefix, func_name, '', func_name_separator))
@@ -864,13 +901,19 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
body_start = 0
else:
output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
- for func_line in func_body[body_start:]:
+ func_lines = generalize_asm_check_lines(func_body[body_start:],
+ vars_seen, global_vars_seen)
+ for func_line in func_lines:
if func_line.strip() == '':
output_lines.append('%s %s-EMPTY:' % (comment_marker, checkprefix))
else:
check_suffix = '-NEXT' if not is_filtered else ''
output_lines.append('%s %s%s: %s' % (comment_marker, checkprefix,
check_suffix, func_line))
+ # Remember new global variables we have not seen before
+ for key in global_vars_seen:
+ if key not in global_vars_seen_before:
+ global_vars_seen_dict[checkprefix][key] = global_vars_seen[key]
break
# For IR output, change all defs to FileCheck variables, so we're immune
@@ -911,7 +954,7 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
# line of code in the test function.
output_lines.append(comment_marker)
- # Remembe new global variables we have not seen before
+ # Remember new global variables we have not seen before
for key in global_vars_seen:
if key not in global_vars_seen_before:
global_vars_seen_dict[checkprefix][key] = global_vars_seen[key]
@@ -935,7 +978,7 @@ def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, fun
is_filtered)
def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes):
- for nameless_value in nameless_values:
+ for nameless_value in itertools.chain(ir_nameless_values, asm_nameless_values):
if nameless_value.global_ir_prefix is None:
continue
@@ -963,7 +1006,7 @@ def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes):
def add_global_checks(glob_val_dict, comment_marker, prefix_list, output_lines, global_vars_seen_dict, is_analyze, is_before_functions):
printed_prefixes = set()
- for nameless_value in nameless_values:
+ for nameless_value in ir_nameless_values:
if nameless_value.global_ir_prefix is None:
continue
if nameless_value.is_before_functions != is_before_functions: