blob: a849b8e10013aba1ef8ee6613a49d35a1b25dcb7 [file] [log] [blame]
Michael Krusec0a19b02016-02-29 14:58:131#! /usr/bin/env python3
2# -*- coding: UTF-8 -*-
3
4# Polly/LLVM update_check.py
5# Update lit FileCheck files by replacing the 'CHECK:' lines by the actual output of the 'RUN:' command.
6
7import argparse
8import os
9import subprocess
10import shlex
11import re
12
13
14polly_src_dir = '''@POLLY_SOURCE_DIR@'''
15polly_lib_dir = '''@POLLY_LIB_DIR@'''
16shlibext = '''@LLVM_SHLIBEXT@'''
17llvm_tools_dir = '''@LLVM_TOOLS_DIR@'''
18link_polly_into_tools = not '''@LINK_POLLY_INTO_TOOLS@'''.lower() in {'','0','n','no','off','false','notfound','link_polly_into_tools-notfound'}
19
20runre = re.compile(r'\s*\;\s*RUN\s*\:(?P<tool>.*)')
21filecheckre = re.compile(r'\s*(?P<tool>.*)\|\s*(?P<filecheck>FileCheck\s[^|]*)')
22emptyline = re.compile(r'\s*(\;\s*)?')
23commentline = re.compile(r'\s*(\;.*)?')
24
25
26def ltrim_emptylines(lines,meta=None):
27 while len(lines) and emptyline.fullmatch(lines[0]):
28 del lines[0]
29 if meta is not None:
30 del meta[0]
31
32
33def rtrim_emptylines(lines):
34 while len(lines) and emptyline.fullmatch(lines[-1]):
35 del lines[-1]
36
37
38def trim_emptylines(lines):
39 ltrim_emptylines(lines)
40 rtrim_emptylines(lines)
41
42
43def complete_exename(path, filename):
44 complpath = os.path.join(path, filename)
45 if os.path.isfile(complpath):
46 return complpath
47 elif os.path.isfile(complpath + '.exe'):
48 return complpath + '.exe'
49 return filename
50
51
52def indention(line):
53 for i,c in enumerate(line):
54 if c != ' ' and c != '\t':
55 return i
56 return None
57
58
59def common_indent(lines):
60 indentions = (indention(line) for line in lines)
61 indentions = (indent for indent in indentions if indent is not None)
62 return min(indentions,default=0)
63
64
65funcre = re.compile(r'^ Function: \S*$')
66regionre = re.compile(r'^ Region: \S*$')
67depthre = re.compile(r'^ Max Loop Depth: .*')
68paramre = re.compile(r' [0-9a-z-A-Z_]+\: .*')
69
70def classyfier1(lines):
71 i = iter(lines)
72 line = i.__next__()
73 while True:
74 if line.startswith("Printing analysis 'Polly - Calculate dependences' for region: "):
75 yield {'PrintingDependenceInfo'}
76 elif line.startswith("remark: "):
77 yield {'Remark'}
78 elif funcre.fullmatch(line):
79 yield {'Function'}
80 elif regionre.fullmatch(line):
81 yield { 'Region'}
82 elif depthre.fullmatch(line):
83 yield {'MaxLoopDepth'}
84 elif line == ' Invariant Accesses: {':
85 while True:
86 yield { 'InvariantAccesses'}
87 if line == ' }':
88 break
89 line = i.__next__()
90 elif line == ' Context:':
91 yield {'Context'}
92 line = i.__next__()
93 yield {'Context'}
94 elif line == ' Assumed Context:':
95 yield {'AssumedContext'}
96 line = i.__next__()
97 yield {'AssumedContext'}
98 elif line == ' Boundary Context:':
99 yield {'BoundaryContext'}
100 line = i.__next__()
101 yield {'BoundaryContext'}
102 line = i.__next__()
103 while paramre.fullmatch(line):
104 yield {'Param'}
105 line = i.__next__()
106 continue
107 elif line == ' Arrays {':
108 while True:
109 yield {'Arrays'}
110 if line == ' }':
111 break
112 line = i.__next__()
113 elif line == ' Arrays (Bounds as pw_affs) {':
114 while True:
115 yield {'PwAffArrays'}
116 if line == ' }':
117 break
118 line = i.__next__()
119 elif line.startswith(' Alias Groups ('):
120 while True:
121 yield {'AliasGroups'}
122 line = i.__next__()
123 if not line.startswith(' '):
124 break
125 continue
126 elif line == ' Statements {':
127 while True:
128 yield {'Statements'}
129 if line == ' }':
130 break
131 line = i.__next__()
132 elif line == ' RAW dependences:':
133 yield {'RAWDep','BasicDep','Dep','DepInfo'}
134 line = i.__next__()
135 while line.startswith(' '):
136 yield {'RAWDep','BasicDep','Dep','DepInfo'}
137 line = i.__next__()
138 continue
139 elif line == ' WAR dependences:':
140 yield {'WARDep','BasicDep','Dep','DepInfo'}
141 line = i.__next__()
142 while line.startswith(' '):
143 yield {'WARDep','BasicDep','Dep','DepInfo'}
144 line = i.__next__()
145 continue
146 elif line == ' WAW dependences:':
147 yield {'WAWDep','BasicDep','Dep','DepInfo'}
148 line = i.__next__()
149 while line.startswith(' '):
150 yield {'WAWDep','BasicDep','Dep','DepInfo'}
151 line = i.__next__()
152 continue
153 elif line == ' Reduction dependences:':
154 yield {'RedDep','Dep','DepInfo'}
155 line = i.__next__()
156 while line.startswith(' '):
157 yield {'RedDep','Dep','DepInfo'}
158 line = i.__next__()
159 continue
160 elif line == ' Transitive closure of reduction dependences:':
161 yield {'TransitiveClosureDep','DepInfo'}
162 line = i.__next__()
163 while line.startswith(' '):
164 yield {'TransitiveClosureDep','DepInfo'}
165 line = i.__next__()
166 continue
167 else:
168 yield set()
169 line = i.__next__()
170
171
172def classyfier2(lines):
173 i = iter(lines)
174 line = i.__next__()
175 while True:
176 if funcre.fullmatch(line):
177 while line.startswith(' '):
178 yield {'FunctionDetail'}
179 line = i.__next__()
180 continue
181 elif line.startswith("Printing analysis 'Polly - Generate an AST from the SCoP (isl)' for region: "):
182 yield {'PrintingIslAst'}
183 line = i.__next__()
184 while not line.startswith('Printing analysis'):
185 yield {'AstDetail'}
186 line = i.__next__()
187 continue
188 else:
189 yield set()
190 line = i.__next__()
191
192
193replrepl = {'{{':'{{[{][{]}}','}}': '{{[}][}]}}', '[[':'{{\[\[}}',']]': '{{\]\]}}'}
194replre = re.compile('|'.join(re.escape(k) for k in replrepl.keys()))
195
196def main():
197 parser = argparse.ArgumentParser(description="Update CHECK lines")
198 parser.add_argument('testfile',help="File to update (absolute or relative to --testdir)")
199 parser.add_argument('--check-style',choices=['CHECK','CHECK-NEXT'],default='CHECK-NEXT',help="What kind of checks lines to generate")
200 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")
201 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)")
202 parser.add_argument('--check-label-include',action='append',default=[],help="Use CHECK-LABEL for these includes")
203 parser.add_argument('--check-part-newline',action='store_true',help="Add empty line between different check parts")
204 parser.add_argument('--prefix-only',action='append',default=None,help="Update only these prefixes (default: all)")
205 parser.add_argument('--bindir',help="Location of the opt program")
206 parser.add_argument('--testdir',help="Root dir for unit tests")
207 parser.add_argument('--inplace','-i',action='store_true',help="Replace input file")
208 parser.add_argument('--output','-o',help="Write changed input to this file")
209 known = parser.parse_args()
210
211 if not known.inplace and known.output is None:
212 print("Must specify what to do with output (--output or --inplace)")
213 exit(1)
214 if known.inplace and known.output is not None:
215 print("--inplace and --output are mutually exclusive")
216 exit(1)
217
218 outfile = known.output
219
220 filecheckparser = argparse.ArgumentParser(add_help=False)
221 filecheckparser.add_argument('-check-prefix','--check-prefix',default='CHECK')
222
223 filename = known.testfile
224 for dir in ['.', known.testdir, os.path.join(polly_src_dir,'test'), polly_src_dir]:
225 if not dir:
226 continue
227 testfilename = os.path.join(dir,filename)
228 if os.path.isfile(testfilename):
229 filename = testfilename
230 break
231
232 if known.inplace:
233 outfile = filename
234
235 allchecklines = []
236 checkprefixes = []
237
238 with open(filename, 'r') as file:
239 oldlines = [line.rstrip('\r\n') for line in file.readlines()]
240
241 runlines = []
242 for line in oldlines:
243 m = runre.match(line)
244 if m:
245 runlines.append(m.group('tool'))
246
247 continuation = ''
248 newrunlines = []
249 for line in runlines:
250 if line.endswith('\\'):
251 continuation += line[:-2] + ' '
252 else:
253 newrunlines.append(continuation + line)
254 continuation = ''
255 if continuation:
256 newrunlines.append(continuation)
257
258 for line in newrunlines:
259 m = filecheckre.match(line)
260 if not m:
261 continue
262
263 tool, filecheck = m.group('tool', 'filecheck')
264 filecheck = shlex.split(filecheck)
265 tool = shlex.split(tool)
266 if known.bindir is not None:
267 tool[0] = complete_exename(known.bindir, tool[0])
268 if os.path.isdir(llvm_tools_dir):
269 tool[0] = complete_exename(llvm_tools_dir, tool[0])
270 check_prefix = filecheckparser.parse_known_args(filecheck)[0].check_prefix
271 if known.prefix_only is not None and not check_prefix in known.prefix_only:
272 continue
273 if check_prefix in checkprefixes:
274 continue
275 checkprefixes.append(check_prefix)
276
277 newtool = []
278 optstderr = None
279 for toolarg in tool:
280 toolarg = toolarg.replace('%s', filename)
281 toolarg = toolarg.replace('%S', os.path.dirname(filename))
282 if toolarg == '%loadPolly':
283 if not link_polly_into_tools:
284 newtool += ['-load',os.path.join(polly_lib_dir,'LLVMPolly' + shlibext)]
285 newtool.append('-polly-process-unprofitable')
286 elif toolarg == '2>&1':
287 optstderr = subprocess.STDOUT
288 else:
289 newtool.append(toolarg)
290 tool = newtool
291
292 inpfile = None
293 i = 1
294 while i < len(tool):
295 if tool[i] == '<':
296 inpfile = tool[i + 1]
297 del tool[i:i + 2]
298 continue
299 i += 1
300 if inpfile:
301 with open(inpfile) as inp:
302 retlines = subprocess.check_output(tool,universal_newlines=True,stdin=inp,stderr=optstderr)
303 else:
304 retlines = subprocess.check_output(tool,universal_newlines=True,stderr=optstderr)
305 retlines = [line.replace('\t', ' ') for line in retlines.splitlines()]
306 check_include = []
307 for checkme in known.check_include + known.check_label_include:
308 parts = checkme.split('=')
309 if len(parts) == 2:
310 if parts[0] == check_prefix:
311 check_include.append(parts[1])
312 else:
313 check_include.append(checkme)
314
315 if check_include:
316 filtered_retlines = []
317 classified_retlines = []
318 lastmatch = None
319 for line,kind in ((line,class1.union(class2)) for line,class1,class2 in zip(retlines,classyfier1(retlines), classyfier2(retlines))):
320 match = kind.intersection(check_include)
321 if match:
322 if lastmatch != match:
323 filtered_retlines.append('')
324 classified_retlines.append({'Separator'})
325 filtered_retlines.append(line)
326 classified_retlines.append(kind)
327 lastmatch = match
328
329 retlines = filtered_retlines
330 else:
331 classified_retlines = (set() for line in retlines)
332
333 rtrim_emptylines(retlines)
334 ltrim_emptylines(retlines,classified_retlines)
335 retlines = [replre.sub(lambda m: replrepl[m.group(0)], line) for line in retlines]
336 indent = common_indent(retlines)
337 retlines = [line[indent:] for line in retlines]
338 checklines = []
339 previous_was_empty = True
340 for line,kind in zip(retlines,classified_retlines):
341 if line:
342 if known.check_style == 'CHECK' and known.check_label_include:
343 if not kind.isdisjoint(known.check_label_include):
344 checklines.append('; ' + check_prefix + '-LABEL: ' + line)
345 else:
346 checklines.append('; ' + check_prefix + ': ' + line)
347 elif known.check_style == 'CHECK':
348 checklines.append('; ' + check_prefix + ': ' + line)
349 elif known.check_label_include and known.check_label_include:
350 if not kind.isdisjoint(known.check_label_include):
351 checklines.append('; ' + check_prefix + '-LABEL: ' + line)
352 elif previous_was_empty:
353 checklines.append('; ' + check_prefix + ': ' + line)
354 else:
355 checklines.append('; ' + check_prefix + '-NEXT: ' + line)
356 else:
357 if previous_was_empty:
358 checklines.append('; ' + check_prefix + ': ' + line)
359 else:
360 checklines.append('; ' + check_prefix + '-NEXT: ' + line)
361 previous_was_empty = False
362 else:
363 if not 'Separator' in kind or known.check_part_newline:
364 checklines.append(';')
365 previous_was_empty = True
366 allchecklines.append(checklines)
367
368 if not checkprefixes:
369 return
370
371 checkre = re.compile(r'^\s*\;\s*(' + '|'.join([re.escape(s) for s in checkprefixes]) + ')(\-NEXT|\-DAG|\-NOT|\-LABEL|\-SAME)?\s*\:')
372 firstcheckline = None
373 firstnoncommentline = None
374 headerlines = []
375 newlines = []
376 uptonowlines = []
377 emptylines = []
378 lastwascheck = False
379 for line in oldlines:
380 if checkre.match(line):
381 if firstcheckline is None:
382 firstcheckline = len(newlines) + len(emptylines)
383 if not lastwascheck:
384 uptonowlines += emptylines
385 emptylines = []
386 lastwascheck = True
387 elif emptyline.fullmatch(line):
388 emptylines.append(line)
389 else:
390 newlines += uptonowlines
391 newlines += emptylines
392 newlines.append(line)
393 emptylines = []
394 uptonowlines = []
395 lastwascheck = False
396
397 for i,line in enumerate(newlines):
398 if not commentline.fullmatch(line):
399 firstnoncommentline = i
400 break
401
402 with open(outfile,'w',newline='') as file:
403 def writelines(lines):
404 for line in lines:
405 file.write(line)
406 file.write('\n')
407
408 if firstcheckline is not None and known.check_position == 'autodetect':
409 writelines(newlines[:firstcheckline])
410 writelines(uptonowlines)
411 for i,checklines in enumerate(allchecklines):
412 if i != 0:
413 file.write('\n')
414 writelines(checklines)
415 writelines(newlines[firstcheckline:])
416 writelines(emptylines)
417 elif firstnoncommentline is not None and known.check_position == 'before-content':
418 headerlines = newlines[:firstnoncommentline]
419 rtrim_emptylines(headerlines)
420 contentlines = newlines[firstnoncommentline:]
421 ltrim_emptylines(contentlines)
422
423 writelines(headerlines)
424 for checklines in allchecklines:
425 file.write('\n')
426 writelines(checklines)
427 file.write('\n')
428 writelines(contentlines)
429 writelines(uptonowlines)
430 writelines(emptylines)
431 else:
432 writelines(newlines)
433 rtrim_emptylines(newlines)
434 for checklines in allchecklines:
435 file.write('\n\n')
436 writelines(checklines)
437
438
439if __name__ == '__main__':
440 main()