[AArch64] Fix SDNode type mismatches between *.td files and ISel (#116523)
[llvm-project.git] / llvm / utils / update_cc_test_checks.py
blob3ffb07ddf6ad8fc9dc1ec5222b05a7eade47328d
1 #!/usr/bin/env python3
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
9 Usage:
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
13 """
15 from __future__ import print_function
17 import argparse
18 import collections
19 import json
20 import os
21 import re
22 import shlex
23 import shutil
24 import subprocess
25 import sys
26 import tempfile
28 from UpdateTestChecks import common
30 SUBST = {
31 "%clang": [],
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(
49 json_dump_args,
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)
59 sys.exit(2)
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:
66 if node_kind in (
67 "NamespaceDecl",
68 "LinkageSpecDecl",
69 "TranslationUnitDecl",
70 "CXXRecordDecl",
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"]
79 else:
80 inner_loc = None
81 inner_search = None
82 if "inner" in node:
83 for inner in node["inner"]:
84 parse_clang_ast_json(inner, inner_loc, inner_search)
85 # Otherwise we ignore everything except functions:
86 if node_kind not in (
87 "FunctionDecl",
88 "CXXMethodDecl",
89 "CXXConstructorDecl",
90 "CXXDestructorDecl",
91 "CXXConversionDecl",
93 return
94 if loc is None:
95 loc = node["loc"]
96 if node.get("isImplicit") is True and node.get("storageClass") == "extern":
97 common.debug("Skipping builtin function:", node["name"], "@", loc)
98 return
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
102 if line is None:
103 common.debug(
104 "Skipping function without line number:", node["name"], "@", loc
106 return
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.
112 has_body = False
113 if "inner" in node:
114 for i in node["inner"]:
115 if i.get("kind", "ParmVarDecl") != "ParmVarDecl":
116 has_body = True
117 break
118 if not has_body:
119 common.debug("Skipping function without body:", node["name"], "@", loc)
120 return
121 spell = node["name"]
122 if search is None:
123 search = spell
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?")
130 sys.exit(2)
131 parse_clang_ast_json(ast, None, None)
133 for line, funcs in sorted(ret.items()):
134 for func in funcs:
135 common.debug(
136 "line {}: found function {}".format(line + 1, func), file=sys.stderr
138 if not ret:
139 common.warn("Did not find any functions using", " ".join(json_dump_args))
140 return ret
143 def str_to_commandline(value):
144 if not value:
145 return []
146 return shlex.split(value)
149 def infer_dependent_args(args):
150 if not args.clang:
151 if not args.llvm_bin:
152 args.clang = "clang"
153 else:
154 args.clang = os.path.join(args.llvm_bin, "clang")
155 if not args.opt:
156 if not args.llvm_bin:
157 args.opt = "opt"
158 else:
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)
170 def config():
171 parser = argparse.ArgumentParser(
172 description=__doc__, formatter_class=argparse.RawTextHelpFormatter
174 parser.add_argument("--llvm-bin", help="llvm $prefix/bin path")
175 parser.add_argument(
176 "--clang", help='"clang" executable, defaults to $llvm_bin/clang'
178 parser.add_argument(
179 "--clang-args",
180 default=[],
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')
185 parser.add_argument(
186 "--functions",
187 nargs="+",
188 help="A list of function name regexes. "
189 "If specified, update CHECK lines for functions matching at least one regex",
191 parser.add_argument(
192 "--x86_extra_scrub",
193 action="store_true",
194 help="Use more regex for x86 matching to reduce diffs between various subtargets",
196 parser.add_argument(
197 "--function-signature",
198 action="store_true",
199 help="Keep function signature information around for the check line",
201 parser.add_argument(
202 "--check-attributes",
203 action="store_true",
204 help='Check "Function Attributes" for functions',
206 parser.add_argument(
207 "--check-globals",
208 nargs="?",
209 const="all",
210 default="default",
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)
220 sys.exit(1)
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.
225 try:
226 builtin_include_dir = (
227 subprocess.check_output([args.clang, "-print-file-name=include"])
228 .decode()
229 .strip()
231 SUBST["%clang_cc1"] = [
232 "-cc1",
233 "-internal-isystem",
234 builtin_include_dir,
235 "-nostdsysteminc",
237 except subprocess.CalledProcessError:
238 common.warn(
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.
247 args.opt = None
249 return args, parser
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())
260 f.flush()
261 if extra_args[0] == "opt":
262 if args.opt is None:
263 print(
264 filename,
265 "needs to run opt. " "Please specify --llvm-bin or --opt",
266 file=sys.stderr,
268 sys.exit(1)
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)
276 else:
277 print(
278 "The clang command line should include -emit-llvm as asm tests "
279 "are discouraged in Clang testsuite.",
280 file=sys.stderr,
282 sys.exit(1)
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)
294 sys.exit(3)
297 def main():
298 initial_args, parser = config()
299 script_name = os.path.basename(__file__)
301 for ti in common.itertests(
302 initial_args.tests,
303 parser,
304 "utils/" + script_name,
305 comment_prefix="//",
306 argparse_callback=infer_dependent_args,
308 # Build a list of filechecked and non-filechecked RUN lines.
309 run_list = []
310 line2func_list = collections.defaultdict(list)
312 subs = {
313 "%s": ti.path,
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("|")]
321 triple_in_cmd = None
322 m = common.TRIPLE_ARG_RE.search(commands[0])
323 if m:
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.
331 for s in subs:
332 exec_args = [
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))
336 continue
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]]
341 for s in subs:
342 clang_args = [
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))
354 continue
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,
366 flags=ti.args,
367 scrubber_args=[],
368 path=ti.path,
369 ginfo=ginfo,
372 for prefixes, args, extra_commands, triple_in_cmd in run_list:
373 # Execute non-filechecked runline.
374 if not prefixes:
375 print(
376 "NOTE: Executing non-FileChecked RUN line: " + " ".join(args),
377 file=sys.stderr,
379 exec_run_line(args)
380 continue
382 clang_args = args
383 common.debug("Extracted clang cmd: clang {}".format(clang_args))
384 common.debug("Extracted FileCheck prefixes: {}".format(prefixes))
386 get_function_body(
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]])
398 output_lines = []
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",
405 True,
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
418 # lines.
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(
424 my_output_lines,
425 "//",
426 prefixes,
427 func_dict,
428 func,
429 False,
430 ti.args.function_signature,
431 ginfo,
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(),
440 "//",
441 run_list,
442 output_lines,
443 ginfo,
444 global_vars_seen_dict,
445 False,
446 True,
447 ti.args.check_globals,
450 generated_prefixes.extend(
451 common.add_checks_at_end(
452 output_lines,
453 filecheck_run_list,
454 builder.func_order(),
455 "//",
456 lambda my_output_lines, prefixes, func: check_generator(
457 my_output_lines, prefixes, func
461 else:
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
467 include_line = True
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:
473 continue
474 if idx in line2func_list:
475 added = set()
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:
484 continue
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()
492 output_lines.pop()
493 last_line = output_lines[-1].strip()
494 if (
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(),
501 "//",
502 run_list,
503 output_lines,
504 ginfo,
505 global_vars_seen_dict,
506 False,
507 True,
508 ti.args.check_globals,
511 has_checked_pre_function_globals = True
512 if added:
513 output_lines.append("//")
514 added.add(mangled)
515 generated_prefixes.extend(
516 common.add_ir_checks(
517 output_lines,
518 "//",
519 filecheck_run_list,
520 func_dict,
521 mangled,
522 False,
523 args.function_signature,
524 ginfo,
525 global_vars_seen_dict,
526 is_filtered=builder.is_filtered(),
529 if line.rstrip("\n") == "//":
530 include_line = False
532 if include_line:
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(),
539 "//",
540 run_list,
541 output_lines,
542 ginfo,
543 global_vars_seen_dict,
544 False,
545 False,
546 ti.args.check_globals,
549 if ti.args.gen_unused_prefix_body:
550 output_lines.extend(
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])
557 return 0
560 if __name__ == "__main__":
561 sys.exit(main())