forked from OSchip/llvm-project
Add update_test.py script.
The script updates a lit test case that uses FileCheck using the actual output of the 'RUN:'-lines program. Useful when updating test cases due expected output changes and diff'ing expected and actual output. llvm-svn: 261899
This commit is contained in:
parent
50ca4464c4
commit
9cfc49d5b5
|
@ -95,6 +95,14 @@ configure_file(
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
|
||||
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/update_check.py
|
||||
${CMAKE_CURRENT_BINARY_DIR}/update_check.py)
|
||||
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/update_check.py
|
||||
DESTINATION ${LLVM_TOOLS_BINARY_DIR}
|
||||
FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
|
||||
GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
|
||||
# Add a legacy target spelling: polly-test
|
||||
add_custom_target(polly-test)
|
||||
set_target_properties(polly-test PROPERTIES FOLDER "Polly")
|
||||
|
|
|
@ -0,0 +1,440 @@
|
|||
#! /usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
# Polly/LLVM update_check.py
|
||||
# Update lit FileCheck files by replacing the 'CHECK:' lines by the actual output of the 'RUN:' command.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import shlex
|
||||
import re
|
||||
|
||||
|
||||
polly_src_dir = '''@POLLY_SOURCE_DIR@'''
|
||||
polly_lib_dir = '''@POLLY_LIB_DIR@'''
|
||||
shlibext = '''@LLVM_SHLIBEXT@'''
|
||||
llvm_tools_dir = '''@LLVM_TOOLS_DIR@'''
|
||||
link_polly_into_tools = not '''@LINK_POLLY_INTO_TOOLS@'''.lower() in {'','0','n','no','off','false','notfound','link_polly_into_tools-notfound'}
|
||||
|
||||
runre = re.compile(r'\s*\;\s*RUN\s*\:(?P<tool>.*)')
|
||||
filecheckre = re.compile(r'\s*(?P<tool>.*)\|\s*(?P<filecheck>FileCheck\s[^|]*)')
|
||||
emptyline = re.compile(r'\s*(\;\s*)?')
|
||||
commentline = re.compile(r'\s*(\;.*)?')
|
||||
|
||||
|
||||
def ltrim_emptylines(lines,meta=None):
|
||||
while len(lines) and emptyline.fullmatch(lines[0]):
|
||||
del lines[0]
|
||||
if meta is not None:
|
||||
del meta[0]
|
||||
|
||||
|
||||
def rtrim_emptylines(lines):
|
||||
while len(lines) and emptyline.fullmatch(lines[-1]):
|
||||
del lines[-1]
|
||||
|
||||
|
||||
def trim_emptylines(lines):
|
||||
ltrim_emptylines(lines)
|
||||
rtrim_emptylines(lines)
|
||||
|
||||
|
||||
def complete_exename(path, filename):
|
||||
complpath = os.path.join(path, filename)
|
||||
if os.path.isfile(complpath):
|
||||
return complpath
|
||||
elif os.path.isfile(complpath + '.exe'):
|
||||
return complpath + '.exe'
|
||||
return filename
|
||||
|
||||
|
||||
def indention(line):
|
||||
for i,c in enumerate(line):
|
||||
if c != ' ' and c != '\t':
|
||||
return i
|
||||
return None
|
||||
|
||||
|
||||
def common_indent(lines):
|
||||
indentions = (indention(line) for line in lines)
|
||||
indentions = (indent for indent in indentions if indent is not None)
|
||||
return min(indentions,default=0)
|
||||
|
||||
|
||||
funcre = re.compile(r'^ Function: \S*$')
|
||||
regionre = re.compile(r'^ Region: \S*$')
|
||||
depthre = re.compile(r'^ Max Loop Depth: .*')
|
||||
paramre = re.compile(r' [0-9a-z-A-Z_]+\: .*')
|
||||
|
||||
def classyfier1(lines):
|
||||
i = iter(lines)
|
||||
line = i.__next__()
|
||||
while True:
|
||||
if line.startswith("Printing analysis 'Polly - Calculate dependences' for region: "):
|
||||
yield {'PrintingDependenceInfo'}
|
||||
elif line.startswith("remark: "):
|
||||
yield {'Remark'}
|
||||
elif funcre.fullmatch(line):
|
||||
yield {'Function'}
|
||||
elif regionre.fullmatch(line):
|
||||
yield { 'Region'}
|
||||
elif depthre.fullmatch(line):
|
||||
yield {'MaxLoopDepth'}
|
||||
elif line == ' Invariant Accesses: {':
|
||||
while True:
|
||||
yield { 'InvariantAccesses'}
|
||||
if line == ' }':
|
||||
break
|
||||
line = i.__next__()
|
||||
elif line == ' Context:':
|
||||
yield {'Context'}
|
||||
line = i.__next__()
|
||||
yield {'Context'}
|
||||
elif line == ' Assumed Context:':
|
||||
yield {'AssumedContext'}
|
||||
line = i.__next__()
|
||||
yield {'AssumedContext'}
|
||||
elif line == ' Boundary Context:':
|
||||
yield {'BoundaryContext'}
|
||||
line = i.__next__()
|
||||
yield {'BoundaryContext'}
|
||||
line = i.__next__()
|
||||
while paramre.fullmatch(line):
|
||||
yield {'Param'}
|
||||
line = i.__next__()
|
||||
continue
|
||||
elif line == ' Arrays {':
|
||||
while True:
|
||||
yield {'Arrays'}
|
||||
if line == ' }':
|
||||
break
|
||||
line = i.__next__()
|
||||
elif line == ' Arrays (Bounds as pw_affs) {':
|
||||
while True:
|
||||
yield {'PwAffArrays'}
|
||||
if line == ' }':
|
||||
break
|
||||
line = i.__next__()
|
||||
elif line.startswith(' Alias Groups ('):
|
||||
while True:
|
||||
yield {'AliasGroups'}
|
||||
line = i.__next__()
|
||||
if not line.startswith(' '):
|
||||
break
|
||||
continue
|
||||
elif line == ' Statements {':
|
||||
while True:
|
||||
yield {'Statements'}
|
||||
if line == ' }':
|
||||
break
|
||||
line = i.__next__()
|
||||
elif line == ' RAW dependences:':
|
||||
yield {'RAWDep','BasicDep','Dep','DepInfo'}
|
||||
line = i.__next__()
|
||||
while line.startswith(' '):
|
||||
yield {'RAWDep','BasicDep','Dep','DepInfo'}
|
||||
line = i.__next__()
|
||||
continue
|
||||
elif line == ' WAR dependences:':
|
||||
yield {'WARDep','BasicDep','Dep','DepInfo'}
|
||||
line = i.__next__()
|
||||
while line.startswith(' '):
|
||||
yield {'WARDep','BasicDep','Dep','DepInfo'}
|
||||
line = i.__next__()
|
||||
continue
|
||||
elif line == ' WAW dependences:':
|
||||
yield {'WAWDep','BasicDep','Dep','DepInfo'}
|
||||
line = i.__next__()
|
||||
while line.startswith(' '):
|
||||
yield {'WAWDep','BasicDep','Dep','DepInfo'}
|
||||
line = i.__next__()
|
||||
continue
|
||||
elif line == ' Reduction dependences:':
|
||||
yield {'RedDep','Dep','DepInfo'}
|
||||
line = i.__next__()
|
||||
while line.startswith(' '):
|
||||
yield {'RedDep','Dep','DepInfo'}
|
||||
line = i.__next__()
|
||||
continue
|
||||
elif line == ' Transitive closure of reduction dependences:':
|
||||
yield {'TransitiveClosureDep','DepInfo'}
|
||||
line = i.__next__()
|
||||
while line.startswith(' '):
|
||||
yield {'TransitiveClosureDep','DepInfo'}
|
||||
line = i.__next__()
|
||||
continue
|
||||
else:
|
||||
yield set()
|
||||
line = i.__next__()
|
||||
|
||||
|
||||
def classyfier2(lines):
|
||||
i = iter(lines)
|
||||
line = i.__next__()
|
||||
while True:
|
||||
if funcre.fullmatch(line):
|
||||
while line.startswith(' '):
|
||||
yield {'FunctionDetail'}
|
||||
line = i.__next__()
|
||||
continue
|
||||
elif line.startswith("Printing analysis 'Polly - Generate an AST from the SCoP (isl)' for region: "):
|
||||
yield {'PrintingIslAst'}
|
||||
line = i.__next__()
|
||||
while not line.startswith('Printing analysis'):
|
||||
yield {'AstDetail'}
|
||||
line = i.__next__()
|
||||
continue
|
||||
else:
|
||||
yield set()
|
||||
line = i.__next__()
|
||||
|
||||
|
||||
replrepl = {'{{':'{{[{][{]}}','}}': '{{[}][}]}}', '[[':'{{\[\[}}',']]': '{{\]\]}}'}
|
||||
replre = re.compile('|'.join(re.escape(k) for k in replrepl.keys()))
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Update CHECK lines")
|
||||
parser.add_argument('testfile',help="File to update (absolute or relative to --testdir)")
|
||||
parser.add_argument('--check-style',choices=['CHECK','CHECK-NEXT'],default='CHECK-NEXT',help="What kind of checks lines to generate")
|
||||
parser.add_argument('--check-position',choices=['end','before-content','autodetect'],default='autodetect',help="Where to add the CHECK lines into the file; 'autodetect' searches for the first 'CHECK' line ind inserts it there")
|
||||
parser.add_argument('--check-include',action='append',default=[], help="What parts of the output lines to check; use syntax 'CHECK=include' to apply to one CHECK-prefix only (by default, everything)")
|
||||
parser.add_argument('--check-label-include',action='append',default=[],help="Use CHECK-LABEL for these includes")
|
||||
parser.add_argument('--check-part-newline',action='store_true',help="Add empty line between different check parts")
|
||||
parser.add_argument('--prefix-only',action='append',default=None,help="Update only these prefixes (default: all)")
|
||||
parser.add_argument('--bindir',help="Location of the opt program")
|
||||
parser.add_argument('--testdir',help="Root dir for unit tests")
|
||||
parser.add_argument('--inplace','-i',action='store_true',help="Replace input file")
|
||||
parser.add_argument('--output','-o',help="Write changed input to this file")
|
||||
known = parser.parse_args()
|
||||
|
||||
if not known.inplace and known.output is None:
|
||||
print("Must specify what to do with output (--output or --inplace)")
|
||||
exit(1)
|
||||
if known.inplace and known.output is not None:
|
||||
print("--inplace and --output are mutually exclusive")
|
||||
exit(1)
|
||||
|
||||
outfile = known.output
|
||||
|
||||
filecheckparser = argparse.ArgumentParser(add_help=False)
|
||||
filecheckparser.add_argument('-check-prefix','--check-prefix',default='CHECK')
|
||||
|
||||
filename = known.testfile
|
||||
for dir in ['.', known.testdir, os.path.join(polly_src_dir,'test'), polly_src_dir]:
|
||||
if not dir:
|
||||
continue
|
||||
testfilename = os.path.join(dir,filename)
|
||||
if os.path.isfile(testfilename):
|
||||
filename = testfilename
|
||||
break
|
||||
|
||||
if known.inplace:
|
||||
outfile = filename
|
||||
|
||||
allchecklines = []
|
||||
checkprefixes = []
|
||||
|
||||
with open(filename, 'r') as file:
|
||||
oldlines = [line.rstrip('\r\n') for line in file.readlines()]
|
||||
|
||||
runlines = []
|
||||
for line in oldlines:
|
||||
m = runre.match(line)
|
||||
if m:
|
||||
runlines.append(m.group('tool'))
|
||||
|
||||
continuation = ''
|
||||
newrunlines = []
|
||||
for line in runlines:
|
||||
if line.endswith('\\'):
|
||||
continuation += line[:-2] + ' '
|
||||
else:
|
||||
newrunlines.append(continuation + line)
|
||||
continuation = ''
|
||||
if continuation:
|
||||
newrunlines.append(continuation)
|
||||
|
||||
for line in newrunlines:
|
||||
m = filecheckre.match(line)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
tool, filecheck = m.group('tool', 'filecheck')
|
||||
filecheck = shlex.split(filecheck)
|
||||
tool = shlex.split(tool)
|
||||
if known.bindir is not None:
|
||||
tool[0] = complete_exename(known.bindir, tool[0])
|
||||
if os.path.isdir(llvm_tools_dir):
|
||||
tool[0] = complete_exename(llvm_tools_dir, tool[0])
|
||||
check_prefix = filecheckparser.parse_known_args(filecheck)[0].check_prefix
|
||||
if known.prefix_only is not None and not check_prefix in known.prefix_only:
|
||||
continue
|
||||
if check_prefix in checkprefixes:
|
||||
continue
|
||||
checkprefixes.append(check_prefix)
|
||||
|
||||
newtool = []
|
||||
optstderr = None
|
||||
for toolarg in tool:
|
||||
toolarg = toolarg.replace('%s', filename)
|
||||
toolarg = toolarg.replace('%S', os.path.dirname(filename))
|
||||
if toolarg == '%loadPolly':
|
||||
if not link_polly_into_tools:
|
||||
newtool += ['-load',os.path.join(polly_lib_dir,'LLVMPolly' + shlibext)]
|
||||
newtool.append('-polly-process-unprofitable')
|
||||
elif toolarg == '2>&1':
|
||||
optstderr = subprocess.STDOUT
|
||||
else:
|
||||
newtool.append(toolarg)
|
||||
tool = newtool
|
||||
|
||||
inpfile = None
|
||||
i = 1
|
||||
while i < len(tool):
|
||||
if tool[i] == '<':
|
||||
inpfile = tool[i + 1]
|
||||
del tool[i:i + 2]
|
||||
continue
|
||||
i += 1
|
||||
if inpfile:
|
||||
with open(inpfile) as inp:
|
||||
retlines = subprocess.check_output(tool,universal_newlines=True,stdin=inp,stderr=optstderr)
|
||||
else:
|
||||
retlines = subprocess.check_output(tool,universal_newlines=True,stderr=optstderr)
|
||||
retlines = [line.replace('\t', ' ') for line in retlines.splitlines()]
|
||||
check_include = []
|
||||
for checkme in known.check_include + known.check_label_include:
|
||||
parts = checkme.split('=')
|
||||
if len(parts) == 2:
|
||||
if parts[0] == check_prefix:
|
||||
check_include.append(parts[1])
|
||||
else:
|
||||
check_include.append(checkme)
|
||||
|
||||
if check_include:
|
||||
filtered_retlines = []
|
||||
classified_retlines = []
|
||||
lastmatch = None
|
||||
for line,kind in ((line,class1.union(class2)) for line,class1,class2 in zip(retlines,classyfier1(retlines), classyfier2(retlines))):
|
||||
match = kind.intersection(check_include)
|
||||
if match:
|
||||
if lastmatch != match:
|
||||
filtered_retlines.append('')
|
||||
classified_retlines.append({'Separator'})
|
||||
filtered_retlines.append(line)
|
||||
classified_retlines.append(kind)
|
||||
lastmatch = match
|
||||
|
||||
retlines = filtered_retlines
|
||||
else:
|
||||
classified_retlines = (set() for line in retlines)
|
||||
|
||||
rtrim_emptylines(retlines)
|
||||
ltrim_emptylines(retlines,classified_retlines)
|
||||
retlines = [replre.sub(lambda m: replrepl[m.group(0)], line) for line in retlines]
|
||||
indent = common_indent(retlines)
|
||||
retlines = [line[indent:] for line in retlines]
|
||||
checklines = []
|
||||
previous_was_empty = True
|
||||
for line,kind in zip(retlines,classified_retlines):
|
||||
if line:
|
||||
if known.check_style == 'CHECK' and known.check_label_include:
|
||||
if not kind.isdisjoint(known.check_label_include):
|
||||
checklines.append('; ' + check_prefix + '-LABEL: ' + line)
|
||||
else:
|
||||
checklines.append('; ' + check_prefix + ': ' + line)
|
||||
elif known.check_style == 'CHECK':
|
||||
checklines.append('; ' + check_prefix + ': ' + line)
|
||||
elif known.check_label_include and known.check_label_include:
|
||||
if not kind.isdisjoint(known.check_label_include):
|
||||
checklines.append('; ' + check_prefix + '-LABEL: ' + line)
|
||||
elif previous_was_empty:
|
||||
checklines.append('; ' + check_prefix + ': ' + line)
|
||||
else:
|
||||
checklines.append('; ' + check_prefix + '-NEXT: ' + line)
|
||||
else:
|
||||
if previous_was_empty:
|
||||
checklines.append('; ' + check_prefix + ': ' + line)
|
||||
else:
|
||||
checklines.append('; ' + check_prefix + '-NEXT: ' + line)
|
||||
previous_was_empty = False
|
||||
else:
|
||||
if not 'Separator' in kind or known.check_part_newline:
|
||||
checklines.append(';')
|
||||
previous_was_empty = True
|
||||
allchecklines.append(checklines)
|
||||
|
||||
if not checkprefixes:
|
||||
return
|
||||
|
||||
checkre = re.compile(r'^\s*\;\s*(' + '|'.join([re.escape(s) for s in checkprefixes]) + ')(\-NEXT|\-DAG|\-NOT|\-LABEL|\-SAME)?\s*\:')
|
||||
firstcheckline = None
|
||||
firstnoncommentline = None
|
||||
headerlines = []
|
||||
newlines = []
|
||||
uptonowlines = []
|
||||
emptylines = []
|
||||
lastwascheck = False
|
||||
for line in oldlines:
|
||||
if checkre.match(line):
|
||||
if firstcheckline is None:
|
||||
firstcheckline = len(newlines) + len(emptylines)
|
||||
if not lastwascheck:
|
||||
uptonowlines += emptylines
|
||||
emptylines = []
|
||||
lastwascheck = True
|
||||
elif emptyline.fullmatch(line):
|
||||
emptylines.append(line)
|
||||
else:
|
||||
newlines += uptonowlines
|
||||
newlines += emptylines
|
||||
newlines.append(line)
|
||||
emptylines = []
|
||||
uptonowlines = []
|
||||
lastwascheck = False
|
||||
|
||||
for i,line in enumerate(newlines):
|
||||
if not commentline.fullmatch(line):
|
||||
firstnoncommentline = i
|
||||
break
|
||||
|
||||
with open(outfile,'w',newline='') as file:
|
||||
def writelines(lines):
|
||||
for line in lines:
|
||||
file.write(line)
|
||||
file.write('\n')
|
||||
|
||||
if firstcheckline is not None and known.check_position == 'autodetect':
|
||||
writelines(newlines[:firstcheckline])
|
||||
writelines(uptonowlines)
|
||||
for i,checklines in enumerate(allchecklines):
|
||||
if i != 0:
|
||||
file.write('\n')
|
||||
writelines(checklines)
|
||||
writelines(newlines[firstcheckline:])
|
||||
writelines(emptylines)
|
||||
elif firstnoncommentline is not None and known.check_position == 'before-content':
|
||||
headerlines = newlines[:firstnoncommentline]
|
||||
rtrim_emptylines(headerlines)
|
||||
contentlines = newlines[firstnoncommentline:]
|
||||
ltrim_emptylines(contentlines)
|
||||
|
||||
writelines(headerlines)
|
||||
for checklines in allchecklines:
|
||||
file.write('\n')
|
||||
writelines(checklines)
|
||||
file.write('\n')
|
||||
writelines(contentlines)
|
||||
writelines(uptonowlines)
|
||||
writelines(emptylines)
|
||||
else:
|
||||
writelines(newlines)
|
||||
rtrim_emptylines(newlines)
|
||||
for checklines in allchecklines:
|
||||
file.write('\n\n')
|
||||
writelines(checklines)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue