[utils] remove ability to generate llc check lines from update_test_checks.py

The dream of a unified check-line auto-generator for all phases of compilation is dead.
The llc script has already diverged to be better at its goal, so having 2 scripts that
do almost the same thing just causes confusion. Now, this script will only work with 
opt to produce check lines for IR transforms.

llvm-svn: 305208
This commit is contained in:
Sanjay Patel 2017-06-12 17:44:30 +00:00
parent b5fe855dfb
commit cae64a0497
1 changed files with 27 additions and 69 deletions

View File

@ -1,13 +1,13 @@
#!/usr/bin/env python2.7 #!/usr/bin/env python2.7
"""A script to generate FileCheck statements for regression tests. """A script to generate FileCheck statements for 'opt' regression tests.
This script is a utility to update LLVM opt or llc test cases with new This script is a utility to update LLVM opt test cases with new
FileCheck patterns. It can either update all of the tests in the file or FileCheck patterns. It can either update all of the tests in the file or
a single test function. a single test function.
Example usage: Example usage:
$ update_test_checks.py --tool=../bin/opt test/foo.ll $ update_test_checks.py --opt=../bin/opt test/foo.ll
Workflow: Workflow:
1. Make a compiler patch that requires updating some number of FileCheck lines 1. Make a compiler patch that requires updating some number of FileCheck lines
@ -45,23 +45,11 @@ ADVERT = '; NOTE: Assertions have been autogenerated by '
SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)') SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)')
SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M) SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M)
SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M) SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M)
SCRUB_X86_SHUFFLES_RE = (
re.compile(
r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$',
flags=re.M))
SCRUB_X86_SP_RE = re.compile(r'\d+\(%(esp|rsp)\)')
SCRUB_X86_RIP_RE = re.compile(r'[.\w]+\(%rip\)')
SCRUB_X86_LCP_RE = re.compile(r'\.LCPI[0-9]+_[0-9]+')
SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n') SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n')
SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*') SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*')
RUN_LINE_RE = re.compile('^\s*;\s*RUN:\s*(.*)$') RUN_LINE_RE = re.compile('^\s*;\s*RUN:\s*(.*)$')
IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@([\w-]+)\s*\(') IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@([\w-]+)\s*\(')
LLC_FUNCTION_RE = re.compile(
r'^_?(?P<func>[^:]+):[ \t]*#+[ \t]*@(?P=func)\n[^:]*?'
r'(?P<body>^##?[ \t]+[^:]+:.*?)\s*'
r'^\s*(?:[^:\n]+?:\s*\n\s*\.size|\.cfi_endproc|\.globl|\.comm|\.(?:sub)?section)',
flags=(re.M | re.S))
OPT_FUNCTION_RE = re.compile( OPT_FUNCTION_RE = re.compile(
r'^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[\w-]+?)\s*\(' r'^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[\w-]+?)\s*\('
r'(\s+)?[^)]*[^{]*\{\n(?P<body>.*?)^\}$', r'(\s+)?[^)]*[^{]*\{\n(?P<body>.*?)^\}$',
@ -76,29 +64,14 @@ IR_VALUE_RE = re.compile(r'(\s+)%([\w\.]+?)([,\s\(\)]|\Z)')
# Invoke the tool that is being tested. # Invoke the tool that is being tested.
def invoke_tool(args, cmd_args, ir): def invoke_tool(args, cmd_args, ir):
with open(ir) as ir_file: with open(ir) as ir_file:
stdout = subprocess.check_output(args.tool_binary + ' ' + cmd_args, stdout = subprocess.check_output(args.opt_binary + ' ' + cmd_args,
shell=True, stdin=ir_file) shell=True, stdin=ir_file)
# Fix line endings to unix CR style. # Fix line endings to unix CR style.
stdout = stdout.replace('\r\n', '\n') stdout = stdout.replace('\r\n', '\n')
return stdout return stdout
# FIXME: Separate the x86-specific scrubbers, so this can be used for other targets. def scrub_body(body, opt_basename):
def scrub_asm(asm):
# Detect shuffle asm comments and hide the operands in favor of the comments.
asm = SCRUB_X86_SHUFFLES_RE.sub(r'\1 {{.*#+}} \2', asm)
# Generically match the stack offset of a memory operand.
asm = SCRUB_X86_SP_RE.sub(r'{{[0-9]+}}(%\1)', asm)
# Generically match a RIP-relative memory operand.
asm = SCRUB_X86_RIP_RE.sub(r'{{.*}}(%rip)', asm)
# Generically match a LCP symbol.
asm = SCRUB_X86_LCP_RE.sub(r'{{\.LCPI.*}}', asm)
# Strip kill operands inserted into the asm.
asm = SCRUB_KILL_COMMENT_RE.sub('', asm)
return asm
def scrub_body(body, tool_basename):
# Scrub runs of whitespace out of the assembly, but leave the leading # Scrub runs of whitespace out of the assembly, but leave the leading
# whitespace in place. # whitespace in place.
body = SCRUB_WHITESPACE_RE.sub(r' ', body) body = SCRUB_WHITESPACE_RE.sub(r' ', body)
@ -106,22 +79,17 @@ def scrub_body(body, tool_basename):
body = string.expandtabs(body, 2) body = string.expandtabs(body, 2)
# Strip trailing whitespace. # Strip trailing whitespace.
body = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', body) body = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', body)
if tool_basename == "llc":
body = scrub_asm(body)
return body return body
# Build up a dictionary of all the function bodies. # Build up a dictionary of all the function bodies.
def build_function_body_dictionary(raw_tool_output, prefixes, func_dict, verbose, tool_basename): def build_function_body_dictionary(raw_tool_output, prefixes, func_dict, verbose, opt_basename):
if tool_basename == "llc":
func_regex = LLC_FUNCTION_RE
else:
func_regex = OPT_FUNCTION_RE func_regex = OPT_FUNCTION_RE
for m in func_regex.finditer(raw_tool_output): for m in func_regex.finditer(raw_tool_output):
if not m: if not m:
continue continue
func = m.group('func') func = m.group('func')
scrubbed_body = scrub_body(m.group('body'), tool_basename) scrubbed_body = scrub_body(m.group('body'), opt_basename)
if func.startswith('stress'): if func.startswith('stress'):
# We only use the last line of the function body for stress tests. # We only use the last line of the function body for stress tests.
scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:]) scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:])
@ -188,11 +156,8 @@ def genericize_check_lines(lines):
return lines return lines
def add_checks(output_lines, prefix_list, func_dict, func_name, tool_basename): def add_checks(output_lines, prefix_list, func_dict, func_name, opt_basename):
# Select a label format based on the whether we're checking asm or IR. # Label format is based on IR string.
if tool_basename == "llc":
check_label_format = "; %s-LABEL: %s:"
else:
check_label_format = "; %s-LABEL: @%s(" check_label_format = "; %s-LABEL: @%s("
printed_prefixes = [] printed_prefixes = []
@ -212,7 +177,6 @@ def add_checks(output_lines, prefix_list, func_dict, func_name, tool_basename):
# For IR output, change all defs to FileCheck variables, so we're immune # For IR output, change all defs to FileCheck variables, so we're immune
# to variable naming fashions. # to variable naming fashions.
if tool_basename == "opt":
func_body = genericize_check_lines(func_body) func_body = genericize_check_lines(func_body)
# This could be selectively enabled with an optional invocation argument. # This could be selectively enabled with an optional invocation argument.
@ -226,19 +190,13 @@ def add_checks(output_lines, prefix_list, func_dict, func_name, tool_basename):
# output_lines.append('; %s: %s' % (checkprefix, func_body[0])) # output_lines.append('; %s: %s' % (checkprefix, func_body[0]))
# is_blank_line = False # is_blank_line = False
# For llc tests, there may be asm directives between the label and the
# first checked line (most likely that first checked line is "# BB#0").
if tool_basename == "opt":
is_blank_line = False is_blank_line = False
else:
is_blank_line = True;
for func_line in func_body: for func_line in func_body:
if func_line.strip() == '': if func_line.strip() == '':
is_blank_line = True is_blank_line = True
continue continue
# Do not waste time checking IR comments. # Do not waste time checking IR comments.
if tool_basename == "opt":
func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line) func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line)
# Skip blank lines instead of checking them. # Skip blank lines instead of checking them.
@ -275,8 +233,8 @@ def main():
parser = argparse.ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter) parser = argparse.ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
parser.add_argument('-v', '--verbose', action='store_true', parser.add_argument('-v', '--verbose', action='store_true',
help='Show verbose output') help='Show verbose output')
parser.add_argument('--tool-binary', default='llc', parser.add_argument('--opt-binary', default='opt',
help='The tool used to generate the test case') help='The opt binary used to generate the test case')
parser.add_argument( parser.add_argument(
'--function', help='The function in the test file to update') '--function', help='The function in the test file to update')
parser.add_argument('tests', nargs='+') parser.add_argument('tests', nargs='+')
@ -284,9 +242,9 @@ def main():
autogenerated_note = (ADVERT + 'utils/' + os.path.basename(__file__)) autogenerated_note = (ADVERT + 'utils/' + os.path.basename(__file__))
tool_basename = os.path.basename(args.tool_binary) opt_basename = os.path.basename(args.opt_binary)
if (tool_basename != "llc" and tool_basename != "opt"): if (opt_basename != "opt"):
print >>sys.stderr, 'ERROR: Unexpected tool name: ' + tool_basename print >>sys.stderr, 'ERROR: Unexpected opt name: ' + opt_basename
sys.exit(1) sys.exit(1)
for test in args.tests: for test in args.tests:
@ -313,15 +271,15 @@ def main():
for l in run_lines: for l in run_lines:
(tool_cmd, filecheck_cmd) = tuple([cmd.strip() for cmd in l.split('|', 1)]) (tool_cmd, filecheck_cmd) = tuple([cmd.strip() for cmd in l.split('|', 1)])
if not tool_cmd.startswith(tool_basename + ' '): if not tool_cmd.startswith(opt_basename + ' '):
print >>sys.stderr, 'WARNING: Skipping non-%s RUN line: %s' % (tool_basename, l) print >>sys.stderr, 'WARNING: Skipping non-%s RUN line: %s' % (opt_basename, l)
continue continue
if not filecheck_cmd.startswith('FileCheck '): if not filecheck_cmd.startswith('FileCheck '):
print >>sys.stderr, 'WARNING: Skipping non-FileChecked RUN line: ' + l print >>sys.stderr, 'WARNING: Skipping non-FileChecked RUN line: ' + l
continue continue
tool_cmd_args = tool_cmd[len(tool_basename):].strip() tool_cmd_args = tool_cmd[len(opt_basename):].strip()
tool_cmd_args = tool_cmd_args.replace('< %s', '').replace('%s', '').strip() tool_cmd_args = tool_cmd_args.replace('< %s', '').replace('%s', '').strip()
check_prefixes = [item for m in CHECK_PREFIX_RE.finditer(filecheck_cmd) check_prefixes = [item for m in CHECK_PREFIX_RE.finditer(filecheck_cmd)
@ -337,13 +295,13 @@ def main():
for prefixes, _ in prefix_list: for prefixes, _ in prefix_list:
for prefix in prefixes: for prefix in prefixes:
func_dict.update({prefix: dict()}) func_dict.update({prefix: dict()})
for prefixes, tool_args in prefix_list: for prefixes, opt_args in prefix_list:
if args.verbose: if args.verbose:
print >>sys.stderr, 'Extracted tool cmd: ' + tool_basename + ' ' + tool_args print >>sys.stderr, 'Extracted opt cmd: ' + opt_basename + ' ' + opt_args
print >>sys.stderr, 'Extracted FileCheck prefixes: ' + str(prefixes) print >>sys.stderr, 'Extracted FileCheck prefixes: ' + str(prefixes)
raw_tool_output = invoke_tool(args, tool_args, test) raw_tool_output = invoke_tool(args, opt_args, test)
build_function_body_dictionary(raw_tool_output, prefixes, func_dict, args.verbose, tool_basename) build_function_body_dictionary(raw_tool_output, prefixes, func_dict, args.verbose, opt_basename)
is_in_function = False is_in_function = False
is_in_function_start = False is_in_function_start = False
@ -364,7 +322,7 @@ def main():
continue continue
# Print out the various check lines here. # Print out the various check lines here.
output_lines = add_checks(output_lines, prefix_list, func_dict, name, tool_basename) output_lines = add_checks(output_lines, prefix_list, func_dict, name, opt_basename)
is_in_function_start = False is_in_function_start = False
if is_in_function: if is_in_function: