[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / llvm / utils / update_cc_test_checks.py
blobb9e91f19461b0f3e1aefbc47b5381984039f3019
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 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 builder.processed_prefixes(prefixes)
218 else:
219 print('The clang command line should include -emit-llvm as asm tests '
220 'are discouraged in Clang testsuite.', file=sys.stderr)
221 sys.exit(1)
223 def exec_run_line(exe):
224 popen = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
225 stdout, stderr = popen.communicate()
226 if popen.returncode != 0:
227 sys.stderr.write('Failed to run ' + ' '.join(exe) + '\n')
228 sys.stderr.write(stderr)
229 sys.stderr.write(stdout)
230 sys.exit(3)
232 def main():
233 initial_args, parser = config()
234 script_name = os.path.basename(__file__)
236 for ti in common.itertests(initial_args.tests, parser, 'utils/' + script_name,
237 comment_prefix='//', argparse_callback=infer_dependent_args):
238 # Build a list of filechecked and non-filechecked RUN lines.
239 run_list = []
240 line2func_list = collections.defaultdict(list)
242 subs = {
243 '%s' : ti.path,
244 '%t' : tempfile.NamedTemporaryFile().name,
245 '%S' : os.path.dirname(ti.path),
248 for l in ti.run_lines:
249 commands = [cmd.strip() for cmd in l.split('|')]
251 triple_in_cmd = None
252 m = common.TRIPLE_ARG_RE.search(commands[0])
253 if m:
254 triple_in_cmd = m.groups()[0]
256 # Parse executable args.
257 exec_args = shlex.split(commands[0])
258 # Execute non-clang runline.
259 if exec_args[0] not in SUBST:
260 # Do lit-like substitutions.
261 for s in subs:
262 exec_args = [i.replace(s, subs[s]) if s in i else i for i in exec_args]
263 run_list.append((None, exec_args, None, None))
264 continue
265 # This is a clang runline, apply %clang substitution rule, do lit-like substitutions,
266 # and append args.clang_args
267 clang_args = exec_args
268 clang_args[0:1] = SUBST[clang_args[0]]
269 for s in subs:
270 clang_args = [i.replace(s, subs[s]) if s in i else i for i in clang_args]
271 clang_args += ti.args.clang_args
273 # Extract -check-prefix in FileCheck args
274 filecheck_cmd = commands[-1]
275 common.verify_filecheck_prefixes(filecheck_cmd)
276 if not filecheck_cmd.startswith('FileCheck '):
277 # Execute non-filechecked clang runline.
278 exe = [ti.args.clang] + clang_args
279 run_list.append((None, exe, None, None))
280 continue
282 check_prefixes = [item for m in common.CHECK_PREFIX_RE.finditer(filecheck_cmd)
283 for item in m.group(1).split(',')]
284 if not check_prefixes:
285 check_prefixes = ['CHECK']
286 run_list.append((check_prefixes, clang_args, commands[1:-1], triple_in_cmd))
288 # Execute clang, generate LLVM IR, and extract functions.
290 # Store only filechecked runlines.
291 filecheck_run_list = [i for i in run_list if i[0]]
292 builder = common.FunctionTestBuilder(
293 run_list=filecheck_run_list,
294 flags=ti.args,
295 scrubber_args=[],
296 path=ti.path)
298 for prefixes, args, extra_commands, triple_in_cmd in run_list:
299 # Execute non-filechecked runline.
300 if not prefixes:
301 print('NOTE: Executing non-FileChecked RUN line: ' + ' '.join(args), file=sys.stderr)
302 exec_run_line(args)
303 continue
305 clang_args = args
306 common.debug('Extracted clang cmd: clang {}'.format(clang_args))
307 common.debug('Extracted FileCheck prefixes: {}'.format(prefixes))
309 get_function_body(builder, ti.args, ti.path, clang_args, extra_commands,
310 prefixes)
312 # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
313 # mangled names. Forward all clang args for now.
314 for k, v in get_line2func_list(ti.args, clang_args).items():
315 line2func_list[k].extend(v)
317 func_dict = builder.finish_and_get_func_dict()
318 global_vars_seen_dict = {}
319 prefix_set = set([prefix for p in filecheck_run_list for prefix in p[0]])
320 output_lines = []
321 has_checked_pre_function_globals = False
323 include_generated_funcs = common.find_arg_in_test(ti,
324 lambda args: ti.args.include_generated_funcs,
325 '--include-generated-funcs',
326 True)
328 if include_generated_funcs:
329 # Generate the appropriate checks for each function. We need to emit
330 # these in the order according to the generated output so that CHECK-LABEL
331 # works properly. func_order provides that.
333 # It turns out that when clang generates functions (for example, with
334 # -fopenmp), it can sometimes cause functions to be re-ordered in the
335 # output, even functions that exist in the source file. Therefore we
336 # can't insert check lines before each source function and instead have to
337 # put them at the end. So the first thing to do is dump out the source
338 # lines.
339 common.dump_input_lines(output_lines, ti, prefix_set, '//')
341 # Now generate all the checks.
342 def check_generator(my_output_lines, prefixes, func):
343 if '-emit-llvm' in clang_args:
344 return common.add_ir_checks(my_output_lines, '//',
345 prefixes,
346 func_dict, func, False,
347 ti.args.function_signature,
348 global_vars_seen_dict,
349 is_filtered=builder.is_filtered())
350 else:
351 return asm.add_checks(my_output_lines, '//',
352 prefixes,
353 func_dict, func, global_vars_seen_dict,
354 is_filtered=builder.is_filtered())
356 if ti.args.check_globals:
357 common.add_global_checks(builder.global_var_dict(), '//', run_list,
358 output_lines, global_vars_seen_dict, True,
359 True)
360 common.add_checks_at_end(output_lines, filecheck_run_list, builder.func_order(),
361 '//', lambda my_output_lines, prefixes, func:
362 check_generator(my_output_lines,
363 prefixes, func))
364 else:
365 # Normal mode. Put checks before each source function.
366 for line_info in ti.iterlines(output_lines):
367 idx = line_info.line_number
368 line = line_info.line
369 args = line_info.args
370 include_line = True
371 m = common.CHECK_RE.match(line)
372 if m and m.group(1) in prefix_set:
373 continue # Don't append the existing CHECK lines
374 # Skip special separator comments added by commmon.add_global_checks.
375 if line.strip() == '//' + common.SEPARATOR:
376 continue
377 if idx in line2func_list:
378 added = set()
379 for spell, mangled, search in line2func_list[idx]:
380 # One line may contain multiple function declarations.
381 # Skip if the mangled name has been added before.
382 # The line number may come from an included file, we simply require
383 # the search string (normally the function's spelling name, but is
384 # the class's spelling name for class specializations) to appear on
385 # the line to exclude functions from other files.
386 if mangled in added or search not in line:
387 continue
388 if args.functions is None or any(re.search(regex, spell) for regex in args.functions):
389 last_line = output_lines[-1].strip()
390 while last_line == '//':
391 # Remove the comment line since we will generate a new comment
392 # line as part of common.add_ir_checks()
393 output_lines.pop()
394 last_line = output_lines[-1].strip()
395 if ti.args.check_globals and not has_checked_pre_function_globals:
396 common.add_global_checks(builder.global_var_dict(), '//',
397 run_list, output_lines,
398 global_vars_seen_dict, True, True)
399 has_checked_pre_function_globals = True
400 if added:
401 output_lines.append('//')
402 added.add(mangled)
403 common.add_ir_checks(output_lines, '//', filecheck_run_list, func_dict, mangled,
404 False, args.function_signature, global_vars_seen_dict,
405 is_filtered=builder.is_filtered())
406 if line.rstrip('\n') == '//':
407 include_line = False
409 if include_line:
410 output_lines.append(line.rstrip('\n'))
412 if ti.args.check_globals:
413 common.add_global_checks(builder.global_var_dict(), '//', run_list,
414 output_lines, global_vars_seen_dict, True, False)
415 common.debug('Writing %d lines to %s...' % (len(output_lines), ti.path))
416 with open(ti.path, 'wb') as f:
417 f.writelines(['{}\n'.format(l).encode('utf-8') for l in output_lines])
419 return 0
422 if __name__ == '__main__':
423 sys.exit(main())