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++'],
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
)
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']
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'):
81 if node
.get('isImplicit') is True and node
.get('storageClass') == 'extern':
82 common
.debug('Skipping builtin function:', node
['name'], '@', loc
)
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
88 common
.debug('Skipping function without line number:', node
['name'], '@', loc
)
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.
97 for i
in node
['inner']:
98 if i
.get('kind', 'ParmVarDecl') != 'ParmVarDecl':
102 common
.debug('Skipping function without body:', node
['name'], '@', loc
)
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?')
114 parse_clang_ast_json(ast
, None, None)
116 for line
, funcs
in sorted(ret
.items()):
118 common
.debug('line {}: found function {}'.format(line
+1, func
), file=sys
.stderr
)
120 common
.warn('Did not find any functions using', ' '.join(json_dump_args
))
124 def str_to_commandline(value
):
127 return shlex
.split(value
)
130 def infer_dependent_args(args
):
132 if not args
.llvm_bin
:
135 args
.clang
= os
.path
.join(args
.llvm_bin
, 'clang')
137 if not args
.llvm_bin
:
140 args
.opt
= os
.path
.join(args
.llvm_bin
, 'opt')
143 def find_executable(executable
):
144 _
, ext
= os
.path
.splitext(executable
)
145 if sys
.platform
== 'win32' and ext
!= '.exe':
146 executable
= executable
+ '.exe'
148 return shutil
.which(executable
)
152 parser
= argparse
.ArgumentParser(
154 formatter_class
=argparse
.RawTextHelpFormatter
)
155 parser
.add_argument('--llvm-bin', help='llvm $prefix/bin path')
156 parser
.add_argument('--clang',
157 help='"clang" executable, defaults to $llvm_bin/clang')
158 parser
.add_argument('--clang-args', default
=[], type=str_to_commandline
,
159 help='Space-separated extra args to clang, e.g. --clang-args=-v')
160 parser
.add_argument('--opt',
161 help='"opt" executable, defaults to $llvm_bin/opt')
163 '--functions', nargs
='+', help='A list of function name regexes. '
164 'If specified, update CHECK lines for functions matching at least one regex')
166 '--x86_extra_scrub', action
='store_true',
167 help='Use more regex for x86 matching to reduce diffs between various subtargets')
168 parser
.add_argument('--function-signature', action
='store_true',
169 help='Keep function signature information around for the check line')
170 parser
.add_argument('--check-attributes', action
='store_true',
171 help='Check "Function Attributes" for functions')
172 parser
.add_argument('--check-globals', action
='store_true',
173 help='Check global entries (global variables, metadata, attribute sets, ...) for functions')
174 parser
.add_argument('tests', nargs
='+')
175 args
= common
.parse_commandline_args(parser
)
176 infer_dependent_args(args
)
178 if not find_executable(args
.clang
):
179 print('Please specify --llvm-bin or --clang', file=sys
.stderr
)
182 # Determine the builtin includes directory so that we can update tests that
183 # depend on the builtin headers. See get_clang_builtin_include_dir() and
184 # use_clang() in llvm/utils/lit/lit/llvm/config.py.
186 builtin_include_dir
= subprocess
.check_output(
187 [args
.clang
, '-print-file-name=include']).decode().strip()
188 SUBST
['%clang_cc1'] = ['-cc1', '-internal-isystem', builtin_include_dir
,
190 except subprocess
.CalledProcessError
:
191 common
.warn('Could not determine clang builtins directory, some tests '
192 'might not update correctly.')
194 if not find_executable(args
.opt
):
195 # Many uses of this tool will not need an opt binary, because it's only
196 # needed for updating a test that runs clang | opt | FileCheck. So we
197 # defer this error message until we find that opt is actually needed.
203 def get_function_body(builder
, args
, filename
, clang_args
, extra_commands
,
205 # TODO Clean up duplication of asm/common build_function_body_dictionary
206 # Invoke external tool and extract function bodies.
207 raw_tool_output
= common
.invoke_tool(args
.clang
, clang_args
, filename
)
208 for extra_command
in extra_commands
:
209 extra_args
= shlex
.split(extra_command
)
210 with tempfile
.NamedTemporaryFile() as f
:
211 f
.write(raw_tool_output
.encode())
213 if extra_args
[0] == 'opt':
215 print(filename
, 'needs to run opt. '
216 'Please specify --llvm-bin or --opt', file=sys
.stderr
)
218 extra_args
[0] = args
.opt
219 raw_tool_output
= common
.invoke_tool(extra_args
[0],
220 extra_args
[1:], f
.name
)
221 if '-emit-llvm' in clang_args
:
222 builder
.process_run_line(
223 common
.OPT_FUNCTION_RE
, common
.scrub_body
, raw_tool_output
,
225 builder
.processed_prefixes(prefixes
)
227 print('The clang command line should include -emit-llvm as asm tests '
228 'are discouraged in Clang testsuite.', file=sys
.stderr
)
231 def exec_run_line(exe
):
232 popen
= subprocess
.Popen(exe
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, universal_newlines
=True)
233 stdout
, stderr
= popen
.communicate()
234 if popen
.returncode
!= 0:
235 sys
.stderr
.write('Failed to run ' + ' '.join(exe
) + '\n')
236 sys
.stderr
.write(stderr
)
237 sys
.stderr
.write(stdout
)
241 initial_args
, parser
= config()
242 script_name
= os
.path
.basename(__file__
)
244 for ti
in common
.itertests(initial_args
.tests
, parser
, 'utils/' + script_name
,
245 comment_prefix
='//', argparse_callback
=infer_dependent_args
):
246 # Build a list of filechecked and non-filechecked RUN lines.
248 line2func_list
= collections
.defaultdict(list)
252 '%t' : tempfile
.NamedTemporaryFile().name
,
253 '%S' : os
.path
.dirname(ti
.path
),
256 for l
in ti
.run_lines
:
257 commands
= [cmd
.strip() for cmd
in l
.split('|')]
260 m
= common
.TRIPLE_ARG_RE
.search(commands
[0])
262 triple_in_cmd
= m
.groups()[0]
264 # Parse executable args.
265 exec_args
= shlex
.split(commands
[0])
266 # Execute non-clang runline.
267 if exec_args
[0] not in SUBST
:
268 # Do lit-like substitutions.
270 exec_args
= [i
.replace(s
, subs
[s
]) if s
in i
else i
for i
in exec_args
]
271 run_list
.append((None, exec_args
, None, None))
273 # This is a clang runline, apply %clang substitution rule, do lit-like substitutions,
274 # and append args.clang_args
275 clang_args
= exec_args
276 clang_args
[0:1] = SUBST
[clang_args
[0]]
278 clang_args
= [i
.replace(s
, subs
[s
]) if s
in i
else i
for i
in clang_args
]
279 clang_args
+= ti
.args
.clang_args
281 # Extract -check-prefix in FileCheck args
282 filecheck_cmd
= commands
[-1]
283 common
.verify_filecheck_prefixes(filecheck_cmd
)
284 if not filecheck_cmd
.startswith('FileCheck '):
285 # Execute non-filechecked clang runline.
286 exe
= [ti
.args
.clang
] + clang_args
287 run_list
.append((None, exe
, None, None))
290 check_prefixes
= common
.get_check_prefixes(filecheck_cmd
)
291 run_list
.append((check_prefixes
, clang_args
, commands
[1:-1], triple_in_cmd
))
293 # Execute clang, generate LLVM IR, and extract functions.
295 # Store only filechecked runlines.
296 filecheck_run_list
= [i
for i
in run_list
if i
[0]]
297 builder
= common
.FunctionTestBuilder(
298 run_list
=filecheck_run_list
,
303 for prefixes
, args
, extra_commands
, triple_in_cmd
in run_list
:
304 # Execute non-filechecked runline.
306 print('NOTE: Executing non-FileChecked RUN line: ' + ' '.join(args
), file=sys
.stderr
)
311 common
.debug('Extracted clang cmd: clang {}'.format(clang_args
))
312 common
.debug('Extracted FileCheck prefixes: {}'.format(prefixes
))
314 get_function_body(builder
, ti
.args
, ti
.path
, clang_args
, extra_commands
,
317 # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
318 # mangled names. Forward all clang args for now.
319 for k
, v
in get_line2func_list(ti
.args
, clang_args
).items():
320 line2func_list
[k
].extend(v
)
322 func_dict
= builder
.finish_and_get_func_dict()
323 global_vars_seen_dict
= {}
324 prefix_set
= set([prefix
for p
in filecheck_run_list
for prefix
in p
[0]])
326 has_checked_pre_function_globals
= False
328 include_generated_funcs
= common
.find_arg_in_test(ti
,
329 lambda args
: ti
.args
.include_generated_funcs
,
330 '--include-generated-funcs',
332 generated_prefixes
= []
333 if include_generated_funcs
:
334 # Generate the appropriate checks for each function. We need to emit
335 # these in the order according to the generated output so that CHECK-LABEL
336 # works properly. func_order provides that.
338 # It turns out that when clang generates functions (for example, with
339 # -fopenmp), it can sometimes cause functions to be re-ordered in the
340 # output, even functions that exist in the source file. Therefore we
341 # can't insert check lines before each source function and instead have to
342 # put them at the end. So the first thing to do is dump out the source
344 common
.dump_input_lines(output_lines
, ti
, prefix_set
, '//')
346 # Now generate all the checks.
347 def check_generator(my_output_lines
, prefixes
, func
):
348 if '-emit-llvm' in clang_args
:
349 return common
.add_ir_checks(my_output_lines
, '//',
351 func_dict
, func
, False,
352 ti
.args
.function_signature
,
354 global_vars_seen_dict
,
355 is_filtered
=builder
.is_filtered())
357 return asm
.add_checks(my_output_lines
, '//',
359 func_dict
, func
, global_vars_seen_dict
,
360 is_filtered
=builder
.is_filtered())
362 if ti
.args
.check_globals
:
363 generated_prefixes
.extend(
364 common
.add_global_checks(builder
.global_var_dict(), '//', run_list
,
365 output_lines
, global_vars_seen_dict
, True,
367 generated_prefixes
.extend(
368 common
.add_checks_at_end(
369 output_lines
, filecheck_run_list
, builder
.func_order(), '//',
370 lambda my_output_lines
, prefixes
, func
: check_generator(
371 my_output_lines
, prefixes
, func
)))
373 # Normal mode. Put checks before each source function.
374 for line_info
in ti
.iterlines(output_lines
):
375 idx
= line_info
.line_number
376 line
= line_info
.line
377 args
= line_info
.args
379 m
= common
.CHECK_RE
.match(line
)
380 if m
and m
.group(1) in prefix_set
:
381 continue # Don't append the existing CHECK lines
382 # Skip special separator comments added by commmon.add_global_checks.
383 if line
.strip() == '//' + common
.SEPARATOR
:
385 if idx
in line2func_list
:
387 for spell
, mangled
, search
in line2func_list
[idx
]:
388 # One line may contain multiple function declarations.
389 # Skip if the mangled name has been added before.
390 # The line number may come from an included file, we simply require
391 # the search string (normally the function's spelling name, but is
392 # the class's spelling name for class specializations) to appear on
393 # the line to exclude functions from other files.
394 if mangled
in added
or search
not in line
:
396 if args
.functions
is None or any(re
.search(regex
, spell
) for regex
in args
.functions
):
397 last_line
= output_lines
[-1].strip()
398 while last_line
== '//':
399 # Remove the comment line since we will generate a new comment
400 # line as part of common.add_ir_checks()
402 last_line
= output_lines
[-1].strip()
403 if ti
.args
.check_globals
and not has_checked_pre_function_globals
:
404 generated_prefixes
.extend(
405 common
.add_global_checks(builder
.global_var_dict(), '//',
406 run_list
, output_lines
,
407 global_vars_seen_dict
, True, True))
408 has_checked_pre_function_globals
= True
410 output_lines
.append('//')
412 generated_prefixes
.extend(
413 common
.add_ir_checks(
420 args
.function_signature
,
422 global_vars_seen_dict
,
423 is_filtered
=builder
.is_filtered()))
424 if line
.rstrip('\n') == '//':
428 output_lines
.append(line
.rstrip('\n'))
430 if ti
.args
.check_globals
:
431 generated_prefixes
.extend(
432 common
.add_global_checks(builder
.global_var_dict(), '//', run_list
,
433 output_lines
, global_vars_seen_dict
, True,
435 if ti
.args
.gen_unused_prefix_body
:
437 ti
.get_checks_for_unused_prefixes(run_list
, generated_prefixes
))
438 common
.debug('Writing %d lines to %s...' % (len(output_lines
), ti
.path
))
439 with
open(ti
.path
, 'wb') as f
:
440 f
.writelines(['{}\n'.format(l
).encode('utf-8') for l
in output_lines
])
445 if __name__
== '__main__':