2 """A utility to update LLVM IR CHECK lines in C/C++ FileCheck test files.
4 Example RUN lines in .c/.cc test files:
6 // RUN: %clang -emit-llvm -S %s -o - -O2 | FileCheck %s
7 // RUN: %clangxx -emit-llvm -S %s -o - -O2 | FileCheck -check-prefix=CHECK-A %s
11 % utils/update_cc_test_checks.py --llvm-bin=release/bin test/a.cc
12 % utils/update_cc_test_checks.py --clang=release/bin/clang /tmp/c/a.cc
15 from __future__
import print_function
28 from UpdateTestChecks
import common
32 "%clang_cc1": ["-cc1"],
33 "%clangxx": ["--driver-mode=g++"],
37 def get_line2func_list(args
, clang_args
):
38 ret
= collections
.defaultdict(list)
39 # Use clang's JSON AST dump to get the mangled name
40 json_dump_args
= [args
.clang
] + clang_args
+ ["-fsyntax-only", "-o", "-"]
41 if "-cc1" not in json_dump_args
:
42 # For tests that invoke %clang instead if %clang_cc1 we have to use
43 # -Xclang -ast-dump=json instead:
44 json_dump_args
.append("-Xclang")
45 json_dump_args
.append("-ast-dump=json")
46 common
.debug("Running", " ".join(json_dump_args
))
48 popen
= subprocess
.Popen(
50 stdout
=subprocess
.PIPE
,
51 stderr
=subprocess
.PIPE
,
52 universal_newlines
=True,
54 stdout
, stderr
= popen
.communicate()
55 if popen
.returncode
!= 0:
56 sys
.stderr
.write("Failed to run " + " ".join(json_dump_args
) + "\n")
57 sys
.stderr
.write(stderr
)
58 sys
.stderr
.write(stdout
)
61 # Parse the clang JSON and add all children of type FunctionDecl.
62 # TODO: Should we add checks for global variables being emitted?
63 def parse_clang_ast_json(node
, loc
, search
):
64 node_kind
= node
["kind"]
65 # Recurse for the following nodes that can contain nested function decls:
69 "TranslationUnitDecl",
71 "ClassTemplateSpecializationDecl",
73 # Specializations must use the loc from the specialization, not the
74 # template, and search for the class's spelling as the specialization
75 # does not mention the method names in the source.
76 if node_kind
== "ClassTemplateSpecializationDecl":
77 inner_loc
= node
["loc"]
78 inner_search
= node
["name"]
83 for inner
in node
["inner"]:
84 parse_clang_ast_json(inner
, inner_loc
, inner_search
)
85 # Otherwise we ignore everything except functions:
96 if node
.get("isImplicit") is True and node
.get("storageClass") == "extern":
97 common
.debug("Skipping builtin function:", node
["name"], "@", loc
)
99 common
.debug("Found function:", node
["kind"], node
["name"], "@", loc
)
100 line
= loc
.get("line")
101 # If there is no line it is probably a builtin function -> skip
104 "Skipping function without line number:", node
["name"], "@", loc
108 # If there is no 'inner' object, it is a function declaration and we can
109 # skip it. However, function declarations may also contain an 'inner' list,
110 # but in that case it will only contains ParmVarDecls. If we find an entry
111 # that is not a ParmVarDecl, we know that this is a function definition.
114 for i
in node
["inner"]:
115 if i
.get("kind", "ParmVarDecl") != "ParmVarDecl":
119 common
.debug("Skipping function without body:", node
["name"], "@", loc
)
124 mangled
= node
.get("mangledName", spell
)
125 ret
[int(line
) - 1].append((spell
, mangled
, search
))
127 ast
= json
.loads(stdout
)
128 if ast
["kind"] != "TranslationUnitDecl":
129 common
.error("Clang AST dump JSON format changed?")
131 parse_clang_ast_json(ast
, None, None)
133 for line
, funcs
in sorted(ret
.items()):
136 "line {}: found function {}".format(line
+ 1, func
), file=sys
.stderr
139 common
.warn("Did not find any functions using", " ".join(json_dump_args
))
143 def str_to_commandline(value
):
146 return shlex
.split(value
)
149 def infer_dependent_args(args
):
151 if not args
.llvm_bin
:
154 args
.clang
= os
.path
.join(args
.llvm_bin
, "clang")
156 if not args
.llvm_bin
:
159 args
.opt
= os
.path
.join(args
.llvm_bin
, "opt")
162 def find_executable(executable
):
163 _
, ext
= os
.path
.splitext(executable
)
164 if sys
.platform
== "win32" and ext
!= ".exe":
165 executable
= executable
+ ".exe"
167 return shutil
.which(executable
)
171 parser
= argparse
.ArgumentParser(
172 description
=__doc__
, formatter_class
=argparse
.RawTextHelpFormatter
174 parser
.add_argument("--llvm-bin", help="llvm $prefix/bin path")
176 "--clang", help='"clang" executable, defaults to $llvm_bin/clang'
181 type=str_to_commandline
,
182 help="Space-separated extra args to clang, e.g. --clang-args=-v",
184 parser
.add_argument("--opt", help='"opt" executable, defaults to $llvm_bin/opt')
188 help="A list of function name regexes. "
189 "If specified, update CHECK lines for functions matching at least one regex",
194 help="Use more regex for x86 matching to reduce diffs between various subtargets",
197 "--function-signature",
199 help="Keep function signature information around for the check line",
202 "--check-attributes",
204 help='Check "Function Attributes" for functions',
211 choices
=["none", "smart", "all"],
212 help="Check global entries (global variables, metadata, attribute sets, ...) for functions",
214 parser
.add_argument("tests", nargs
="+")
215 args
= common
.parse_commandline_args(parser
)
216 infer_dependent_args(args
)
218 if not find_executable(args
.clang
):
219 print("Please specify --llvm-bin or --clang", file=sys
.stderr
)
222 # Determine the builtin includes directory so that we can update tests that
223 # depend on the builtin headers. See get_clang_builtin_include_dir() and
224 # use_clang() in llvm/utils/lit/lit/llvm/config.py.
226 builtin_include_dir
= (
227 subprocess
.check_output([args
.clang
, "-print-file-name=include"])
231 SUBST
["%clang_cc1"] = [
237 except subprocess
.CalledProcessError
:
239 "Could not determine clang builtins directory, some tests "
240 "might not update correctly."
243 if not find_executable(args
.opt
):
244 # Many uses of this tool will not need an opt binary, because it's only
245 # needed for updating a test that runs clang | opt | FileCheck. So we
246 # defer this error message until we find that opt is actually needed.
252 def get_function_body(builder
, args
, filename
, clang_args
, extra_commands
, prefixes
):
253 # TODO Clean up duplication of asm/common build_function_body_dictionary
254 # Invoke external tool and extract function bodies.
255 raw_tool_output
= common
.invoke_tool(args
.clang
, clang_args
, filename
)
256 for extra_command
in extra_commands
:
257 extra_args
= shlex
.split(extra_command
)
258 with tempfile
.NamedTemporaryFile() as f
:
259 f
.write(raw_tool_output
.encode())
261 if extra_args
[0] == "opt":
265 "needs to run opt. " "Please specify --llvm-bin or --opt",
269 extra_args
[0] = args
.opt
270 raw_tool_output
= common
.invoke_tool(extra_args
[0], extra_args
[1:], f
.name
)
271 if "-emit-llvm" in clang_args
:
272 builder
.process_run_line(
273 common
.OPT_FUNCTION_RE
, common
.scrub_body
, raw_tool_output
, prefixes
275 builder
.processed_prefixes(prefixes
)
278 "The clang command line should include -emit-llvm as asm tests "
279 "are discouraged in Clang testsuite.",
285 def exec_run_line(exe
):
286 popen
= subprocess
.Popen(
287 exe
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, universal_newlines
=True
289 stdout
, stderr
= popen
.communicate()
290 if popen
.returncode
!= 0:
291 sys
.stderr
.write("Failed to run " + " ".join(exe
) + "\n")
292 sys
.stderr
.write(stderr
)
293 sys
.stderr
.write(stdout
)
298 initial_args
, parser
= config()
299 script_name
= os
.path
.basename(__file__
)
301 for ti
in common
.itertests(
304 "utils/" + script_name
,
306 argparse_callback
=infer_dependent_args
,
308 # Build a list of filechecked and non-filechecked RUN lines.
310 line2func_list
= collections
.defaultdict(list)
314 "%t": tempfile
.NamedTemporaryFile().name
,
315 "%S": os
.path
.dirname(ti
.path
),
318 for l
in ti
.run_lines
:
319 commands
= [cmd
.strip() for cmd
in l
.split("|")]
322 m
= common
.TRIPLE_ARG_RE
.search(commands
[0])
324 triple_in_cmd
= m
.groups()[0]
326 # Parse executable args.
327 exec_args
= shlex
.split(commands
[0])
328 # Execute non-clang runline.
329 if exec_args
[0] not in SUBST
:
330 # Do lit-like substitutions.
333 i
.replace(s
, subs
[s
]) if s
in i
else i
for i
in exec_args
335 run_list
.append((None, exec_args
, None, None))
337 # This is a clang runline, apply %clang substitution rule, do lit-like substitutions,
338 # and append args.clang_args
339 clang_args
= exec_args
340 clang_args
[0:1] = SUBST
[clang_args
[0]]
343 i
.replace(s
, subs
[s
]) if s
in i
else i
for i
in clang_args
345 clang_args
+= ti
.args
.clang_args
347 # Extract -check-prefix in FileCheck args
348 filecheck_cmd
= commands
[-1]
349 common
.verify_filecheck_prefixes(filecheck_cmd
)
350 if not filecheck_cmd
.startswith("FileCheck "):
351 # Execute non-filechecked clang runline.
352 exe
= [ti
.args
.clang
] + clang_args
353 run_list
.append((None, exe
, None, None))
356 check_prefixes
= common
.get_check_prefixes(filecheck_cmd
)
357 run_list
.append((check_prefixes
, clang_args
, commands
[1:-1], triple_in_cmd
))
359 # Execute clang, generate LLVM IR, and extract functions.
361 # Store only filechecked runlines.
362 filecheck_run_list
= [i
for i
in run_list
if i
[0]]
363 ginfo
= common
.make_ir_generalizer(version
=ti
.args
.version
)
364 builder
= common
.FunctionTestBuilder(
365 run_list
=filecheck_run_list
,
372 for prefixes
, args
, extra_commands
, triple_in_cmd
in run_list
:
373 # Execute non-filechecked runline.
376 "NOTE: Executing non-FileChecked RUN line: " + " ".join(args
),
383 common
.debug("Extracted clang cmd: clang {}".format(clang_args
))
384 common
.debug("Extracted FileCheck prefixes: {}".format(prefixes
))
387 builder
, ti
.args
, ti
.path
, clang_args
, extra_commands
, prefixes
390 # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
391 # mangled names. Forward all clang args for now.
392 for k
, v
in get_line2func_list(ti
.args
, clang_args
).items():
393 line2func_list
[k
].extend(v
)
395 func_dict
= builder
.finish_and_get_func_dict()
396 global_vars_seen_dict
= {}
397 prefix_set
= set([prefix
for p
in filecheck_run_list
for prefix
in p
[0]])
399 has_checked_pre_function_globals
= False
401 include_generated_funcs
= common
.find_arg_in_test(
403 lambda args
: ti
.args
.include_generated_funcs
,
404 "--include-generated-funcs",
407 generated_prefixes
= []
408 if include_generated_funcs
:
409 # Generate the appropriate checks for each function. We need to emit
410 # these in the order according to the generated output so that CHECK-LABEL
411 # works properly. func_order provides that.
413 # It turns out that when clang generates functions (for example, with
414 # -fopenmp), it can sometimes cause functions to be re-ordered in the
415 # output, even functions that exist in the source file. Therefore we
416 # can't insert check lines before each source function and instead have to
417 # put them at the end. So the first thing to do is dump out the source
419 common
.dump_input_lines(output_lines
, ti
, prefix_set
, "//")
421 # Now generate all the checks.
422 def check_generator(my_output_lines
, prefixes
, func
):
423 return common
.add_ir_checks(
430 ti
.args
.function_signature
,
432 global_vars_seen_dict
,
433 is_filtered
=builder
.is_filtered(),
436 if ti
.args
.check_globals
!= 'none':
437 generated_prefixes
.extend(
438 common
.add_global_checks(
439 builder
.global_var_dict(),
444 global_vars_seen_dict
,
447 ti
.args
.check_globals
,
450 generated_prefixes
.extend(
451 common
.add_checks_at_end(
454 builder
.func_order(),
456 lambda my_output_lines
, prefixes
, func
: check_generator(
457 my_output_lines
, prefixes
, func
462 # Normal mode. Put checks before each source function.
463 for line_info
in ti
.iterlines(output_lines
):
464 idx
= line_info
.line_number
465 line
= line_info
.line
466 args
= line_info
.args
468 m
= common
.CHECK_RE
.match(line
)
469 if m
and m
.group(1) in prefix_set
:
470 continue # Don't append the existing CHECK lines
471 # Skip special separator comments added by commmon.add_global_checks.
472 if line
.strip() == "//" + common
.SEPARATOR
:
474 if idx
in line2func_list
:
476 for spell
, mangled
, search
in line2func_list
[idx
]:
477 # One line may contain multiple function declarations.
478 # Skip if the mangled name has been added before.
479 # The line number may come from an included file, we simply require
480 # the search string (normally the function's spelling name, but is
481 # the class's spelling name for class specializations) to appear on
482 # the line to exclude functions from other files.
483 if mangled
in added
or search
not in line
:
485 if args
.functions
is None or any(
486 re
.search(regex
, spell
) for regex
in args
.functions
488 last_line
= output_lines
[-1].strip()
489 while last_line
== "//":
490 # Remove the comment line since we will generate a new comment
491 # line as part of common.add_ir_checks()
493 last_line
= output_lines
[-1].strip()
495 ti
.args
.check_globals
!= 'none'
496 and not has_checked_pre_function_globals
498 generated_prefixes
.extend(
499 common
.add_global_checks(
500 builder
.global_var_dict(),
505 global_vars_seen_dict
,
508 ti
.args
.check_globals
,
511 has_checked_pre_function_globals
= True
513 output_lines
.append("//")
515 generated_prefixes
.extend(
516 common
.add_ir_checks(
523 args
.function_signature
,
525 global_vars_seen_dict
,
526 is_filtered
=builder
.is_filtered(),
529 if line
.rstrip("\n") == "//":
533 output_lines
.append(line
.rstrip("\n"))
535 if ti
.args
.check_globals
!= 'none':
536 generated_prefixes
.extend(
537 common
.add_global_checks(
538 builder
.global_var_dict(),
543 global_vars_seen_dict
,
546 ti
.args
.check_globals
,
549 if ti
.args
.gen_unused_prefix_body
:
551 ti
.get_checks_for_unused_prefixes(run_list
, generated_prefixes
)
553 common
.debug("Writing %d lines to %s..." % (len(output_lines
), ti
.path
))
554 with
open(ti
.path
, "wb") as f
:
555 f
.writelines(["{}\n".format(l
).encode("utf-8") for l
in output_lines
])
560 if __name__
== "__main__":