[llvm-shlib] Fix the version naming style of libLLVM for Windows (#85710)
[llvm-project.git] / llvm / utils / update_cc_test_checks.py
blob28c6bb0409f3af4708680cccdfc77bf83d654d3b
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, False
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 builder = common.FunctionTestBuilder(
364 run_list=filecheck_run_list, flags=ti.args, scrubber_args=[], path=ti.path
367 for prefixes, args, extra_commands, triple_in_cmd in run_list:
368 # Execute non-filechecked runline.
369 if not prefixes:
370 print(
371 "NOTE: Executing non-FileChecked RUN line: " + " ".join(args),
372 file=sys.stderr,
374 exec_run_line(args)
375 continue
377 clang_args = args
378 common.debug("Extracted clang cmd: clang {}".format(clang_args))
379 common.debug("Extracted FileCheck prefixes: {}".format(prefixes))
381 get_function_body(
382 builder, ti.args, ti.path, clang_args, extra_commands, prefixes
385 # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
386 # mangled names. Forward all clang args for now.
387 for k, v in get_line2func_list(ti.args, clang_args).items():
388 line2func_list[k].extend(v)
390 func_dict = builder.finish_and_get_func_dict()
391 global_vars_seen_dict = {}
392 prefix_set = set([prefix for p in filecheck_run_list for prefix in p[0]])
393 output_lines = []
394 has_checked_pre_function_globals = False
396 include_generated_funcs = common.find_arg_in_test(
398 lambda args: ti.args.include_generated_funcs,
399 "--include-generated-funcs",
400 True,
402 generated_prefixes = []
403 if include_generated_funcs:
404 # Generate the appropriate checks for each function. We need to emit
405 # these in the order according to the generated output so that CHECK-LABEL
406 # works properly. func_order provides that.
408 # It turns out that when clang generates functions (for example, with
409 # -fopenmp), it can sometimes cause functions to be re-ordered in the
410 # output, even functions that exist in the source file. Therefore we
411 # can't insert check lines before each source function and instead have to
412 # put them at the end. So the first thing to do is dump out the source
413 # lines.
414 common.dump_input_lines(output_lines, ti, prefix_set, "//")
416 # Now generate all the checks.
417 def check_generator(my_output_lines, prefixes, func):
418 if "-emit-llvm" in clang_args:
419 return common.add_ir_checks(
420 my_output_lines,
421 "//",
422 prefixes,
423 func_dict,
424 func,
425 False,
426 ti.args.function_signature,
427 ti.args.version,
428 global_vars_seen_dict,
429 is_filtered=builder.is_filtered(),
431 else:
432 return asm.add_checks(
433 my_output_lines,
434 "//",
435 prefixes,
436 func_dict,
437 func,
438 global_vars_seen_dict,
439 is_filtered=builder.is_filtered(),
442 if ti.args.check_globals != 'none':
443 generated_prefixes.extend(
444 common.add_global_checks(
445 builder.global_var_dict(),
446 "//",
447 run_list,
448 output_lines,
449 global_vars_seen_dict,
450 False,
451 True,
452 ti.args.check_globals,
455 generated_prefixes.extend(
456 common.add_checks_at_end(
457 output_lines,
458 filecheck_run_list,
459 builder.func_order(),
460 "//",
461 lambda my_output_lines, prefixes, func: check_generator(
462 my_output_lines, prefixes, func
466 else:
467 # Normal mode. Put checks before each source function.
468 for line_info in ti.iterlines(output_lines):
469 idx = line_info.line_number
470 line = line_info.line
471 args = line_info.args
472 include_line = True
473 m = common.CHECK_RE.match(line)
474 if m and m.group(1) in prefix_set:
475 continue # Don't append the existing CHECK lines
476 # Skip special separator comments added by commmon.add_global_checks.
477 if line.strip() == "//" + common.SEPARATOR:
478 continue
479 if idx in line2func_list:
480 added = set()
481 for spell, mangled, search in line2func_list[idx]:
482 # One line may contain multiple function declarations.
483 # Skip if the mangled name has been added before.
484 # The line number may come from an included file, we simply require
485 # the search string (normally the function's spelling name, but is
486 # the class's spelling name for class specializations) to appear on
487 # the line to exclude functions from other files.
488 if mangled in added or search not in line:
489 continue
490 if args.functions is None or any(
491 re.search(regex, spell) for regex in args.functions
493 last_line = output_lines[-1].strip()
494 while last_line == "//":
495 # Remove the comment line since we will generate a new comment
496 # line as part of common.add_ir_checks()
497 output_lines.pop()
498 last_line = output_lines[-1].strip()
499 if (
500 ti.args.check_globals != 'none'
501 and not has_checked_pre_function_globals
503 generated_prefixes.extend(
504 common.add_global_checks(
505 builder.global_var_dict(),
506 "//",
507 run_list,
508 output_lines,
509 global_vars_seen_dict,
510 False,
511 True,
512 ti.args.check_globals,
515 has_checked_pre_function_globals = True
516 if added:
517 output_lines.append("//")
518 added.add(mangled)
519 generated_prefixes.extend(
520 common.add_ir_checks(
521 output_lines,
522 "//",
523 filecheck_run_list,
524 func_dict,
525 mangled,
526 False,
527 args.function_signature,
528 args.version,
529 global_vars_seen_dict,
530 is_filtered=builder.is_filtered(),
533 if line.rstrip("\n") == "//":
534 include_line = False
536 if include_line:
537 output_lines.append(line.rstrip("\n"))
539 if ti.args.check_globals != 'none':
540 generated_prefixes.extend(
541 common.add_global_checks(
542 builder.global_var_dict(),
543 "//",
544 run_list,
545 output_lines,
546 global_vars_seen_dict,
547 False,
548 False,
549 ti.args.check_globals,
552 if ti.args.gen_unused_prefix_body:
553 output_lines.extend(
554 ti.get_checks_for_unused_prefixes(run_list, generated_prefixes)
556 common.debug("Writing %d lines to %s..." % (len(output_lines), ti.path))
557 with open(ti.path, "wb") as f:
558 f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines])
560 return 0
563 if __name__ == "__main__":
564 sys.exit(main())