Add default constructor for MaybeAlign
[llvm-complete.git] / utils / update_cc_test_checks.py
blob205b57698dae6af0d37906f534590dbeb95baa41
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 --c-index-test=release/bin/c-index-test \
13 --clang=release/bin/clang /tmp/c/a.cc
14 '''
16 import argparse
17 import collections
18 import distutils.spawn
19 import os
20 import shlex
21 import string
22 import subprocess
23 import sys
24 import re
25 import tempfile
27 from UpdateTestChecks import asm, common
29 ADVERT = '// NOTE: Assertions have been autogenerated by '
31 CHECK_RE = re.compile(r'^\s*//\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL)?:')
32 RUN_LINE_RE = re.compile('^//\s*RUN:\s*(.*)$')
34 SUBST = {
35 '%clang': [],
36 '%clang_cc1': ['-cc1'],
37 '%clangxx': ['--driver-mode=g++'],
40 def get_line2spell_and_mangled(args, clang_args):
41 ret = {}
42 with tempfile.NamedTemporaryFile() as f:
43 # TODO Make c-index-test print mangled names without circumventing through precompiled headers
44 status = subprocess.run([args.c_index_test, '-write-pch', f.name, *clang_args],
45 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
46 if status.returncode:
47 sys.stderr.write(status.stdout.decode())
48 sys.exit(2)
49 output = subprocess.check_output([args.c_index_test,
50 '-test-print-mangle', f.name])
51 if sys.version_info[0] > 2:
52 output = output.decode()
54 RE = re.compile(r'^FunctionDecl=(\w+):(\d+):\d+ \(Definition\) \[mangled=([^]]+)\]')
55 for line in output.splitlines():
56 m = RE.match(line)
57 if not m: continue
58 spell, line, mangled = m.groups()
59 if mangled == '_' + spell:
60 # HACK for MacOS (where the mangled name includes an _ for C but the IR won't):
61 mangled = spell
62 # Note -test-print-mangle does not print file names so if #include is used,
63 # the line number may come from an included file.
64 ret[int(line)-1] = (spell, mangled)
65 if args.verbose:
66 for line, func_name in sorted(ret.items()):
67 print('line {}: found function {}'.format(line+1, func_name), file=sys.stderr)
68 return ret
71 def config():
72 parser = argparse.ArgumentParser(
73 description=__doc__,
74 formatter_class=argparse.RawTextHelpFormatter)
75 parser.add_argument('-v', '--verbose', action='store_true')
76 parser.add_argument('--llvm-bin', help='llvm $prefix/bin path')
77 parser.add_argument('--clang',
78 help='"clang" executable, defaults to $llvm_bin/clang')
79 parser.add_argument('--clang-args',
80 help='Space-separated extra args to clang, e.g. --clang-args=-v')
81 parser.add_argument('--c-index-test',
82 help='"c-index-test" executable, defaults to $llvm_bin/c-index-test')
83 parser.add_argument(
84 '--functions', nargs='+', help='A list of function name regexes. '
85 'If specified, update CHECK lines for functions matching at least one regex')
86 parser.add_argument(
87 '--x86_extra_scrub', action='store_true',
88 help='Use more regex for x86 matching to reduce diffs between various subtargets')
89 parser.add_argument('tests', nargs='+')
90 args = parser.parse_args()
91 args.clang_args = shlex.split(args.clang_args or '')
93 if args.clang is None:
94 if args.llvm_bin is None:
95 args.clang = 'clang'
96 else:
97 args.clang = os.path.join(args.llvm_bin, 'clang')
98 if not distutils.spawn.find_executable(args.clang):
99 print('Please specify --llvm-bin or --clang', file=sys.stderr)
100 sys.exit(1)
101 if args.c_index_test is None:
102 if args.llvm_bin is None:
103 args.c_index_test = 'c-index-test'
104 else:
105 args.c_index_test = os.path.join(args.llvm_bin, 'c-index-test')
106 if not distutils.spawn.find_executable(args.c_index_test):
107 print('Please specify --llvm-bin or --c-index-test', file=sys.stderr)
108 sys.exit(1)
110 return args
113 def get_function_body(args, filename, clang_args, prefixes, triple_in_cmd, func_dict):
114 # TODO Clean up duplication of asm/common build_function_body_dictionary
115 # Invoke external tool and extract function bodies.
116 raw_tool_output = common.invoke_tool(args.clang, clang_args, filename)
117 if '-emit-llvm' in clang_args:
118 common.build_function_body_dictionary(
119 common.OPT_FUNCTION_RE, common.scrub_body, [],
120 raw_tool_output, prefixes, func_dict, args.verbose)
121 else:
122 print('The clang command line should include -emit-llvm as asm tests '
123 'are discouraged in Clang testsuite.', file=sys.stderr)
124 sys.exit(1)
127 def main():
128 args = config()
129 autogenerated_note = (ADVERT + 'utils/' + os.path.basename(__file__))
131 for filename in args.tests:
132 with open(filename) as f:
133 input_lines = [l.rstrip() for l in f]
135 # Extract RUN lines.
136 raw_lines = [m.group(1)
137 for m in [RUN_LINE_RE.match(l) for l in input_lines] if m]
138 run_lines = [raw_lines[0]] if len(raw_lines) > 0 else []
139 for l in raw_lines[1:]:
140 if run_lines[-1].endswith("\\"):
141 run_lines[-1] = run_lines[-1].rstrip("\\") + " " + l
142 else:
143 run_lines.append(l)
145 if args.verbose:
146 print('Found {} RUN lines:'.format(len(run_lines)), file=sys.stderr)
147 for l in run_lines:
148 print(' RUN: ' + l, file=sys.stderr)
150 # Build a list of clang command lines and check prefixes from RUN lines.
151 run_list = []
152 line2spell_and_mangled_list = collections.defaultdict(list)
153 for l in run_lines:
154 commands = [cmd.strip() for cmd in l.split('|', 1)]
156 triple_in_cmd = None
157 m = common.TRIPLE_ARG_RE.search(commands[0])
158 if m:
159 triple_in_cmd = m.groups()[0]
161 # Apply %clang substitution rule, replace %s by `filename`, and append args.clang_args
162 clang_args = shlex.split(commands[0])
163 if clang_args[0] not in SUBST:
164 print('WARNING: Skipping non-clang RUN line: ' + l, file=sys.stderr)
165 continue
166 clang_args[0:1] = SUBST[clang_args[0]]
167 clang_args = [filename if i == '%s' else i for i in clang_args] + args.clang_args
169 # Extract -check-prefix in FileCheck args
170 filecheck_cmd = commands[-1]
171 common.verify_filecheck_prefixes(filecheck_cmd)
172 if not filecheck_cmd.startswith('FileCheck '):
173 print('WARNING: Skipping non-FileChecked RUN line: ' + l, file=sys.stderr)
174 continue
175 check_prefixes = [item for m in common.CHECK_PREFIX_RE.finditer(filecheck_cmd)
176 for item in m.group(1).split(',')]
177 if not check_prefixes:
178 check_prefixes = ['CHECK']
179 run_list.append((check_prefixes, clang_args, triple_in_cmd))
181 # Strip CHECK lines which are in `prefix_set`, update test file.
182 prefix_set = set([prefix for p in run_list for prefix in p[0]])
183 input_lines = []
184 with open(filename, 'r+') as f:
185 for line in f:
186 m = CHECK_RE.match(line)
187 if not (m and m.group(1) in prefix_set) and line != '//\n':
188 input_lines.append(line)
189 f.seek(0)
190 f.writelines(input_lines)
191 f.truncate()
193 # Execute clang, generate LLVM IR, and extract functions.
194 func_dict = {}
195 for p in run_list:
196 prefixes = p[0]
197 for prefix in prefixes:
198 func_dict.update({prefix: dict()})
199 for prefixes, clang_args, triple_in_cmd in run_list:
200 if args.verbose:
201 print('Extracted clang cmd: clang {}'.format(clang_args), file=sys.stderr)
202 print('Extracted FileCheck prefixes: {}'.format(prefixes), file=sys.stderr)
204 get_function_body(args, filename, clang_args, prefixes, triple_in_cmd, func_dict)
206 # Invoke c-index-test to get mapping from start lines to mangled names.
207 # Forward all clang args for now.
208 for k, v in get_line2spell_and_mangled(args, clang_args).items():
209 line2spell_and_mangled_list[k].append(v)
211 output_lines = [autogenerated_note]
212 for idx, line in enumerate(input_lines):
213 # Discard any previous script advertising.
214 if line.startswith(ADVERT):
215 continue
216 if idx in line2spell_and_mangled_list:
217 added = set()
218 for spell, mangled in line2spell_and_mangled_list[idx]:
219 # One line may contain multiple function declarations.
220 # Skip if the mangled name has been added before.
221 # The line number may come from an included file,
222 # we simply require the spelling name to appear on the line
223 # to exclude functions from other files.
224 if mangled in added or spell not in line:
225 continue
226 if args.functions is None or any(re.search(regex, spell) for regex in args.functions):
227 if added:
228 output_lines.append('//')
229 added.add(mangled)
230 common.add_ir_checks(output_lines, '//', run_list, func_dict, mangled)
231 output_lines.append(line.rstrip('\n'))
233 # Update the test file.
234 with open(filename, 'w') as f:
235 for line in output_lines:
236 f.write(line + '\n')
238 return 0
241 if __name__ == '__main__':
242 sys.exit(main())