[ORC] Add std::tuple support to SimplePackedSerialization.
[llvm-project.git] / llvm / utils / update_cc_test_checks.py
blob15318e19fd66dcb728df3bd613a9339e10b0071d
1 #!/usr/bin/env python
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 distutils.spawn
20 import json
21 import os
22 import re
23 import shlex
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++'],
36 def get_line2func_list(args, clang_args):
37 ret = collections.defaultdict(list)
38 # Use clang's JSON AST dump to get the mangled name
39 json_dump_args = [args.clang] + clang_args + ['-fsyntax-only', '-o', '-']
40 if '-cc1' not in json_dump_args:
41 # For tests that invoke %clang instead if %clang_cc1 we have to use
42 # -Xclang -ast-dump=json instead:
43 json_dump_args.append('-Xclang')
44 json_dump_args.append('-ast-dump=json')
45 common.debug('Running', ' '.join(json_dump_args))
47 popen = subprocess.Popen(json_dump_args, stdout=subprocess.PIPE,
48 stderr=subprocess.PIPE, universal_newlines=True)
49 stdout, stderr = popen.communicate()
50 if popen.returncode != 0:
51 sys.stderr.write('Failed to run ' + ' '.join(json_dump_args) + '\n')
52 sys.stderr.write(stderr)
53 sys.stderr.write(stdout)
54 sys.exit(2)
56 # Parse the clang JSON and add all children of type FunctionDecl.
57 # TODO: Should we add checks for global variables being emitted?
58 def parse_clang_ast_json(node, loc, search):
59 node_kind = node['kind']
60 # Recurse for the following nodes that can contain nested function decls:
61 if node_kind in ('NamespaceDecl', 'LinkageSpecDecl', 'TranslationUnitDecl',
62 'CXXRecordDecl', 'ClassTemplateSpecializationDecl'):
63 # Specializations must use the loc from the specialization, not the
64 # template, and search for the class's spelling as the specialization
65 # does not mention the method names in the source.
66 if node_kind == 'ClassTemplateSpecializationDecl':
67 inner_loc = node['loc']
68 inner_search = node['name']
69 else:
70 inner_loc = None
71 inner_search = None
72 if 'inner' in node:
73 for inner in node['inner']:
74 parse_clang_ast_json(inner, inner_loc, inner_search)
75 # Otherwise we ignore everything except functions:
76 if node_kind not in ('FunctionDecl', 'CXXMethodDecl', 'CXXConstructorDecl',
77 'CXXDestructorDecl', 'CXXConversionDecl'):
78 return
79 if loc is None:
80 loc = node['loc']
81 if node.get('isImplicit') is True and node.get('storageClass') == 'extern':
82 common.debug('Skipping builtin function:', node['name'], '@', loc)
83 return
84 common.debug('Found function:', node['kind'], node['name'], '@', loc)
85 line = loc.get('line')
86 # If there is no line it is probably a builtin function -> skip
87 if line is None:
88 common.debug('Skipping function without line number:', node['name'], '@', loc)
89 return
91 # If there is no 'inner' object, it is a function declaration and we can
92 # skip it. However, function declarations may also contain an 'inner' list,
93 # but in that case it will only contains ParmVarDecls. If we find an entry
94 # that is not a ParmVarDecl, we know that this is a function definition.
95 has_body = False
96 if 'inner' in node:
97 for i in node['inner']:
98 if i.get('kind', 'ParmVarDecl') != 'ParmVarDecl':
99 has_body = True
100 break
101 if not has_body:
102 common.debug('Skipping function without body:', node['name'], '@', loc)
103 return
104 spell = node['name']
105 if search is None:
106 search = spell
107 mangled = node.get('mangledName', spell)
108 ret[int(line)-1].append((spell, mangled, search))
110 ast = json.loads(stdout)
111 if ast['kind'] != 'TranslationUnitDecl':
112 common.error('Clang AST dump JSON format changed?')
113 sys.exit(2)
114 parse_clang_ast_json(ast, None, None)
116 for line, funcs in sorted(ret.items()):
117 for func in funcs:
118 common.debug('line {}: found function {}'.format(line+1, func), file=sys.stderr)
119 if not ret:
120 common.warn('Did not find any functions using', ' '.join(json_dump_args))
121 return ret
124 def str_to_commandline(value):
125 if not value:
126 return []
127 return shlex.split(value)
130 def infer_dependent_args(args):
131 if not args.clang:
132 if not args.llvm_bin:
133 args.clang = 'clang'
134 else:
135 args.clang = os.path.join(args.llvm_bin, 'clang')
136 if not args.opt:
137 if not args.llvm_bin:
138 args.opt = 'opt'
139 else:
140 args.opt = os.path.join(args.llvm_bin, 'opt')
143 def config():
144 parser = argparse.ArgumentParser(
145 description=__doc__,
146 formatter_class=argparse.RawTextHelpFormatter)
147 parser.add_argument('--llvm-bin', help='llvm $prefix/bin path')
148 parser.add_argument('--clang',
149 help='"clang" executable, defaults to $llvm_bin/clang')
150 parser.add_argument('--clang-args', default=[], type=str_to_commandline,
151 help='Space-separated extra args to clang, e.g. --clang-args=-v')
152 parser.add_argument('--opt',
153 help='"opt" executable, defaults to $llvm_bin/opt')
154 parser.add_argument(
155 '--functions', nargs='+', help='A list of function name regexes. '
156 'If specified, update CHECK lines for functions matching at least one regex')
157 parser.add_argument(
158 '--x86_extra_scrub', action='store_true',
159 help='Use more regex for x86 matching to reduce diffs between various subtargets')
160 parser.add_argument('--function-signature', action='store_true',
161 help='Keep function signature information around for the check line')
162 parser.add_argument('--check-attributes', action='store_true',
163 help='Check "Function Attributes" for functions')
164 parser.add_argument('--check-globals', action='store_true',
165 help='Check global entries (global variables, metadata, attribute sets, ...) for functions')
166 parser.add_argument('tests', nargs='+')
167 args = common.parse_commandline_args(parser)
168 infer_dependent_args(args)
170 if not distutils.spawn.find_executable(args.clang):
171 print('Please specify --llvm-bin or --clang', file=sys.stderr)
172 sys.exit(1)
174 # Determine the builtin includes directory so that we can update tests that
175 # depend on the builtin headers. See get_clang_builtin_include_dir() and
176 # use_clang() in llvm/utils/lit/lit/llvm/config.py.
177 try:
178 builtin_include_dir = subprocess.check_output(
179 [args.clang, '-print-file-name=include']).decode().strip()
180 SUBST['%clang_cc1'] = ['-cc1', '-internal-isystem', builtin_include_dir,
181 '-nostdsysteminc']
182 except subprocess.CalledProcessError:
183 common.warn('Could not determine clang builtins directory, some tests '
184 'might not update correctly.')
186 if not distutils.spawn.find_executable(args.opt):
187 # Many uses of this tool will not need an opt binary, because it's only
188 # needed for updating a test that runs clang | opt | FileCheck. So we
189 # defer this error message until we find that opt is actually needed.
190 args.opt = None
192 return args, parser
195 def get_function_body(builder, args, filename, clang_args, extra_commands,
196 prefixes):
197 # TODO Clean up duplication of asm/common build_function_body_dictionary
198 # Invoke external tool and extract function bodies.
199 raw_tool_output = common.invoke_tool(args.clang, clang_args, filename)
200 for extra_command in extra_commands:
201 extra_args = shlex.split(extra_command)
202 with tempfile.NamedTemporaryFile() as f:
203 f.write(raw_tool_output.encode())
204 f.flush()
205 if extra_args[0] == 'opt':
206 if args.opt is None:
207 print(filename, 'needs to run opt. '
208 'Please specify --llvm-bin or --opt', file=sys.stderr)
209 sys.exit(1)
210 extra_args[0] = args.opt
211 raw_tool_output = common.invoke_tool(extra_args[0],
212 extra_args[1:], f.name)
213 if '-emit-llvm' in clang_args:
214 builder.process_run_line(
215 common.OPT_FUNCTION_RE, common.scrub_body, raw_tool_output,
216 prefixes, False)
217 else:
218 print('The clang command line should include -emit-llvm as asm tests '
219 'are discouraged in Clang testsuite.', file=sys.stderr)
220 sys.exit(1)
222 def exec_run_line(exe):
223 popen = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
224 stdout, stderr = popen.communicate()
225 if popen.returncode != 0:
226 sys.stderr.write('Failed to run ' + ' '.join(exe) + '\n')
227 sys.stderr.write(stderr)
228 sys.stderr.write(stdout)
229 sys.exit(3)
231 def main():
232 initial_args, parser = config()
233 script_name = os.path.basename(__file__)
235 for ti in common.itertests(initial_args.tests, parser, 'utils/' + script_name,
236 comment_prefix='//', argparse_callback=infer_dependent_args):
237 # Build a list of filechecked and non-filechecked RUN lines.
238 run_list = []
239 line2func_list = collections.defaultdict(list)
241 subs = {
242 '%s' : ti.path,
243 '%t' : tempfile.NamedTemporaryFile().name,
244 '%S' : os.getcwd(),
247 for l in ti.run_lines:
248 commands = [cmd.strip() for cmd in l.split('|')]
250 triple_in_cmd = None
251 m = common.TRIPLE_ARG_RE.search(commands[0])
252 if m:
253 triple_in_cmd = m.groups()[0]
255 # Parse executable args.
256 exec_args = shlex.split(commands[0])
257 # Execute non-clang runline.
258 if exec_args[0] not in SUBST:
259 # Do lit-like substitutions.
260 for s in subs:
261 exec_args = [i.replace(s, subs[s]) if s in i else i for i in exec_args]
262 run_list.append((None, exec_args, None, None))
263 continue
264 # This is a clang runline, apply %clang substitution rule, do lit-like substitutions,
265 # and append args.clang_args
266 clang_args = exec_args
267 clang_args[0:1] = SUBST[clang_args[0]]
268 for s in subs:
269 clang_args = [i.replace(s, subs[s]) if s in i else i for i in clang_args]
270 clang_args += ti.args.clang_args
272 # Extract -check-prefix in FileCheck args
273 filecheck_cmd = commands[-1]
274 common.verify_filecheck_prefixes(filecheck_cmd)
275 if not filecheck_cmd.startswith('FileCheck '):
276 # Execute non-filechecked clang runline.
277 exe = [ti.args.clang] + clang_args
278 run_list.append((None, exe, None, None))
279 continue
281 check_prefixes = [item for m in common.CHECK_PREFIX_RE.finditer(filecheck_cmd)
282 for item in m.group(1).split(',')]
283 if not check_prefixes:
284 check_prefixes = ['CHECK']
285 run_list.append((check_prefixes, clang_args, commands[1:-1], triple_in_cmd))
287 # Execute clang, generate LLVM IR, and extract functions.
289 # Store only filechecked runlines.
290 filecheck_run_list = [i for i in run_list if i[0]]
291 builder = common.FunctionTestBuilder(
292 run_list=filecheck_run_list,
293 flags=ti.args,
294 scrubber_args=[],
295 path=ti.path)
297 for prefixes, args, extra_commands, triple_in_cmd in run_list:
298 # Execute non-filechecked runline.
299 if not prefixes:
300 print('NOTE: Executing non-FileChecked RUN line: ' + ' '.join(args), file=sys.stderr)
301 exec_run_line(args)
302 continue
304 clang_args = args
305 common.debug('Extracted clang cmd: clang {}'.format(clang_args))
306 common.debug('Extracted FileCheck prefixes: {}'.format(prefixes))
308 get_function_body(builder, ti.args, ti.path, clang_args, extra_commands,
309 prefixes)
311 # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
312 # mangled names. Forward all clang args for now.
313 for k, v in get_line2func_list(ti.args, clang_args).items():
314 line2func_list[k].extend(v)
316 func_dict = builder.finish_and_get_func_dict()
317 global_vars_seen_dict = {}
318 prefix_set = set([prefix for p in filecheck_run_list for prefix in p[0]])
319 output_lines = []
320 has_checked_pre_function_globals = False
322 include_generated_funcs = common.find_arg_in_test(ti,
323 lambda args: ti.args.include_generated_funcs,
324 '--include-generated-funcs',
325 True)
327 if include_generated_funcs:
328 # Generate the appropriate checks for each function. We need to emit
329 # these in the order according to the generated output so that CHECK-LABEL
330 # works properly. func_order provides that.
332 # It turns out that when clang generates functions (for example, with
333 # -fopenmp), it can sometimes cause functions to be re-ordered in the
334 # output, even functions that exist in the source file. Therefore we
335 # can't insert check lines before each source function and instead have to
336 # put them at the end. So the first thing to do is dump out the source
337 # lines.
338 common.dump_input_lines(output_lines, ti, prefix_set, '//')
340 # Now generate all the checks.
341 def check_generator(my_output_lines, prefixes, func):
342 if '-emit-llvm' in clang_args:
343 common.add_ir_checks(my_output_lines, '//',
344 prefixes,
345 func_dict, func, False,
346 ti.args.function_signature,
347 global_vars_seen_dict)
348 else:
349 asm.add_asm_checks(my_output_lines, '//',
350 prefixes,
351 func_dict, func)
353 if ti.args.check_globals:
354 common.add_global_checks(builder.global_var_dict(), '//', run_list,
355 output_lines, global_vars_seen_dict, True,
356 True)
357 common.add_checks_at_end(output_lines, filecheck_run_list, builder.func_order(),
358 '//', lambda my_output_lines, prefixes, func:
359 check_generator(my_output_lines,
360 prefixes, func))
361 else:
362 # Normal mode. Put checks before each source function.
363 for line_info in ti.iterlines(output_lines):
364 idx = line_info.line_number
365 line = line_info.line
366 args = line_info.args
367 include_line = True
368 m = common.CHECK_RE.match(line)
369 if m and m.group(1) in prefix_set:
370 continue # Don't append the existing CHECK lines
371 # Skip special separator comments added by commmon.add_global_checks.
372 if line.strip() == '//' + common.SEPARATOR:
373 continue
374 if idx in line2func_list:
375 added = set()
376 for spell, mangled, search in line2func_list[idx]:
377 # One line may contain multiple function declarations.
378 # Skip if the mangled name has been added before.
379 # The line number may come from an included file, we simply require
380 # the search string (normally the function's spelling name, but is
381 # the class's spelling name for class specializations) to appear on
382 # the line to exclude functions from other files.
383 if mangled in added or search not in line:
384 continue
385 if args.functions is None or any(re.search(regex, spell) for regex in args.functions):
386 last_line = output_lines[-1].strip()
387 while last_line == '//':
388 # Remove the comment line since we will generate a new comment
389 # line as part of common.add_ir_checks()
390 output_lines.pop()
391 last_line = output_lines[-1].strip()
392 if ti.args.check_globals and not has_checked_pre_function_globals:
393 common.add_global_checks(builder.global_var_dict(), '//',
394 run_list, output_lines,
395 global_vars_seen_dict, True, True)
396 has_checked_pre_function_globals = True
397 if added:
398 output_lines.append('//')
399 added.add(mangled)
400 common.add_ir_checks(output_lines, '//', filecheck_run_list, func_dict, mangled,
401 False, args.function_signature, global_vars_seen_dict)
402 if line.rstrip('\n') == '//':
403 include_line = False
405 if include_line:
406 output_lines.append(line.rstrip('\n'))
408 if ti.args.check_globals:
409 common.add_global_checks(builder.global_var_dict(), '//', run_list,
410 output_lines, global_vars_seen_dict, True, False)
411 common.debug('Writing %d lines to %s...' % (len(output_lines), ti.path))
412 with open(ti.path, 'wb') as f:
413 f.writelines(['{}\n'.format(l).encode('utf-8') for l in output_lines])
415 return 0
418 if __name__ == '__main__':
419 sys.exit(main())