1 #! /usr/bin/env python3
2 # -*- coding: UTF-8 -*-
4 # Polly/LLVM update_check.py
5 # Update lit FileCheck files by replacing the 'CHECK:' lines by the actual output of the 'RUN:' command.
14 polly_src_dir
= '''@POLLY_SOURCE_DIR@'''
15 polly_lib_dir
= '''@POLLY_LIB_DIR@'''
16 shlibext
= '''@LLVM_SHLIBEXT@'''
17 llvm_tools_dir
= '''@LLVM_TOOLS_DIR@'''
18 llvm_polly_link_into_tools
= not '''@LLVM_POLLY_LINK_INTO_TOOLS@'''.lower() in {'','0','n','no','off','false','notfound','llvm_polly_link_into_tools-notfound'}
20 runre
= re
.compile(r
'\s*\;\s*RUN\s*\:(?P<tool>.*)')
21 filecheckre
= re
.compile(r
'\s*(?P<tool>.*)\|\s*(?P<filecheck>FileCheck\s[^|]*)')
22 emptyline
= re
.compile(r
'\s*(\;\s*)?')
23 commentline
= re
.compile(r
'\s*(\;.*)?')
26 def ltrim_emptylines(lines
,meta
=None):
27 while len(lines
) and emptyline
.fullmatch(lines
[0]):
33 def rtrim_emptylines(lines
):
34 while len(lines
) and emptyline
.fullmatch(lines
[-1]):
38 def trim_emptylines(lines
):
39 ltrim_emptylines(lines
)
40 rtrim_emptylines(lines
)
43 def complete_exename(path
, filename
):
44 complpath
= os
.path
.join(path
, filename
)
45 if os
.path
.isfile(complpath
):
47 elif os
.path
.isfile(complpath
+ '.exe'):
48 return complpath
+ '.exe'
53 for i
,c
in enumerate(line
):
54 if c
!= ' ' and c
!= '\t':
59 def 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)
65 funcre
= re
.compile(r
'^ Function: \S*$')
66 regionre
= re
.compile(r
'^ Region: \S*$')
67 depthre
= re
.compile(r
'^ Max Loop Depth: .*')
68 paramre
= re
.compile(r
' [0-9a-z-A-Z_]+\: .*')
70 def classyfier1(lines
):
74 if line
.startswith("Printing analysis 'Polly - Calculate dependences' for region: "):
75 yield {'PrintingDependenceInfo'}
76 elif line
.startswith("remark: "):
78 elif funcre
.fullmatch(line
):
80 elif regionre
.fullmatch(line
):
82 elif depthre
.fullmatch(line
):
83 yield {'MaxLoopDepth'}
84 elif line
== ' Invariant Accesses: {':
86 yield { 'InvariantAccesses'}
90 elif line
== ' Context:':
94 elif line
== ' Assumed Context:':
95 yield {'AssumedContext'}
97 yield {'AssumedContext'}
98 elif line
== ' Invalid Context:':
99 yield {'InvalidContext'}
101 yield {'InvalidContext'}
102 elif line
== ' Boundary Context:':
103 yield {'BoundaryContext'}
105 yield {'BoundaryContext'}
107 while paramre
.fullmatch(line
):
111 elif line
== ' Arrays {':
117 elif line
== ' Arrays (Bounds as pw_affs) {':
119 yield {'PwAffArrays'}
123 elif line
.startswith(' Alias Groups ('):
125 yield {'AliasGroups'}
127 if not line
.startswith(' '):
130 elif line
== ' Statements {':
136 elif line
== ' RAW dependences:':
137 yield {'RAWDep','BasicDep','Dep','DepInfo'}
139 while line
.startswith(' '):
140 yield {'RAWDep','BasicDep','Dep','DepInfo'}
143 elif line
== ' WAR dependences:':
144 yield {'WARDep','BasicDep','Dep','DepInfo'}
146 while line
.startswith(' '):
147 yield {'WARDep','BasicDep','Dep','DepInfo'}
150 elif line
== ' WAW dependences:':
151 yield {'WAWDep','BasicDep','Dep','DepInfo'}
153 while line
.startswith(' '):
154 yield {'WAWDep','BasicDep','Dep','DepInfo'}
157 elif line
== ' Reduction dependences:':
158 yield {'RedDep','Dep','DepInfo'}
160 while line
.startswith(' '):
161 yield {'RedDep','Dep','DepInfo'}
164 elif line
== ' Transitive closure of reduction dependences:':
165 yield {'TransitiveClosureDep','DepInfo'}
167 while line
.startswith(' '):
168 yield {'TransitiveClosureDep','DepInfo'}
171 elif line
.startswith("New access function '"):
172 yield {'NewAccessFunction'}
173 elif line
== 'Schedule before flattening {':
175 yield {'ScheduleBeforeFlattening'}
179 elif line
== 'Schedule after flattening {':
181 yield {'ScheduleAfterFlattening'}
190 def classyfier2(lines
):
194 if funcre
.fullmatch(line
):
195 while line
.startswith(' '):
196 yield {'FunctionDetail'}
199 elif line
.startswith("Printing analysis 'Polly - Generate an AST from the SCoP (isl)' for region: "):
200 yield {'PrintingIslAst'}
202 while not line
.startswith('Printing analysis'):
211 replrepl
= {'{{':'{{[{][{]}}','}}': '{{[}][}]}}', '[[':'{{\[\[}}',']]': '{{\]\]}}'}
212 replre
= re
.compile('|'.join(re
.escape(k
) for k
in replrepl
.keys()))
215 parser
= argparse
.ArgumentParser(description
="Update CHECK lines")
216 parser
.add_argument('testfile',help="File to update (absolute or relative to --testdir)")
217 parser
.add_argument('--check-style',choices
=['CHECK','CHECK-NEXT'],default
='CHECK-NEXT',help="What kind of checks lines to generate")
218 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")
219 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)")
220 parser
.add_argument('--check-label-include',action
='append',default
=[],help="Use CHECK-LABEL for these includes")
221 parser
.add_argument('--check-part-newline',action
='store_true',help="Add empty line between different check parts")
222 parser
.add_argument('--prefix-only',action
='append',default
=None,help="Update only these prefixes (default: all)")
223 parser
.add_argument('--bindir',help="Location of the opt program")
224 parser
.add_argument('--testdir',help="Root dir for unit tests")
225 parser
.add_argument('--inplace','-i',action
='store_true',help="Replace input file")
226 parser
.add_argument('--output','-o',help="Write changed input to this file")
227 known
= parser
.parse_args()
229 if not known
.inplace
and known
.output
is None:
230 print("Must specify what to do with output (--output or --inplace)")
232 if known
.inplace
and known
.output
is not None:
233 print("--inplace and --output are mutually exclusive")
236 outfile
= known
.output
238 filecheckparser
= argparse
.ArgumentParser(add_help
=False)
239 filecheckparser
.add_argument('-check-prefix','--check-prefix',default
='CHECK')
241 filename
= known
.testfile
242 for dir in ['.', known
.testdir
, os
.path
.join(polly_src_dir
,'test'), polly_src_dir
]:
245 testfilename
= os
.path
.join(dir,filename
)
246 if os
.path
.isfile(testfilename
):
247 filename
= testfilename
256 with
open(filename
, 'r') as file:
257 oldlines
= [line
.rstrip('\r\n') for line
in file.readlines()]
260 for line
in oldlines
:
261 m
= runre
.match(line
)
263 runlines
.append(m
.group('tool'))
267 for line
in runlines
:
268 if line
.endswith('\\'):
269 continuation
+= line
[:-2] + ' '
271 newrunlines
.append(continuation
+ line
)
274 newrunlines
.append(continuation
)
276 for line
in newrunlines
:
277 m
= filecheckre
.match(line
)
281 tool
, filecheck
= m
.group('tool', 'filecheck')
282 filecheck
= shlex
.split(filecheck
)
283 tool
= shlex
.split(tool
)
284 if known
.bindir
is not None:
285 tool
[0] = complete_exename(known
.bindir
, tool
[0])
286 if os
.path
.isdir(llvm_tools_dir
):
287 tool
[0] = complete_exename(llvm_tools_dir
, tool
[0])
288 check_prefix
= filecheckparser
.parse_known_args(filecheck
)[0].check_prefix
289 if known
.prefix_only
is not None and not check_prefix
in known
.prefix_only
:
291 if check_prefix
in checkprefixes
:
293 checkprefixes
.append(check_prefix
)
298 toolarg
= toolarg
.replace('%s', filename
)
299 toolarg
= toolarg
.replace('%S', os
.path
.dirname(filename
))
300 if toolarg
== '%loadPolly':
301 if not llvm_polly_link_into_tools
:
302 newtool
+= ['-load',os
.path
.join(polly_lib_dir
,'LLVMPolly' + shlibext
)]
303 newtool
.append('-polly-process-unprofitable')
304 newtool
.append('-polly-remarks-minimal')
305 elif toolarg
== '2>&1':
306 optstderr
= subprocess
.STDOUT
308 newtool
.append(toolarg
)
315 inpfile
= tool
[i
+ 1]
320 with
open(inpfile
) as inp
:
321 retlines
= subprocess
.check_output(tool
,universal_newlines
=True,stdin
=inp
,stderr
=optstderr
)
323 retlines
= subprocess
.check_output(tool
,universal_newlines
=True,stderr
=optstderr
)
324 retlines
= [line
.replace('\t', ' ') for line
in retlines
.splitlines()]
326 for checkme
in known
.check_include
+ known
.check_label_include
:
327 parts
= checkme
.split('=')
329 if parts
[0] == check_prefix
:
330 check_include
.append(parts
[1])
332 check_include
.append(checkme
)
335 filtered_retlines
= []
336 classified_retlines
= []
338 for line
,kind
in ((line
,class1
.union(class2
)) for line
,class1
,class2
in zip(retlines
,classyfier1(retlines
), classyfier2(retlines
))):
339 match
= kind
.intersection(check_include
)
341 if lastmatch
!= match
:
342 filtered_retlines
.append('')
343 classified_retlines
.append({'Separator'})
344 filtered_retlines
.append(line
)
345 classified_retlines
.append(kind
)
348 retlines
= filtered_retlines
350 classified_retlines
= (set() for line
in retlines
)
352 rtrim_emptylines(retlines
)
353 ltrim_emptylines(retlines
,classified_retlines
)
354 retlines
= [replre
.sub(lambda m
: replrepl
[m
.group(0)], line
) for line
in retlines
]
355 indent
= common_indent(retlines
)
356 retlines
= [line
[indent
:] for line
in retlines
]
358 previous_was_empty
= True
359 for line
,kind
in zip(retlines
,classified_retlines
):
361 if known
.check_style
== 'CHECK' and known
.check_label_include
:
362 if not kind
.isdisjoint(known
.check_label_include
):
363 checklines
.append('; ' + check_prefix
+ '-LABEL: ' + line
)
365 checklines
.append('; ' + check_prefix
+ ': ' + line
)
366 elif known
.check_style
== 'CHECK':
367 checklines
.append('; ' + check_prefix
+ ': ' + line
)
368 elif known
.check_label_include
and known
.check_label_include
:
369 if not kind
.isdisjoint(known
.check_label_include
):
370 checklines
.append('; ' + check_prefix
+ '-LABEL: ' + line
)
371 elif previous_was_empty
:
372 checklines
.append('; ' + check_prefix
+ ': ' + line
)
374 checklines
.append('; ' + check_prefix
+ '-NEXT: ' + line
)
376 if previous_was_empty
:
377 checklines
.append('; ' + check_prefix
+ ': ' + line
)
379 checklines
.append('; ' + check_prefix
+ '-NEXT: ' + line
)
380 previous_was_empty
= False
382 if not 'Separator' in kind
or known
.check_part_newline
:
383 checklines
.append(';')
384 previous_was_empty
= True
385 allchecklines
.append(checklines
)
387 if not checkprefixes
:
390 checkre
= re
.compile(r
'^\s*\;\s*(' + '|'.join([re
.escape(s
) for s
in checkprefixes
]) + ')(\-NEXT|\-DAG|\-NOT|\-LABEL|\-SAME)?\s*\:')
391 firstcheckline
= None
392 firstnoncommentline
= None
398 for line
in oldlines
:
399 if checkre
.match(line
):
400 if firstcheckline
is None:
401 firstcheckline
= len(newlines
) + len(emptylines
)
403 uptonowlines
+= emptylines
406 elif emptyline
.fullmatch(line
):
407 emptylines
.append(line
)
409 newlines
+= uptonowlines
410 newlines
+= emptylines
411 newlines
.append(line
)
416 for i
,line
in enumerate(newlines
):
417 if not commentline
.fullmatch(line
):
418 firstnoncommentline
= i
421 with
open(outfile
,'w',newline
='') as file:
422 def writelines(lines
):
427 if firstcheckline
is not None and known
.check_position
== 'autodetect':
428 writelines(newlines
[:firstcheckline
])
429 writelines(uptonowlines
)
430 for i
,checklines
in enumerate(allchecklines
):
433 writelines(checklines
)
434 writelines(newlines
[firstcheckline
:])
435 writelines(emptylines
)
436 elif firstnoncommentline
is not None and known
.check_position
== 'before-content':
437 headerlines
= newlines
[:firstnoncommentline
]
438 rtrim_emptylines(headerlines
)
439 contentlines
= newlines
[firstnoncommentline
:]
440 ltrim_emptylines(contentlines
)
442 writelines(headerlines
)
443 for checklines
in allchecklines
:
445 writelines(checklines
)
447 writelines(contentlines
)
448 writelines(uptonowlines
)
449 writelines(emptylines
)
452 rtrim_emptylines(newlines
)
453 for checklines
in allchecklines
:
455 writelines(checklines
)
458 if __name__
== '__main__':