diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/asm-show-inst.ll.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/asm-show-inst.ll.expected --- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/asm-show-inst.ll.expected +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/asm-show-inst.ll.expected @@ -6,17 +6,17 @@ define i8 @add_i8(i8 %a) nounwind { ; VERBOSE-LABEL: add_i8: ; VERBOSE: # %bb.0: -; VERBOSE-NEXT: movb {{[0-9]+}}(%esp), %al # -; VERBOSE-NEXT: # +; VERBOSE-NEXT: movb {{[0-9]+}}(%esp), %al # +; VERBOSE-NEXT: # ; VERBOSE-NEXT: # -; VERBOSE-NEXT: # +; VERBOSE-NEXT: # ; VERBOSE-NEXT: # -; VERBOSE-NEXT: # > -; VERBOSE-NEXT: addb $2, %al # > +; VERBOSE-NEXT: addb $2, %al # > -; VERBOSE-NEXT: retl # > +; VERBOSE-NEXT: retl # > ; ; CHECK-LABEL: add_i8: ; CHECK: # %bb.0: @@ -30,19 +30,19 @@ define i32 @add_i32(i32 %a) nounwind { ; VERBOSE-LABEL: add_i32: ; VERBOSE: # %bb.0: -; VERBOSE-NEXT: movl {{[0-9]+}}(%esp), %eax # -; VERBOSE-NEXT: # +; VERBOSE-NEXT: movl {{[0-9]+}}(%esp), %eax # +; VERBOSE-NEXT: # ; VERBOSE-NEXT: # -; VERBOSE-NEXT: # +; VERBOSE-NEXT: # ; VERBOSE-NEXT: # -; VERBOSE-NEXT: # > -; VERBOSE-NEXT: addl $2, %eax # -; VERBOSE-NEXT: # +; VERBOSE-NEXT: # > +; VERBOSE-NEXT: addl $2, %eax # +; VERBOSE-NEXT: # ; VERBOSE-NEXT: # > -; VERBOSE-NEXT: retl # > +; VERBOSE-NEXT: retl # > ; ; CHECK-LABEL: add_i32: ; CHECK: # %bb.0: diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -423,9 +423,8 @@ asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) return asm - # Returns a tuple of a scrub function and a function regex. Scrub function is -# used to alter function body in some way, for example, remove traling spaces. +# used to alter function body in some way, for example, remove trailing spaces. # Function regex is used to match function name, body, etc. in raw llc output. def get_run_handler(triple): target_handlers = { @@ -482,10 +481,9 @@ ##### Generator of assembly CHECK lines def add_checks(output_lines, comment_marker, prefix_list, func_dict, - func_name, is_filtered): + func_name, global_vars_seen_dict, is_filtered): # Label format is based on ASM string. check_label_format = '{} %s-LABEL: %s%s%s'.format(comment_marker) - global_vars_seen_dict = {} common.add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, True, False, - global_vars_seen_dict, is_filtered = is_filtered) + global_vars_seen_dict, is_filtered=is_filtered) diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py --- a/llvm/utils/UpdateTestChecks/common.py +++ b/llvm/utils/UpdateTestChecks/common.py @@ -3,6 +3,7 @@ import argparse import copy import glob +import itertools import os import re import subprocess @@ -596,7 +597,7 @@ 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 @@ 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 @@ # 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 @@ NamelessValue(r'META' , '!' , None , r'' , r'![0-9]+' , None , r'(?:distinct |)!.*' , False) , ] +asm_nameless_values = [ + NamelessValue(r'MCINST', 'Inst#', None, '\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 @@ 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 @@ 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 @@ 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 @@ 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 @@ # 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 @@ 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 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: diff --git a/llvm/utils/UpdateTestChecks/isel.py b/llvm/utils/UpdateTestChecks/isel.py --- a/llvm/utils/UpdateTestChecks/isel.py +++ b/llvm/utils/UpdateTestChecks/isel.py @@ -48,10 +48,10 @@ ##### Generator of iSel CHECK lines -def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, is_filtered): +def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, + global_vars_seen_dict, is_filtered): # Label format is based on iSel string. check_label_format = '{} %s-LABEL: %s%s%s'.format(comment_marker) - global_vars_seen_dict = {} common.add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, True, False, global_vars_seen_dict, is_filtered = is_filtered) diff --git a/llvm/utils/update_cc_test_checks.py b/llvm/utils/update_cc_test_checks.py --- a/llvm/utils/update_cc_test_checks.py +++ b/llvm/utils/update_cc_test_checks.py @@ -349,7 +349,7 @@ else: asm.add_checks(my_output_lines, '//', prefixes, - func_dict, func, + func_dict, func, global_vars_seen_dict, is_filtered=builder.is_filtered()) if ti.args.check_globals: diff --git a/llvm/utils/update_llc_test_checks.py b/llvm/utils/update_llc_test_checks.py --- a/llvm/utils/update_llc_test_checks.py +++ b/llvm/utils/update_llc_test_checks.py @@ -139,6 +139,7 @@ builder.process_run_line(function_re, scrubber, raw_tool_output, prefixes, True) func_dict = builder.finish_and_get_func_dict() + global_vars_seen_dict = {} is_in_function = False is_in_function_start = False @@ -169,6 +170,7 @@ output_type.add_checks(my_output_lines, check_indent + ';', prefixes, func_dict, func, + global_vars_seen_dict, is_filtered=builder.is_filtered())) else: for input_info in ti.iterlines(output_lines): @@ -185,8 +187,8 @@ # Print out the various check lines here. output_type.add_checks(output_lines, check_indent + ';', run_list, - func_dict, func_name, - is_filtered=builder.is_filtered()) + func_dict, func_name, global_vars_seen_dict, + is_filtered=builder.is_filtered()) is_in_function_start = False if is_in_function: