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 {
26 "llvm_polly_link_into_tools-notfound",
29 runre
= re
.compile(r
"\s*\;\s*RUN\s*\:(?P<tool>.*)")
30 filecheckre
= re
.compile(r
"\s*(?P<tool>.*)\|\s*(?P<filecheck>FileCheck\s[^|]*)")
31 emptyline
= re
.compile(r
"\s*(\;\s*)?")
32 commentline
= re
.compile(r
"\s*(\;.*)?")
35 def ltrim_emptylines(lines
, meta
=None):
36 while len(lines
) and emptyline
.fullmatch(lines
[0]):
42 def rtrim_emptylines(lines
):
43 while len(lines
) and emptyline
.fullmatch(lines
[-1]):
47 def trim_emptylines(lines
):
48 ltrim_emptylines(lines
)
49 rtrim_emptylines(lines
)
52 def complete_exename(path
, filename
):
53 complpath
= os
.path
.join(path
, filename
)
54 if os
.path
.isfile(complpath
):
56 elif os
.path
.isfile(complpath
+ ".exe"):
57 return complpath
+ ".exe"
62 for i
, c
in enumerate(line
):
63 if c
!= " " and c
!= "\t":
68 def common_indent(lines
):
69 indentions
= (indention(line
) for line
in lines
)
70 indentions
= (indent
for indent
in indentions
if indent
is not None)
71 return min(indentions
, default
=0)
74 funcre
= re
.compile(r
"^ Function: \S*$")
75 regionre
= re
.compile(r
"^ Region: \S*$")
76 depthre
= re
.compile(r
"^ Max Loop Depth: .*")
77 paramre
= re
.compile(r
" [0-9a-z-A-Z_]+\: .*")
80 def classyfier1(lines
):
85 "Printing analysis 'Polly - Calculate dependences' for region: "
87 yield {"PrintingDependenceInfo"}
88 elif line
.startswith("remark: "):
90 elif funcre
.fullmatch(line
):
92 elif regionre
.fullmatch(line
):
94 elif depthre
.fullmatch(line
):
95 yield {"MaxLoopDepth"}
96 elif line
== " Invariant Accesses: {":
98 yield {"InvariantAccesses"}
102 elif line
== " Context:":
106 elif line
== " Assumed Context:":
107 yield {"AssumedContext"}
109 yield {"AssumedContext"}
110 elif line
== " Invalid Context:":
111 yield {"InvalidContext"}
113 yield {"InvalidContext"}
114 elif line
== " Boundary Context:":
115 yield {"BoundaryContext"}
117 yield {"BoundaryContext"}
119 while paramre
.fullmatch(line
):
123 elif line
== " Arrays {":
129 elif line
== " Arrays (Bounds as pw_affs) {":
131 yield {"PwAffArrays"}
135 elif line
.startswith(" Alias Groups ("):
137 yield {"AliasGroups"}
139 if not line
.startswith(" "):
142 elif line
== " Statements {":
148 elif line
== " RAW dependences:":
149 yield {"RAWDep", "BasicDep", "Dep", "DepInfo"}
151 while line
.startswith(" "):
152 yield {"RAWDep", "BasicDep", "Dep", "DepInfo"}
155 elif line
== " WAR dependences:":
156 yield {"WARDep", "BasicDep", "Dep", "DepInfo"}
158 while line
.startswith(" "):
159 yield {"WARDep", "BasicDep", "Dep", "DepInfo"}
162 elif line
== " WAW dependences:":
163 yield {"WAWDep", "BasicDep", "Dep", "DepInfo"}
165 while line
.startswith(" "):
166 yield {"WAWDep", "BasicDep", "Dep", "DepInfo"}
169 elif line
== " Reduction dependences:":
170 yield {"RedDep", "Dep", "DepInfo"}
172 while line
.startswith(" "):
173 yield {"RedDep", "Dep", "DepInfo"}
176 elif line
== " Transitive closure of reduction dependences:":
177 yield {"TransitiveClosureDep", "DepInfo"}
179 while line
.startswith(" "):
180 yield {"TransitiveClosureDep", "DepInfo"}
183 elif line
.startswith("New access function '"):
184 yield {"NewAccessFunction"}
185 elif line
== "Schedule before flattening {":
187 yield {"ScheduleBeforeFlattening"}
191 elif line
== "Schedule after flattening {":
193 yield {"ScheduleAfterFlattening"}
202 def classyfier2(lines
):
206 if funcre
.fullmatch(line
):
207 while line
.startswith(" "):
208 yield {"FunctionDetail"}
211 elif line
.startswith(
212 "Printing analysis 'Polly - Generate an AST from the SCoP (isl)' for region: "
214 yield {"PrintingIslAst"}
216 while not line
.startswith("Printing analysis"):
225 replrepl
= {"{{": "{{[{][{]}}", "}}": "{{[}][}]}}", "[[": "{{\[\[}}", "]]": "{{\]\]}}"}
226 replre
= re
.compile("|".join(re
.escape(k
) for k
in replrepl
.keys()))
230 parser
= argparse
.ArgumentParser(description
="Update CHECK lines")
232 "testfile", help="File to update (absolute or relative to --testdir)"
236 choices
=["CHECK", "CHECK-NEXT"],
237 default
="CHECK-NEXT",
238 help="What kind of checks lines to generate",
242 choices
=["end", "before-content", "autodetect"],
243 default
="autodetect",
244 help="Where to add the CHECK lines into the file; 'autodetect' searches for the first 'CHECK' line ind inserts it there",
250 help="What parts of the output lines to check; use syntax 'CHECK=include' to apply to one CHECK-prefix only (by default, everything)",
253 "--check-label-include",
256 help="Use CHECK-LABEL for these includes",
259 "--check-part-newline",
261 help="Add empty line between different check parts",
267 help="Update only these prefixes (default: all)",
269 parser
.add_argument("--bindir", help="Location of the opt program")
270 parser
.add_argument("--testdir", help="Root dir for unit tests")
272 "--inplace", "-i", action
="store_true", help="Replace input file"
274 parser
.add_argument("--output", "-o", help="Write changed input to this file")
275 known
= parser
.parse_args()
277 if not known
.inplace
and known
.output
is None:
278 print("Must specify what to do with output (--output or --inplace)")
280 if known
.inplace
and known
.output
is not None:
281 print("--inplace and --output are mutually exclusive")
284 outfile
= known
.output
286 filecheckparser
= argparse
.ArgumentParser(add_help
=False)
287 filecheckparser
.add_argument("-check-prefix", "--check-prefix", default
="CHECK")
289 filename
= known
.testfile
290 for dir in [".", known
.testdir
, os
.path
.join(polly_src_dir
, "test"), polly_src_dir
]:
293 testfilename
= os
.path
.join(dir, filename
)
294 if os
.path
.isfile(testfilename
):
295 filename
= testfilename
304 with
open(filename
, "r") as file:
305 oldlines
= [line
.rstrip("\r\n") for line
in file.readlines()]
308 for line
in oldlines
:
309 m
= runre
.match(line
)
311 runlines
.append(m
.group("tool"))
315 for line
in runlines
:
316 if line
.endswith("\\"):
317 continuation
+= line
[:-2] + " "
319 newrunlines
.append(continuation
+ line
)
322 newrunlines
.append(continuation
)
324 for line
in newrunlines
:
325 m
= filecheckre
.match(line
)
329 tool
, filecheck
= m
.group("tool", "filecheck")
330 filecheck
= shlex
.split(filecheck
)
331 tool
= shlex
.split(tool
)
332 if known
.bindir
is not None:
333 tool
[0] = complete_exename(known
.bindir
, tool
[0])
334 if os
.path
.isdir(llvm_tools_dir
):
335 tool
[0] = complete_exename(llvm_tools_dir
, tool
[0])
336 check_prefix
= filecheckparser
.parse_known_args(filecheck
)[0].check_prefix
337 if known
.prefix_only
is not None and not check_prefix
in known
.prefix_only
:
339 if check_prefix
in checkprefixes
:
341 checkprefixes
.append(check_prefix
)
346 toolarg
= toolarg
.replace("%s", filename
)
347 toolarg
= toolarg
.replace("%S", os
.path
.dirname(filename
))
348 if toolarg
== "%loadPolly":
349 if not llvm_polly_link_into_tools
:
352 os
.path
.join(polly_lib_dir
, "LLVMPolly" + shlibext
),
354 newtool
.append("-polly-process-unprofitable")
355 newtool
.append("-polly-remarks-minimal")
356 elif toolarg
== "2>&1":
357 optstderr
= subprocess
.STDOUT
359 newtool
.append(toolarg
)
366 inpfile
= tool
[i
+ 1]
371 with
open(inpfile
) as inp
:
372 retlines
= subprocess
.check_output(
373 tool
, universal_newlines
=True, stdin
=inp
, stderr
=optstderr
376 retlines
= subprocess
.check_output(
377 tool
, universal_newlines
=True, stderr
=optstderr
379 retlines
= [line
.replace("\t", " ") for line
in retlines
.splitlines()]
381 for checkme
in known
.check_include
+ known
.check_label_include
:
382 parts
= checkme
.split("=")
384 if parts
[0] == check_prefix
:
385 check_include
.append(parts
[1])
387 check_include
.append(checkme
)
390 filtered_retlines
= []
391 classified_retlines
= []
394 (line
, class1
.union(class2
))
395 for line
, class1
, class2
in zip(
396 retlines
, classyfier1(retlines
), classyfier2(retlines
)
399 match
= kind
.intersection(check_include
)
401 if lastmatch
!= match
:
402 filtered_retlines
.append("")
403 classified_retlines
.append({"Separator"})
404 filtered_retlines
.append(line
)
405 classified_retlines
.append(kind
)
408 retlines
= filtered_retlines
410 classified_retlines
= (set() for line
in retlines
)
412 rtrim_emptylines(retlines
)
413 ltrim_emptylines(retlines
, classified_retlines
)
415 replre
.sub(lambda m
: replrepl
[m
.group(0)], line
) for line
in retlines
417 indent
= common_indent(retlines
)
418 retlines
= [line
[indent
:] for line
in retlines
]
420 previous_was_empty
= True
421 for line
, kind
in zip(retlines
, classified_retlines
):
423 if known
.check_style
== "CHECK" and known
.check_label_include
:
424 if not kind
.isdisjoint(known
.check_label_include
):
425 checklines
.append("; " + check_prefix
+ "-LABEL: " + line
)
427 checklines
.append("; " + check_prefix
+ ": " + line
)
428 elif known
.check_style
== "CHECK":
429 checklines
.append("; " + check_prefix
+ ": " + line
)
430 elif known
.check_label_include
and known
.check_label_include
:
431 if not kind
.isdisjoint(known
.check_label_include
):
432 checklines
.append("; " + check_prefix
+ "-LABEL: " + line
)
433 elif previous_was_empty
:
434 checklines
.append("; " + check_prefix
+ ": " + line
)
436 checklines
.append("; " + check_prefix
+ "-NEXT: " + line
)
438 if previous_was_empty
:
439 checklines
.append("; " + check_prefix
+ ": " + line
)
441 checklines
.append("; " + check_prefix
+ "-NEXT: " + line
)
442 previous_was_empty
= False
444 if not "Separator" in kind
or known
.check_part_newline
:
445 checklines
.append(";")
446 previous_was_empty
= True
447 allchecklines
.append(checklines
)
449 if not checkprefixes
:
452 checkre
= re
.compile(
454 + "|".join([re
.escape(s
) for s
in checkprefixes
])
455 + ")(\-NEXT|\-DAG|\-NOT|\-LABEL|\-SAME)?\s*\:"
457 firstcheckline
= None
458 firstnoncommentline
= None
464 for line
in oldlines
:
465 if checkre
.match(line
):
466 if firstcheckline
is None:
467 firstcheckline
= len(newlines
) + len(emptylines
)
469 uptonowlines
+= emptylines
472 elif emptyline
.fullmatch(line
):
473 emptylines
.append(line
)
475 newlines
+= uptonowlines
476 newlines
+= emptylines
477 newlines
.append(line
)
482 for i
, line
in enumerate(newlines
):
483 if not commentline
.fullmatch(line
):
484 firstnoncommentline
= i
487 with
open(outfile
, "w", newline
="") as file:
489 def writelines(lines
):
494 if firstcheckline
is not None and known
.check_position
== "autodetect":
495 writelines(newlines
[:firstcheckline
])
496 writelines(uptonowlines
)
497 for i
, checklines
in enumerate(allchecklines
):
500 writelines(checklines
)
501 writelines(newlines
[firstcheckline
:])
502 writelines(emptylines
)
504 firstnoncommentline
is not None and known
.check_position
== "before-content"
506 headerlines
= newlines
[:firstnoncommentline
]
507 rtrim_emptylines(headerlines
)
508 contentlines
= newlines
[firstnoncommentline
:]
509 ltrim_emptylines(contentlines
)
511 writelines(headerlines
)
512 for checklines
in allchecklines
:
514 writelines(checklines
)
516 writelines(contentlines
)
517 writelines(uptonowlines
)
518 writelines(emptylines
)
521 rtrim_emptylines(newlines
)
522 for checklines
in allchecklines
:
524 writelines(checklines
)
527 if __name__
== "__main__":