mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-28 18:54:55 +00:00
reorganize llc checks script to allow more flexibility, part 2; NFCI
The goal is to enhance this script to be used with opt and clang: Break 'main' into functions and change variable names to be more generic because we want to handle more than x86 asm output. llvm-svn: 264307
This commit is contained in:
parent
fe26864440
commit
f3c5f46ed1
@ -9,6 +9,8 @@ a single test function.
|
||||
|
||||
import argparse
|
||||
import itertools
|
||||
# Could be used to advertise this file's name ("autogenerated_note").
|
||||
# import os
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
@ -40,7 +42,7 @@ SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n')
|
||||
RUN_LINE_RE = re.compile('^\s*;\s*RUN:\s*(.*)$')
|
||||
IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@(\w+)\s*\(')
|
||||
ASM_FUNCTION_RE = re.compile(
|
||||
r'^_?(?P<f>[^:]+):[ \t]*#+[ \t]*@(?P=f)\n[^:]*?'
|
||||
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))
|
||||
@ -67,6 +69,70 @@ def scrub_asm(asm):
|
||||
return asm
|
||||
|
||||
|
||||
# Build up a dictionary of all the function bodies.
|
||||
def build_function_body_dictionary(raw_tool_output, prefixes, func_dict, verbose):
|
||||
for m in ASM_FUNCTION_RE.finditer(raw_tool_output):
|
||||
if not m:
|
||||
continue
|
||||
func = m.group('func')
|
||||
scrubbed_body = scrub_asm(m.group('body'))
|
||||
if func.startswith('stress'):
|
||||
# We only use the last line of the function body for stress tests.
|
||||
scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:])
|
||||
if verbose:
|
||||
print >>sys.stderr, 'Processing function: ' + func
|
||||
for l in scrubbed_body.splitlines():
|
||||
print >>sys.stderr, ' ' + l
|
||||
for prefix in prefixes:
|
||||
if func in func_dict[prefix] and func_dict[prefix][func] != scrubbed_body:
|
||||
if prefix == prefixes[-1]:
|
||||
print >>sys.stderr, ('WARNING: Found conflicting asm under the '
|
||||
'same prefix: %r!' % (prefix,))
|
||||
else:
|
||||
func_dict[prefix][func] = None
|
||||
continue
|
||||
|
||||
func_dict[prefix][func] = scrubbed_body
|
||||
|
||||
|
||||
def add_checks(output_lines, prefix_list, func_dict, func_name):
|
||||
printed_prefixes = []
|
||||
for checkprefixes, _ in prefix_list:
|
||||
for checkprefix in checkprefixes:
|
||||
if checkprefix in printed_prefixes:
|
||||
break
|
||||
if not func_dict[checkprefix][func_name]:
|
||||
continue
|
||||
# Add some space between different check prefixes.
|
||||
if len(printed_prefixes) != 0:
|
||||
output_lines.append(';')
|
||||
printed_prefixes.append(checkprefix)
|
||||
output_lines.append('; %s-LABEL: %s:' % (checkprefix, func_name))
|
||||
func_body = func_dict[checkprefix][func_name].splitlines()
|
||||
output_lines.append('; %s: %s' % (checkprefix, func_body[0]))
|
||||
for func_line in func_body[1:]:
|
||||
output_lines.append('; %s-NEXT: %s' % (checkprefix, func_line))
|
||||
# Add space between different check prefixes and the first line of code.
|
||||
# output_lines.append(';')
|
||||
break
|
||||
return output_lines
|
||||
|
||||
|
||||
def should_add_line_to_output(input_line, prefix_set):
|
||||
# Skip any blank comment lines in the IR.
|
||||
if input_line.strip() == ';':
|
||||
return False
|
||||
# Skip any blank lines in the IR.
|
||||
#if input_line.strip() == '':
|
||||
# return False
|
||||
# And skip any CHECK lines. We're building our own.
|
||||
m = CHECK_RE.match(input_line)
|
||||
if m and m.group(1) in prefix_set:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
@ -78,23 +144,25 @@ def main():
|
||||
parser.add_argument('tests', nargs='+')
|
||||
args = parser.parse_args()
|
||||
|
||||
# FIXME: we don't need to hardcode this name.
|
||||
autogenerated_note = ('; NOTE: Assertions have been autogenerated by '
|
||||
'utils/update_llc_test_checks.py')
|
||||
# + os.path.basename(__file__))
|
||||
|
||||
for test in args.tests:
|
||||
if args.verbose:
|
||||
print >>sys.stderr, 'Scanning for RUN lines in test file: %s' % (test,)
|
||||
with open(test) as f:
|
||||
test_lines = [l.rstrip() for l in f]
|
||||
input_lines = [l.rstrip() for l in f]
|
||||
|
||||
run_lines = [m.group(1)
|
||||
for m in [RUN_LINE_RE.match(l) for l in test_lines] if m]
|
||||
for m in [RUN_LINE_RE.match(l) for l in input_lines] if m]
|
||||
if args.verbose:
|
||||
print >>sys.stderr, 'Found %d RUN lines:' % (len(run_lines),)
|
||||
for l in run_lines:
|
||||
print >>sys.stderr, ' RUN: ' + l
|
||||
|
||||
checks = []
|
||||
prefix_list = []
|
||||
for l in run_lines:
|
||||
(llc_cmd, filecheck_cmd) = tuple([cmd.strip() for cmd in l.split('|', 1)])
|
||||
if not llc_cmd.startswith('llc '):
|
||||
@ -115,96 +183,59 @@ def main():
|
||||
|
||||
# FIXME: We should use multiple check prefixes to common check lines. For
|
||||
# now, we just ignore all but the last.
|
||||
checks.append((check_prefixes, llc_cmd_args))
|
||||
prefix_list.append((check_prefixes, llc_cmd_args))
|
||||
|
||||
asm = {}
|
||||
for prefixes, _ in checks:
|
||||
func_dict = {}
|
||||
for prefixes, _ in prefix_list:
|
||||
for prefix in prefixes:
|
||||
asm.update({prefix: dict()})
|
||||
for prefixes, llc_args in checks:
|
||||
func_dict.update({prefix: dict()})
|
||||
for prefixes, llc_args in prefix_list:
|
||||
if args.verbose:
|
||||
print >>sys.stderr, 'Extracted LLC cmd: llc ' + llc_args
|
||||
print >>sys.stderr, 'Extracted FileCheck prefixes: ' + str(prefixes)
|
||||
raw_asm = llc(args, llc_args, test)
|
||||
# Build up a dictionary of all the function bodies.
|
||||
for m in ASM_FUNCTION_RE.finditer(raw_asm):
|
||||
if not m:
|
||||
continue
|
||||
f = m.group('f')
|
||||
f_asm = scrub_asm(m.group('body'))
|
||||
if f.startswith('stress'):
|
||||
# We only use the last line of the asm for stress tests.
|
||||
f_asm = '\n'.join(f_asm.splitlines()[-1:])
|
||||
if args.verbose:
|
||||
print >>sys.stderr, 'Processing asm for function: ' + f
|
||||
for l in f_asm.splitlines():
|
||||
print >>sys.stderr, ' ' + l
|
||||
for prefix in prefixes:
|
||||
if f in asm[prefix] and asm[prefix][f] != f_asm:
|
||||
if prefix == prefixes[-1]:
|
||||
print >>sys.stderr, ('WARNING: Found conflicting asm under the '
|
||||
'same prefix: %r!' % (prefix,))
|
||||
else:
|
||||
asm[prefix][f] = None
|
||||
continue
|
||||
|
||||
asm[prefix][f] = f_asm
|
||||
raw_tool_output = llc(args, llc_args, test)
|
||||
build_function_body_dictionary(raw_tool_output, prefixes, func_dict, args.verbose)
|
||||
|
||||
is_in_function = False
|
||||
is_in_function_start = False
|
||||
prefix_set = set([prefix for prefixes, _ in checks for prefix in prefixes])
|
||||
prefix_set = set([prefix for prefixes, _ in prefix_list for prefix in prefixes])
|
||||
if args.verbose:
|
||||
print >>sys.stderr, 'Rewriting FileCheck prefixes: %s' % (prefix_set,)
|
||||
fixed_lines = []
|
||||
fixed_lines.append(autogenerated_note)
|
||||
output_lines = []
|
||||
output_lines.append(autogenerated_note)
|
||||
|
||||
for l in test_lines:
|
||||
for input_line in input_lines:
|
||||
if is_in_function_start:
|
||||
if l.lstrip().startswith(';'):
|
||||
m = CHECK_RE.match(l)
|
||||
if input_line == '':
|
||||
continue
|
||||
if input_line.lstrip().startswith(';'):
|
||||
m = CHECK_RE.match(input_line)
|
||||
if not m or m.group(1) not in prefix_set:
|
||||
fixed_lines.append(l)
|
||||
output_lines.append(input_line)
|
||||
continue
|
||||
|
||||
# Print out the various check lines here
|
||||
printed_prefixes = []
|
||||
for prefixes, _ in checks:
|
||||
for prefix in prefixes:
|
||||
if prefix in printed_prefixes:
|
||||
break
|
||||
if not asm[prefix][name]:
|
||||
continue
|
||||
if len(printed_prefixes) != 0:
|
||||
fixed_lines.append(';')
|
||||
printed_prefixes.append(prefix)
|
||||
fixed_lines.append('; %s-LABEL: %s:' % (prefix, name))
|
||||
asm_lines = asm[prefix][name].splitlines()
|
||||
fixed_lines.append('; %s: %s' % (prefix, asm_lines[0]))
|
||||
for asm_line in asm_lines[1:]:
|
||||
fixed_lines.append('; %s-NEXT: %s' % (prefix, asm_line))
|
||||
break
|
||||
# Print out the various check lines here.
|
||||
output_lines = add_checks(output_lines, prefix_list, func_dict, name)
|
||||
is_in_function_start = False
|
||||
|
||||
if is_in_function:
|
||||
# Skip any blank comment lines in the IR.
|
||||
if l.strip() == ';':
|
||||
if should_add_line_to_output(input_line, prefix_set) == True:
|
||||
# This input line of the function body will go as-is into the output.
|
||||
output_lines.append(input_line)
|
||||
else:
|
||||
continue
|
||||
# And skip any CHECK lines. We'll build our own.
|
||||
m = CHECK_RE.match(l)
|
||||
if m and m.group(1) in prefix_set:
|
||||
continue
|
||||
# Collect the remaining lines in the function body and look for the end
|
||||
# of the function.
|
||||
fixed_lines.append(l)
|
||||
if l.strip() == '}':
|
||||
if input_line.strip() == '}':
|
||||
is_in_function = False
|
||||
continue
|
||||
|
||||
if l == autogenerated_note:
|
||||
if input_line == autogenerated_note:
|
||||
continue
|
||||
fixed_lines.append(l)
|
||||
|
||||
m = IR_FUNCTION_RE.match(l)
|
||||
# If it's outside a function, it just gets copied to the output.
|
||||
output_lines.append(input_line)
|
||||
|
||||
m = IR_FUNCTION_RE.match(input_line)
|
||||
if not m:
|
||||
continue
|
||||
name = m.group(1)
|
||||
@ -214,10 +245,10 @@ def main():
|
||||
is_in_function = is_in_function_start = True
|
||||
|
||||
if args.verbose:
|
||||
print>>sys.stderr, 'Writing %d fixed lines to %s...' % (
|
||||
len(fixed_lines), test)
|
||||
print>>sys.stderr, 'Writing %d lines to %s...' % (len(output_lines), test)
|
||||
|
||||
with open(test, 'wb') as f:
|
||||
f.writelines([l + '\n' for l in fixed_lines])
|
||||
f.writelines([l + '\n' for l in output_lines])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
Reference in New Issue
Block a user