1from __future__ import print_function 2 3import argparse 4import copy 5import glob 6import itertools 7import os 8import re 9import subprocess 10import sys 11import shlex 12 13from typing import List 14 15##### Common utilities for update_*test_checks.py 16 17 18_verbose = False 19_prefix_filecheck_ir_name = '' 20 21class Regex(object): 22 """Wrap a compiled regular expression object to allow deep copy of a regexp. 23 This is required for the deep copy done in do_scrub. 24 25 """ 26 def __init__(self, regex): 27 self.regex = regex 28 29 def __deepcopy__(self, memo): 30 result = copy.copy(self) 31 result.regex = self.regex 32 return result 33 34 def search(self, line): 35 return self.regex.search(line) 36 37 def sub(self, repl, line): 38 return self.regex.sub(repl, line) 39 40 def pattern(self): 41 return self.regex.pattern 42 43 def flags(self): 44 return self.regex.flags 45 46class Filter(Regex): 47 """Augment a Regex object with a flag indicating whether a match should be 48 added (!is_filter_out) or removed (is_filter_out) from the generated checks. 49 50 """ 51 def __init__(self, regex, is_filter_out): 52 super(Filter, self).__init__(regex) 53 self.is_filter_out = is_filter_out 54 55 def __deepcopy__(self, memo): 56 result = copy.deepcopy(super(Filter, self), memo) 57 result.is_filter_out = copy.deepcopy(self.is_filter_out, memo) 58 return result 59 60def parse_commandline_args(parser): 61 class RegexAction(argparse.Action): 62 """Add a regular expression option value to a list of regular expressions. 63 This compiles the expression, wraps it in a Regex and adds it to the option 64 value list.""" 65 def __init__(self, option_strings, dest, nargs=None, **kwargs): 66 if nargs is not None: 67 raise ValueError('nargs not allowed') 68 super(RegexAction, self).__init__(option_strings, dest, **kwargs) 69 70 def do_call(self, namespace, values, flags): 71 value_list = getattr(namespace, self.dest) 72 if value_list is None: 73 value_list = [] 74 75 try: 76 value_list.append(Regex(re.compile(values, flags))) 77 except re.error as error: 78 raise ValueError('{}: Invalid regular expression \'{}\' ({})'.format( 79 option_string, error.pattern, error.msg)) 80 81 setattr(namespace, self.dest, value_list) 82 83 def __call__(self, parser, namespace, values, option_string=None): 84 self.do_call(namespace, values, 0) 85 86 class FilterAction(RegexAction): 87 """Add a filter to a list of filter option values.""" 88 def __init__(self, option_strings, dest, nargs=None, **kwargs): 89 super(FilterAction, self).__init__(option_strings, dest, nargs, **kwargs) 90 91 def __call__(self, parser, namespace, values, option_string=None): 92 super(FilterAction, self).__call__(parser, namespace, values, option_string) 93 94 value_list = getattr(namespace, self.dest) 95 96 is_filter_out = ( option_string == '--filter-out' ) 97 98 value_list[-1] = Filter(value_list[-1].regex, is_filter_out) 99 100 setattr(namespace, self.dest, value_list) 101 102 filter_group = parser.add_argument_group( 103 'filtering', 104 """Filters are applied to each output line according to the order given. The 105 first matching filter terminates filter processing for that current line.""") 106 107 filter_group.add_argument('--filter', action=FilterAction, dest='filters', 108 metavar='REGEX', 109 help='Only include lines matching REGEX (may be specified multiple times)') 110 filter_group.add_argument('--filter-out', action=FilterAction, dest='filters', 111 metavar='REGEX', 112 help='Exclude lines matching REGEX') 113 114 parser.add_argument('--include-generated-funcs', action='store_true', 115 help='Output checks for functions not in source') 116 parser.add_argument('-v', '--verbose', action='store_true', 117 help='Show verbose output') 118 parser.add_argument('-u', '--update-only', action='store_true', 119 help='Only update test if it was already autogened') 120 parser.add_argument('--force-update', action='store_true', 121 help='Update test even if it was autogened by a different script') 122 parser.add_argument('--enable', action='store_true', dest='enabled', default=True, 123 help='Activate CHECK line generation from this point forward') 124 parser.add_argument('--disable', action='store_false', dest='enabled', 125 help='Deactivate CHECK line generation from this point forward') 126 parser.add_argument('--replace-value-regex', nargs='+', default=[], 127 help='List of regular expressions to replace matching value names') 128 parser.add_argument('--prefix-filecheck-ir-name', default='', 129 help='Add a prefix to FileCheck IR value names to avoid conflicts with scripted names') 130 parser.add_argument('--global-value-regex', nargs='+', default=[], 131 help='List of regular expressions that a global value declaration must match to generate a check (has no effect if checking globals is not enabled)') 132 parser.add_argument('--global-hex-value-regex', nargs='+', default=[], 133 help='List of regular expressions such that, for matching global value declarations, literal integer values should be encoded in hex in the associated FileCheck directives') 134 # FIXME: in 3.9, we can use argparse.BooleanOptionalAction. At that point, 135 # we need to rename the flag to just -generate-body-for-unused-prefixes. 136 parser.add_argument('--no-generate-body-for-unused-prefixes', 137 action='store_false', 138 dest='gen_unused_prefix_body', 139 default=True, 140 help='Generate a function body that always matches for unused prefixes. This is useful when unused prefixes are desired, and it avoids needing to annotate each FileCheck as allowing them.') 141 args = parser.parse_args() 142 global _verbose, _global_value_regex, _global_hex_value_regex 143 _verbose = args.verbose 144 _global_value_regex = args.global_value_regex 145 _global_hex_value_regex = args.global_hex_value_regex 146 return args 147 148 149class InputLineInfo(object): 150 def __init__(self, line, line_number, args, argv): 151 self.line = line 152 self.line_number = line_number 153 self.args = args 154 self.argv = argv 155 156 157class TestInfo(object): 158 def __init__(self, test, parser, script_name, input_lines, args, argv, 159 comment_prefix, argparse_callback): 160 self.parser = parser 161 self.argparse_callback = argparse_callback 162 self.path = test 163 self.args = args 164 if args.prefix_filecheck_ir_name: 165 global _prefix_filecheck_ir_name 166 _prefix_filecheck_ir_name = args.prefix_filecheck_ir_name 167 self.argv = argv 168 self.input_lines = input_lines 169 self.run_lines = find_run_lines(test, self.input_lines) 170 self.comment_prefix = comment_prefix 171 if self.comment_prefix is None: 172 if self.path.endswith('.mir'): 173 self.comment_prefix = '#' 174 else: 175 self.comment_prefix = ';' 176 self.autogenerated_note_prefix = self.comment_prefix + ' ' + UTC_ADVERT 177 self.test_autogenerated_note = self.autogenerated_note_prefix + script_name 178 self.test_autogenerated_note += get_autogennote_suffix(parser, self.args) 179 self.test_unused_note = self.comment_prefix + self.comment_prefix + ' ' + UNUSED_NOTE 180 181 def ro_iterlines(self): 182 for line_num, input_line in enumerate(self.input_lines): 183 args, argv = check_for_command(input_line, self.parser, 184 self.args, self.argv, self.argparse_callback) 185 yield InputLineInfo(input_line, line_num, args, argv) 186 187 def iterlines(self, output_lines): 188 output_lines.append(self.test_autogenerated_note) 189 for line_info in self.ro_iterlines(): 190 input_line = line_info.line 191 # Discard any previous script advertising. 192 if input_line.startswith(self.autogenerated_note_prefix): 193 continue 194 self.args = line_info.args 195 self.argv = line_info.argv 196 if not self.args.enabled: 197 output_lines.append(input_line) 198 continue 199 yield line_info 200 201 def get_checks_for_unused_prefixes(self, run_list, used_prefixes: List[str]) -> List[str]: 202 run_list = [element for element in run_list if element[0] is not None] 203 unused_prefixes = set([ 204 prefix for sublist in run_list for prefix in sublist[0] 205 ]).difference(set(used_prefixes)) 206 207 ret = [] 208 if not unused_prefixes: 209 return ret 210 ret.append(self.test_unused_note) 211 for unused in sorted(unused_prefixes): 212 ret.append('{comment} {prefix}: {match_everything}'.format( 213 comment=self.comment_prefix, 214 prefix=unused, 215 match_everything=r"""{{.*}}""" 216 )) 217 return ret 218 219def itertests(test_patterns, parser, script_name, comment_prefix=None, argparse_callback=None): 220 for pattern in test_patterns: 221 # On Windows we must expand the patterns ourselves. 222 tests_list = glob.glob(pattern) 223 if not tests_list: 224 warn("Test file pattern '%s' was not found. Ignoring it." % (pattern,)) 225 continue 226 for test in tests_list: 227 with open(test) as f: 228 input_lines = [l.rstrip() for l in f] 229 args = parser.parse_args() 230 if argparse_callback is not None: 231 argparse_callback(args) 232 argv = sys.argv[:] 233 first_line = input_lines[0] if input_lines else "" 234 if UTC_ADVERT in first_line: 235 if script_name not in first_line and not args.force_update: 236 warn("Skipping test which wasn't autogenerated by " + script_name, test) 237 continue 238 args, argv = check_for_command(first_line, parser, args, argv, argparse_callback) 239 elif args.update_only: 240 assert UTC_ADVERT not in first_line 241 warn("Skipping test which isn't autogenerated: " + test) 242 continue 243 final_input_lines = [] 244 for l in input_lines: 245 if UNUSED_NOTE in l: 246 break 247 final_input_lines.append(l) 248 yield TestInfo(test, parser, script_name, final_input_lines, args, argv, 249 comment_prefix, argparse_callback) 250 251 252def should_add_line_to_output(input_line, prefix_set, skip_global_checks = False, comment_marker = ';'): 253 # Skip any blank comment lines in the IR. 254 if not skip_global_checks and input_line.strip() == comment_marker: 255 return False 256 # Skip a special double comment line we use as a separator. 257 if input_line.strip() == comment_marker + SEPARATOR: 258 return False 259 # Skip any blank lines in the IR. 260 #if input_line.strip() == '': 261 # return False 262 # And skip any CHECK lines. We're building our own. 263 m = CHECK_RE.match(input_line) 264 if m and m.group(1) in prefix_set: 265 if skip_global_checks: 266 global_ir_value_re = re.compile(r'\[\[', flags=(re.M)) 267 return not global_ir_value_re.search(input_line) 268 return False 269 270 return True 271 272# Perform lit-like substitutions 273def getSubstitutions(sourcepath): 274 sourcedir = os.path.dirname(sourcepath) 275 return [('%s', sourcepath), 276 ('%S', sourcedir), 277 ('%p', sourcedir), 278 ('%{pathsep}', os.pathsep)] 279 280def applySubstitutions(s, substitutions): 281 for a,b in substitutions: 282 s = s.replace(a, b) 283 return s 284 285# Invoke the tool that is being tested. 286def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False): 287 with open(ir) as ir_file: 288 substitutions = getSubstitutions(ir) 289 290 # TODO Remove the str form which is used by update_test_checks.py and 291 # update_llc_test_checks.py 292 # The safer list form is used by update_cc_test_checks.py 293 if preprocess_cmd: 294 # Allow pre-processing the IR file (e.g. using sed): 295 assert isinstance(preprocess_cmd, str) # TODO: use a list instead of using shell 296 preprocess_cmd = applySubstitutions(preprocess_cmd, substitutions).strip() 297 if verbose: 298 print('Pre-processing input file: ', ir, " with command '", 299 preprocess_cmd, "'", sep="", file=sys.stderr) 300 # Python 2.7 doesn't have subprocess.DEVNULL: 301 with open(os.devnull, 'w') as devnull: 302 pp = subprocess.Popen(preprocess_cmd, shell=True, stdin=devnull, 303 stdout=subprocess.PIPE) 304 ir_file = pp.stdout 305 306 if isinstance(cmd_args, list): 307 args = [applySubstitutions(a, substitutions) for a in cmd_args] 308 stdout = subprocess.check_output([exe] + args, stdin=ir_file) 309 else: 310 stdout = subprocess.check_output(exe + ' ' + applySubstitutions(cmd_args, substitutions), 311 shell=True, stdin=ir_file) 312 if sys.version_info[0] > 2: 313 # FYI, if you crashed here with a decode error, your run line probably 314 # results in bitcode or other binary format being written to the pipe. 315 # For an opt test, you probably want to add -S or -disable-output. 316 stdout = stdout.decode() 317 # Fix line endings to unix CR style. 318 return stdout.replace('\r\n', '\n') 319 320##### LLVM IR parser 321RUN_LINE_RE = re.compile(r'^\s*(?://|[;#])\s*RUN:\s*(.*)$') 322CHECK_PREFIX_RE = re.compile(r'--?check-prefix(?:es)?[= ](\S+)') 323PREFIX_RE = re.compile('^[a-zA-Z0-9_-]+$') 324CHECK_RE = re.compile(r'^\s*(?://|[;#])\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL|-SAME|-EMPTY)?:') 325 326UTC_ARGS_KEY = 'UTC_ARGS:' 327UTC_ARGS_CMD = re.compile(r'.*' + UTC_ARGS_KEY + '\s*(?P<cmd>.*)\s*$') 328UTC_ADVERT = 'NOTE: Assertions have been autogenerated by ' 329UNUSED_NOTE = 'NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:' 330 331OPT_FUNCTION_RE = re.compile( 332 r'^(\s*;\s*Function\sAttrs:\s(?P<attrs>[\w\s():,]+?))?\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[\w.$-]+?)\s*' 333 r'(?P<args_and_sig>\((\)|(.*?[\w.-]+?)\))[^{]*\{)\n(?P<body>.*?)^\}$', 334 flags=(re.M | re.S)) 335 336ANALYZE_FUNCTION_RE = re.compile( 337 r'^\s*\'(?P<analysis>[\w\s-]+?)\'\s+for\s+function\s+\'(?P<func>[\w.$-]+?)\':' 338 r'\s*\n(?P<body>.*)$', 339 flags=(re.X | re.S)) 340 341LV_DEBUG_RE = re.compile( 342 r'^\s*\'(?P<func>[\w.$-]+?)\'[^\n]*' 343 r'\s*\n(?P<body>.*)$', 344 flags=(re.X | re.S)) 345 346IR_FUNCTION_RE = re.compile(r'^\s*define\s+(?:internal\s+)?[^@]*@"?([\w.$-]+)"?\s*\(') 347TRIPLE_IR_RE = re.compile(r'^\s*target\s+triple\s*=\s*"([^"]+)"$') 348TRIPLE_ARG_RE = re.compile(r'-mtriple[= ]([^ ]+)') 349MARCH_ARG_RE = re.compile(r'-march[= ]([^ ]+)') 350DEBUG_ONLY_ARG_RE = re.compile(r'-debug-only[= ]([^ ]+)') 351 352SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)') 353SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M) 354SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M) 355SCRUB_TRAILING_WHITESPACE_TEST_RE = SCRUB_TRAILING_WHITESPACE_RE 356SCRUB_TRAILING_WHITESPACE_AND_ATTRIBUTES_RE = re.compile(r'([ \t]|(#[0-9]+))+$', flags=re.M) 357SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n') 358SCRUB_LOOP_COMMENT_RE = re.compile( 359 r'# =>This Inner Loop Header:.*|# in Loop:.*', flags=re.M) 360SCRUB_TAILING_COMMENT_TOKEN_RE = re.compile(r'(?<=\S)+[ \t]*#$', flags=re.M) 361 362SEPARATOR = '.' 363 364def error(msg, test_file=None): 365 if test_file: 366 msg = '{}: {}'.format(msg, test_file) 367 print('ERROR: {}'.format(msg), file=sys.stderr) 368 369def warn(msg, test_file=None): 370 if test_file: 371 msg = '{}: {}'.format(msg, test_file) 372 print('WARNING: {}'.format(msg), file=sys.stderr) 373 374def debug(*args, **kwargs): 375 # Python2 does not allow def debug(*args, file=sys.stderr, **kwargs): 376 if 'file' not in kwargs: 377 kwargs['file'] = sys.stderr 378 if _verbose: 379 print(*args, **kwargs) 380 381def find_run_lines(test, lines): 382 debug('Scanning for RUN lines in test file:', test) 383 raw_lines = [m.group(1) 384 for m in [RUN_LINE_RE.match(l) for l in lines] if m] 385 run_lines = [raw_lines[0]] if len(raw_lines) > 0 else [] 386 for l in raw_lines[1:]: 387 if run_lines[-1].endswith('\\'): 388 run_lines[-1] = run_lines[-1].rstrip('\\') + ' ' + l 389 else: 390 run_lines.append(l) 391 debug('Found {} RUN lines in {}:'.format(len(run_lines), test)) 392 for l in run_lines: 393 debug(' RUN: {}'.format(l)) 394 return run_lines 395 396def get_triple_from_march(march): 397 triples = { 398 'amdgcn': 'amdgcn', 399 'r600': 'r600', 400 'mips': 'mips', 401 'sparc': 'sparc', 402 'hexagon': 'hexagon', 403 've': 've', 404 } 405 for prefix, triple in triples.items(): 406 if march.startswith(prefix): 407 return triple 408 print("Cannot find a triple. Assume 'x86'", file=sys.stderr) 409 return 'x86' 410 411def apply_filters(line, filters): 412 has_filter = False 413 for f in filters: 414 if not f.is_filter_out: 415 has_filter = True 416 if f.search(line): 417 return False if f.is_filter_out else True 418 # If we only used filter-out, keep the line, otherwise discard it since no 419 # filter matched. 420 return False if has_filter else True 421 422def do_filter(body, filters): 423 return body if not filters else '\n'.join(filter( 424 lambda line: apply_filters(line, filters), body.splitlines())) 425 426def scrub_body(body): 427 # Scrub runs of whitespace out of the assembly, but leave the leading 428 # whitespace in place. 429 body = SCRUB_WHITESPACE_RE.sub(r' ', body) 430 # Expand the tabs used for indentation. 431 body = str.expandtabs(body, 2) 432 # Strip trailing whitespace. 433 body = SCRUB_TRAILING_WHITESPACE_TEST_RE.sub(r'', body) 434 return body 435 436def do_scrub(body, scrubber, scrubber_args, extra): 437 if scrubber_args: 438 local_args = copy.deepcopy(scrubber_args) 439 local_args[0].extra_scrub = extra 440 return scrubber(body, *local_args) 441 return scrubber(body, *scrubber_args) 442 443# Build up a dictionary of all the function bodies. 444class function_body(object): 445 def __init__(self, string, extra, args_and_sig, attrs, func_name_separator): 446 self.scrub = string 447 self.extrascrub = extra 448 self.args_and_sig = args_and_sig 449 self.attrs = attrs 450 self.func_name_separator = func_name_separator 451 def is_same_except_arg_names(self, extrascrub, args_and_sig, attrs, is_backend): 452 arg_names = set() 453 def drop_arg_names(match): 454 arg_names.add(match.group(variable_group_in_ir_value_match)) 455 if match.group(attribute_group_in_ir_value_match): 456 attr = match.group(attribute_group_in_ir_value_match) 457 else: 458 attr = '' 459 return match.group(1) + attr + match.group(match.lastindex) 460 def repl_arg_names(match): 461 if match.group(variable_group_in_ir_value_match) is not None and match.group(variable_group_in_ir_value_match) in arg_names: 462 return match.group(1) + match.group(match.lastindex) 463 return match.group(1) + match.group(2) + match.group(match.lastindex) 464 if self.attrs != attrs: 465 return False 466 ans0 = IR_VALUE_RE.sub(drop_arg_names, self.args_and_sig) 467 ans1 = IR_VALUE_RE.sub(drop_arg_names, args_and_sig) 468 if ans0 != ans1: 469 return False 470 if is_backend: 471 # Check without replacements, the replacements are not applied to the 472 # body for backend checks. 473 return self.extrascrub == extrascrub 474 475 es0 = IR_VALUE_RE.sub(repl_arg_names, self.extrascrub) 476 es1 = IR_VALUE_RE.sub(repl_arg_names, extrascrub) 477 es0 = SCRUB_IR_COMMENT_RE.sub(r'', es0) 478 es1 = SCRUB_IR_COMMENT_RE.sub(r'', es1) 479 return es0 == es1 480 481 def __str__(self): 482 return self.scrub 483 484class FunctionTestBuilder: 485 def __init__(self, run_list, flags, scrubber_args, path): 486 self._verbose = flags.verbose 487 self._record_args = flags.function_signature 488 self._check_attributes = flags.check_attributes 489 # Strip double-quotes if input was read by UTC_ARGS 490 self._filters = list(map(lambda f: Filter(re.compile(f.pattern().strip('"'), 491 f.flags()), 492 f.is_filter_out), 493 flags.filters)) if flags.filters else [] 494 self._scrubber_args = scrubber_args 495 self._path = path 496 # Strip double-quotes if input was read by UTC_ARGS 497 self._replace_value_regex = list(map(lambda x: x.strip('"'), flags.replace_value_regex)) 498 self._func_dict = {} 499 self._func_order = {} 500 self._global_var_dict = {} 501 self._processed_prefixes = set() 502 for tuple in run_list: 503 for prefix in tuple[0]: 504 self._func_dict.update({prefix: dict()}) 505 self._func_order.update({prefix: []}) 506 self._global_var_dict.update({prefix: dict()}) 507 508 def finish_and_get_func_dict(self): 509 for prefix in self.get_failed_prefixes(): 510 warn('Prefix %s had conflicting output from different RUN lines for all functions in test %s' % (prefix,self._path,)) 511 return self._func_dict 512 513 def func_order(self): 514 return self._func_order 515 516 def global_var_dict(self): 517 return self._global_var_dict 518 519 def is_filtered(self): 520 return bool(self._filters) 521 522 def process_run_line(self, function_re, scrubber, raw_tool_output, prefixes, is_backend): 523 build_global_values_dictionary(self._global_var_dict, raw_tool_output, prefixes) 524 for m in function_re.finditer(raw_tool_output): 525 if not m: 526 continue 527 func = m.group('func') 528 body = m.group('body') 529 # func_name_separator is the string that is placed right after function name at the 530 # beginning of assembly function definition. In most assemblies, that is just a 531 # colon: `foo:`. But, for example, in nvptx it is a brace: `foo(`. If is_backend is 532 # False, just assume that separator is an empty string. 533 if is_backend: 534 # Use ':' as default separator. 535 func_name_separator = m.group('func_name_separator') if 'func_name_separator' in m.groupdict() else ':' 536 else: 537 func_name_separator = '' 538 attrs = m.group('attrs') if self._check_attributes else '' 539 # Determine if we print arguments, the opening brace, or nothing after the 540 # function name 541 if self._record_args and 'args_and_sig' in m.groupdict(): 542 args_and_sig = scrub_body(m.group('args_and_sig').strip()) 543 elif 'args_and_sig' in m.groupdict(): 544 args_and_sig = '(' 545 else: 546 args_and_sig = '' 547 filtered_body = do_filter(body, self._filters) 548 scrubbed_body = do_scrub(filtered_body, scrubber, self._scrubber_args, 549 extra=False) 550 scrubbed_extra = do_scrub(filtered_body, scrubber, self._scrubber_args, 551 extra=True) 552 if 'analysis' in m.groupdict(): 553 analysis = m.group('analysis') 554 if analysis.lower() != 'cost model analysis': 555 warn('Unsupported analysis mode: %r!' % (analysis,)) 556 if func.startswith('stress'): 557 # We only use the last line of the function body for stress tests. 558 scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:]) 559 if self._verbose: 560 print('Processing function: ' + func, file=sys.stderr) 561 for l in scrubbed_body.splitlines(): 562 print(' ' + l, file=sys.stderr) 563 for prefix in prefixes: 564 # Replace function names matching the regex. 565 for regex in self._replace_value_regex: 566 # Pattern that matches capture groups in the regex in leftmost order. 567 group_regex = re.compile(r'\(.*?\)') 568 # Replace function name with regex. 569 match = re.match(regex, func) 570 if match: 571 func_repl = regex 572 # Replace any capture groups with their matched strings. 573 for g in match.groups(): 574 func_repl = group_regex.sub(re.escape(g), func_repl, count=1) 575 func = re.sub(func_repl, '{{' + func_repl + '}}', func) 576 577 # Replace all calls to regex matching functions. 578 matches = re.finditer(regex, scrubbed_body) 579 for match in matches: 580 func_repl = regex 581 # Replace any capture groups with their matched strings. 582 for g in match.groups(): 583 func_repl = group_regex.sub(re.escape(g), func_repl, count=1) 584 # Substitute function call names that match the regex with the same 585 # capture groups set. 586 scrubbed_body = re.sub(func_repl, '{{' + func_repl + '}}', 587 scrubbed_body) 588 589 if func in self._func_dict[prefix]: 590 if (self._func_dict[prefix][func] is not None and 591 (str(self._func_dict[prefix][func]) != scrubbed_body or 592 self._func_dict[prefix][func].args_and_sig != args_and_sig or 593 self._func_dict[prefix][func].attrs != attrs)): 594 if self._func_dict[prefix][func].is_same_except_arg_names( 595 scrubbed_extra, 596 args_and_sig, 597 attrs, 598 is_backend): 599 self._func_dict[prefix][func].scrub = scrubbed_extra 600 self._func_dict[prefix][func].args_and_sig = args_and_sig 601 else: 602 # This means a previous RUN line produced a body for this function 603 # that is different from the one produced by this current RUN line, 604 # so the body can't be common across RUN lines. We use None to 605 # indicate that. 606 self._func_dict[prefix][func] = None 607 else: 608 if prefix not in self._processed_prefixes: 609 self._func_dict[prefix][func] = function_body( 610 scrubbed_body, scrubbed_extra, args_and_sig, attrs, 611 func_name_separator) 612 self._func_order[prefix].append(func) 613 else: 614 # An earlier RUN line used this check prefixes but didn't produce 615 # a body for this function. This happens in Clang tests that use 616 # preprocesser directives to exclude individual functions from some 617 # RUN lines. 618 self._func_dict[prefix][func] = None 619 620 def processed_prefixes(self, prefixes): 621 """ 622 Mark a set of prefixes as having had at least one applicable RUN line fully 623 processed. This is used to filter out function bodies that don't have 624 outputs for all RUN lines. 625 """ 626 self._processed_prefixes.update(prefixes) 627 628 def get_failed_prefixes(self): 629 # This returns the list of those prefixes that failed to match any function, 630 # because there were conflicting bodies produced by different RUN lines, in 631 # all instances of the prefix. 632 for prefix in self._func_dict: 633 if (self._func_dict[prefix] and 634 (not [fct for fct in self._func_dict[prefix] 635 if self._func_dict[prefix][fct] is not None])): 636 yield prefix 637 638 639##### Generator of LLVM IR CHECK lines 640 641SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*') 642 643# TODO: We should also derive check lines for global, debug, loop declarations, etc.. 644 645class NamelessValue: 646 def __init__(self, check_prefix, check_key, ir_prefix, global_ir_prefix, global_ir_prefix_regexp, 647 ir_regexp, global_ir_rhs_regexp, is_before_functions, *, 648 is_number=False, replace_number_with_counter=False): 649 self.check_prefix = check_prefix 650 self.check_key = check_key 651 self.ir_prefix = ir_prefix 652 self.global_ir_prefix = global_ir_prefix 653 self.global_ir_prefix_regexp = global_ir_prefix_regexp 654 self.ir_regexp = ir_regexp 655 self.global_ir_rhs_regexp = global_ir_rhs_regexp 656 self.is_before_functions = is_before_functions 657 self.is_number = is_number 658 # Some variable numbers (e.g. MCINST1234) will change based on unrelated 659 # modifications to LLVM, replace those with an incrementing counter. 660 self.replace_number_with_counter = replace_number_with_counter 661 self.variable_mapping = {} 662 663 # Return true if this kind of IR value is "local", basically if it matches '%{{.*}}'. 664 def is_local_def_ir_value_match(self, match): 665 return self.ir_prefix == '%' 666 667 # Return true if this kind of IR value is "global", basically if it matches '#{{.*}}'. 668 def is_global_scope_ir_value_match(self, match): 669 return self.global_ir_prefix is not None 670 671 # Return the IR prefix and check prefix we use for this kind or IR value, 672 # e.g., (%, TMP) for locals. 673 def get_ir_prefix_from_ir_value_match(self, match): 674 if self.ir_prefix and match.group(0).strip().startswith(self.ir_prefix): 675 return self.ir_prefix, self.check_prefix 676 return self.global_ir_prefix, self.check_prefix 677 678 # Return the IR regexp we use for this kind or IR value, e.g., [\w.-]+? for locals 679 def get_ir_regex_from_ir_value_re_match(self, match): 680 # for backwards compatibility we check locals with '.*' 681 if self.is_local_def_ir_value_match(match): 682 return '.*' 683 if self.ir_prefix and match.group(0).strip().startswith(self.ir_prefix): 684 return self.ir_regexp 685 return self.global_ir_prefix_regexp 686 687 # Create a FileCheck variable name based on an IR name. 688 def get_value_name(self, var: str, check_prefix: str): 689 var = var.replace('!', '') 690 if self.replace_number_with_counter: 691 assert var.isdigit(), var 692 replacement = self.variable_mapping.get(var, None) 693 if replacement is None: 694 # Replace variable with an incrementing counter 695 replacement = str(len(self.variable_mapping) + 1) 696 self.variable_mapping[var] = replacement 697 var = replacement 698 # This is a nameless value, prepend check_prefix. 699 if var.isdigit(): 700 var = check_prefix + var 701 else: 702 # This is a named value that clashes with the check_prefix, prepend with 703 # _prefix_filecheck_ir_name, if it has been defined. 704 if may_clash_with_default_check_prefix_name(check_prefix, var) and _prefix_filecheck_ir_name: 705 var = _prefix_filecheck_ir_name + var 706 var = var.replace('.', '_') 707 var = var.replace('-', '_') 708 return var.upper() 709 710 # Create a FileCheck variable from regex. 711 def get_value_definition(self, var, match): 712 # for backwards compatibility we check locals with '.*' 713 varname = self.get_value_name(var, self.check_prefix) 714 prefix = self.get_ir_prefix_from_ir_value_match(match)[0] 715 if self.is_number: 716 regex = '' # always capture a number in the default format 717 capture_start = '[[#' 718 else: 719 regex = self.get_ir_regex_from_ir_value_re_match(match) 720 capture_start = '[[' 721 if self.is_local_def_ir_value_match(match): 722 return capture_start + varname + ':' + prefix + regex + ']]' 723 return prefix + capture_start + varname + ':' + regex + ']]' 724 725 # Use a FileCheck variable. 726 def get_value_use(self, var, match, var_prefix=None): 727 if var_prefix is None: 728 var_prefix = self.check_prefix 729 capture_start = '[[#' if self.is_number else '[[' 730 if self.is_local_def_ir_value_match(match): 731 return capture_start + self.get_value_name(var, var_prefix) + ']]' 732 prefix = self.get_ir_prefix_from_ir_value_match(match)[0] 733 return prefix + capture_start + self.get_value_name(var, var_prefix) + ']]' 734 735# Description of the different "unnamed" values we match in the IR, e.g., 736# (local) ssa values, (debug) metadata, etc. 737ir_nameless_values = [ 738 NamelessValue(r'TMP' , '%' , r'%' , None , None , r'[\w$.-]+?' , None , False) , 739 NamelessValue(r'ATTR' , '#' , r'#' , None , None , r'[0-9]+' , None , False) , 740 NamelessValue(r'ATTR' , '#' , None , r'attributes #' , r'[0-9]+' , None , r'{[^}]*}' , False) , 741 NamelessValue(r'GLOB' , '@' , r'@' , None , None , r'[0-9]+' , None , False) , 742 NamelessValue(r'GLOB' , '@' , None , r'@' , r'[a-zA-Z0-9_$"\\.-]+' , None , r'.+' , True) , 743 NamelessValue(r'DBG' , '!' , r'!dbg ' , None , None , r'![0-9]+' , None , False) , 744 NamelessValue(r'DIASSIGNID' , '!' , r'!DIAssignID ' , None , None , r'![0-9]+' , None , False) , 745 NamelessValue(r'PROF' , '!' , r'!prof ' , None , None , r'![0-9]+' , None , False) , 746 NamelessValue(r'TBAA' , '!' , r'!tbaa ' , None , None , r'![0-9]+' , None , False) , 747 NamelessValue(r'TBAA_STRUCT', '!' , r'!tbaa.struct ' , None , None , r'![0-9]+' , None , False) , 748 NamelessValue(r'RNG' , '!' , r'!range ' , None , None , r'![0-9]+' , None , False) , 749 NamelessValue(r'LOOP' , '!' , r'!llvm.loop ' , None , None , r'![0-9]+' , None , False) , 750 NamelessValue(r'META' , '!' , r'metadata ' , None , None , r'![0-9]+' , None , False) , 751 NamelessValue(r'META' , '!' , None , r'' , r'![0-9]+' , None , r'(?:distinct |)!.*' , False) , 752 NamelessValue(r'ACC_GRP' , '!' , r'!llvm.access.group ' , None , None , r'![0-9]+' , None , False) , 753] 754 755asm_nameless_values = [ 756 NamelessValue(r'MCINST', 'Inst#', None, '<MCInst #', r'\d+', None, r'.+', 757 False, is_number=True, replace_number_with_counter=True), 758 NamelessValue(r'MCREG', 'Reg:', None, '<MCOperand Reg:', r'\d+', None, r'.+', 759 False, is_number=True, replace_number_with_counter=True), 760] 761 762def createOrRegexp(old, new): 763 if not old: 764 return new 765 if not new: 766 return old 767 return old + '|' + new 768 769def createPrefixMatch(prefix_str, prefix_re): 770 if prefix_str is None or prefix_re is None: 771 return '' 772 return '(?:' + prefix_str + '(' + prefix_re + '))' 773 774# Build the regexp that matches an "IR value". This can be a local variable, 775# argument, global, or metadata, anything that is "named". It is important that 776# the PREFIX and SUFFIX below only contain a single group, if that changes 777# other locations will need adjustment as well. 778IR_VALUE_REGEXP_PREFIX = r'(\s*)' 779IR_VALUE_REGEXP_STRING = r'' 780for nameless_value in ir_nameless_values: 781 lcl_match = createPrefixMatch(nameless_value.ir_prefix, nameless_value.ir_regexp) 782 glb_match = createPrefixMatch(nameless_value.global_ir_prefix, nameless_value.global_ir_prefix_regexp) 783 assert((lcl_match or glb_match) and not (lcl_match and glb_match)) 784 if lcl_match: 785 IR_VALUE_REGEXP_STRING = createOrRegexp(IR_VALUE_REGEXP_STRING, lcl_match) 786 elif glb_match: 787 IR_VALUE_REGEXP_STRING = createOrRegexp(IR_VALUE_REGEXP_STRING, '^' + glb_match) 788IR_VALUE_REGEXP_SUFFIX = r'([,\s\(\)]|\Z)' 789IR_VALUE_RE = re.compile(IR_VALUE_REGEXP_PREFIX + r'(' + IR_VALUE_REGEXP_STRING + r')' + IR_VALUE_REGEXP_SUFFIX) 790 791# Build the regexp that matches an "ASM value" (currently only for --asm-show-inst comments). 792ASM_VALUE_REGEXP_STRING = '' 793for nameless_value in asm_nameless_values: 794 glb_match = createPrefixMatch(nameless_value.global_ir_prefix, nameless_value.global_ir_prefix_regexp) 795 assert not nameless_value.ir_prefix and not nameless_value.ir_regexp 796 ASM_VALUE_REGEXP_STRING = createOrRegexp(ASM_VALUE_REGEXP_STRING, glb_match) 797ASM_VALUE_REGEXP_SUFFIX = r'([>\s]|\Z)' 798ASM_VALUE_RE = re.compile(r'((?:#|//)\s*)' + '(' + ASM_VALUE_REGEXP_STRING + ')' + ASM_VALUE_REGEXP_SUFFIX) 799 800# The entire match is group 0, the prefix has one group (=1), the entire 801# IR_VALUE_REGEXP_STRING is one group (=2), and then the nameless values start. 802first_nameless_group_in_ir_value_match = 3 803 804# constants for the group id of special matches 805variable_group_in_ir_value_match = 3 806attribute_group_in_ir_value_match = 4 807 808# Check a match for IR_VALUE_RE and inspect it to determine if it was a local 809# value, %..., global @..., debug number !dbg !..., etc. See the PREFIXES above. 810def get_idx_from_ir_value_match(match): 811 for i in range(first_nameless_group_in_ir_value_match, match.lastindex): 812 if match.group(i) is not None: 813 return i - first_nameless_group_in_ir_value_match 814 error("Unable to identify the kind of IR value from the match!") 815 return 0 816 817# See get_idx_from_ir_value_match 818def get_name_from_ir_value_match(match): 819 return match.group(get_idx_from_ir_value_match(match) + first_nameless_group_in_ir_value_match) 820 821def get_nameless_value_from_match(match, nameless_values) -> NamelessValue: 822 return nameless_values[get_idx_from_ir_value_match(match)] 823 824# Return true if var clashes with the scripted FileCheck check_prefix. 825def may_clash_with_default_check_prefix_name(check_prefix, var): 826 return check_prefix and re.match(r'^' + check_prefix + r'[0-9]+?$', var, re.IGNORECASE) 827 828def generalize_check_lines_common(lines, is_analyze, vars_seen, 829 global_vars_seen, nameless_values, 830 nameless_value_regex, is_asm): 831 # This gets called for each match that occurs in 832 # a line. We transform variables we haven't seen 833 # into defs, and variables we have seen into uses. 834 def transform_line_vars(match): 835 var = get_name_from_ir_value_match(match) 836 nameless_value = get_nameless_value_from_match(match, nameless_values) 837 if may_clash_with_default_check_prefix_name(nameless_value.check_prefix, var): 838 warn("Change IR value name '%s' or use --prefix-filecheck-ir-name to prevent possible conflict" 839 " with scripted FileCheck name." % (var,)) 840 key = (var, nameless_value.check_key) 841 is_local_def = nameless_value.is_local_def_ir_value_match(match) 842 if is_local_def and key in vars_seen: 843 rv = nameless_value.get_value_use(var, match) 844 elif not is_local_def and key in global_vars_seen: 845 # We could have seen a different prefix for the global variables first, 846 # ensure we use that one instead of the prefix for the current match. 847 rv = nameless_value.get_value_use(var, match, global_vars_seen[key]) 848 else: 849 if is_local_def: 850 vars_seen.add(key) 851 else: 852 global_vars_seen[key] = nameless_value.check_prefix 853 rv = nameless_value.get_value_definition(var, match) 854 # re.sub replaces the entire regex match 855 # with whatever you return, so we have 856 # to make sure to hand it back everything 857 # including the commas and spaces. 858 return match.group(1) + rv + match.group(match.lastindex) 859 860 lines_with_def = [] 861 862 for i, line in enumerate(lines): 863 if not is_asm: 864 # An IR variable named '%.' matches the FileCheck regex string. 865 line = line.replace('%.', '%dot') 866 for regex in _global_hex_value_regex: 867 if re.match('^@' + regex + ' = ', line): 868 line = re.sub(r'\bi([0-9]+) ([0-9]+)', 869 lambda m : 'i' + m.group(1) + ' [[#' + hex(int(m.group(2))) + ']]', 870 line) 871 break 872 # Ignore any comments, since the check lines will too. 873 scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line) 874 lines[i] = scrubbed_line 875 if is_asm or not is_analyze: 876 # It can happen that two matches are back-to-back and for some reason sub 877 # will not replace both of them. For now we work around this by 878 # substituting until there is no more match. 879 changed = True 880 while changed: 881 (lines[i], changed) = nameless_value_regex.subn(transform_line_vars, 882 lines[i], count=1) 883 return lines 884 885# Replace IR value defs and uses with FileCheck variables. 886def generalize_check_lines(lines, is_analyze, vars_seen, global_vars_seen): 887 return generalize_check_lines_common(lines, is_analyze, vars_seen, 888 global_vars_seen, ir_nameless_values, 889 IR_VALUE_RE, False) 890 891def generalize_asm_check_lines(lines, vars_seen, global_vars_seen): 892 return generalize_check_lines_common(lines, False, vars_seen, 893 global_vars_seen, asm_nameless_values, 894 ASM_VALUE_RE, True) 895 896def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_backend, is_analyze, global_vars_seen_dict, is_filtered): 897 # prefix_exclusions are prefixes we cannot use to print the function because it doesn't exist in run lines that use these prefixes as well. 898 prefix_exclusions = set() 899 printed_prefixes = [] 900 for p in prefix_list: 901 checkprefixes = p[0] 902 # If not all checkprefixes of this run line produced the function we cannot check for it as it does not 903 # exist for this run line. A subset of the check prefixes might know about the function but only because 904 # other run lines created it. 905 if any(map(lambda checkprefix: func_name not in func_dict[checkprefix], checkprefixes)): 906 prefix_exclusions |= set(checkprefixes) 907 continue 908 909 # prefix_exclusions is constructed, we can now emit the output 910 for p in prefix_list: 911 global_vars_seen = {} 912 checkprefixes = p[0] 913 for checkprefix in checkprefixes: 914 if checkprefix in global_vars_seen_dict: 915 global_vars_seen.update(global_vars_seen_dict[checkprefix]) 916 else: 917 global_vars_seen_dict[checkprefix] = {} 918 if checkprefix in printed_prefixes: 919 break 920 921 # Check if the prefix is excluded. 922 if checkprefix in prefix_exclusions: 923 continue 924 925 # If we do not have output for this prefix we skip it. 926 if not func_dict[checkprefix][func_name]: 927 continue 928 929 # Add some space between different check prefixes, but not after the last 930 # check line (before the test code). 931 if is_backend: 932 if len(printed_prefixes) != 0: 933 output_lines.append(comment_marker) 934 935 if checkprefix not in global_vars_seen_dict: 936 global_vars_seen_dict[checkprefix] = {} 937 938 global_vars_seen_before = [key for key in global_vars_seen.keys()] 939 940 vars_seen = set() 941 printed_prefixes.append(checkprefix) 942 attrs = str(func_dict[checkprefix][func_name].attrs) 943 attrs = '' if attrs == 'None' else attrs 944 if attrs: 945 output_lines.append('%s %s: Function Attrs: %s' % (comment_marker, checkprefix, attrs)) 946 args_and_sig = str(func_dict[checkprefix][func_name].args_and_sig) 947 if args_and_sig: 948 args_and_sig = generalize_check_lines([args_and_sig], is_analyze, vars_seen, global_vars_seen)[0] 949 func_name_separator = func_dict[checkprefix][func_name].func_name_separator 950 if '[[' in args_and_sig: 951 output_lines.append(check_label_format % (checkprefix, func_name, '', func_name_separator)) 952 output_lines.append('%s %s-SAME: %s' % (comment_marker, checkprefix, args_and_sig)) 953 else: 954 output_lines.append(check_label_format % (checkprefix, func_name, args_and_sig, func_name_separator)) 955 func_body = str(func_dict[checkprefix][func_name]).splitlines() 956 if not func_body: 957 # We have filtered everything. 958 continue 959 960 # For ASM output, just emit the check lines. 961 if is_backend: 962 body_start = 1 963 if is_filtered: 964 # For filtered output we don't add "-NEXT" so don't add extra spaces 965 # before the first line. 966 body_start = 0 967 else: 968 output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0])) 969 func_lines = generalize_asm_check_lines(func_body[body_start:], 970 vars_seen, global_vars_seen) 971 for func_line in func_lines: 972 if func_line.strip() == '': 973 output_lines.append('%s %s-EMPTY:' % (comment_marker, checkprefix)) 974 else: 975 check_suffix = '-NEXT' if not is_filtered else '' 976 output_lines.append('%s %s%s: %s' % (comment_marker, checkprefix, 977 check_suffix, func_line)) 978 # Remember new global variables we have not seen before 979 for key in global_vars_seen: 980 if key not in global_vars_seen_before: 981 global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] 982 break 983 984 # For IR output, change all defs to FileCheck variables, so we're immune 985 # to variable naming fashions. 986 func_body = generalize_check_lines(func_body, is_analyze, vars_seen, global_vars_seen) 987 988 # This could be selectively enabled with an optional invocation argument. 989 # Disabled for now: better to check everything. Be safe rather than sorry. 990 991 # Handle the first line of the function body as a special case because 992 # it's often just noise (a useless asm comment or entry label). 993 #if func_body[0].startswith("#") or func_body[0].startswith("entry:"): 994 # is_blank_line = True 995 #else: 996 # output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0])) 997 # is_blank_line = False 998 999 is_blank_line = False 1000 1001 for func_line in func_body: 1002 if func_line.strip() == '': 1003 is_blank_line = True 1004 continue 1005 # Do not waste time checking IR comments. 1006 func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line) 1007 1008 # Skip blank lines instead of checking them. 1009 if is_blank_line: 1010 output_lines.append('{} {}: {}'.format( 1011 comment_marker, checkprefix, func_line)) 1012 else: 1013 check_suffix = '-NEXT' if not is_filtered else '' 1014 output_lines.append('{} {}{}: {}'.format( 1015 comment_marker, checkprefix, check_suffix, func_line)) 1016 is_blank_line = False 1017 1018 # Add space between different check prefixes and also before the first 1019 # line of code in the test function. 1020 output_lines.append(comment_marker) 1021 1022 # Remember new global variables we have not seen before 1023 for key in global_vars_seen: 1024 if key not in global_vars_seen_before: 1025 global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] 1026 break 1027 return printed_prefixes 1028 1029def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict, 1030 func_name, preserve_names, function_sig, 1031 global_vars_seen_dict, is_filtered): 1032 # Label format is based on IR string. 1033 function_def_regex = 'define {{[^@]+}}' if function_sig else '' 1034 check_label_format = '{} %s-LABEL: {}@%s%s%s'.format(comment_marker, function_def_regex) 1035 return add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, 1036 check_label_format, False, preserve_names, global_vars_seen_dict, 1037 is_filtered) 1038 1039def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, is_filtered): 1040 check_label_format = '{} %s-LABEL: \'%s%s%s\''.format(comment_marker) 1041 global_vars_seen_dict = {} 1042 return add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, 1043 check_label_format, False, True, global_vars_seen_dict, 1044 is_filtered) 1045 1046def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes): 1047 for nameless_value in itertools.chain(ir_nameless_values, asm_nameless_values): 1048 if nameless_value.global_ir_prefix is None: 1049 continue 1050 1051 lhs_re_str = nameless_value.global_ir_prefix + nameless_value.global_ir_prefix_regexp 1052 rhs_re_str = nameless_value.global_ir_rhs_regexp 1053 1054 global_ir_value_re_str = r'^' + lhs_re_str + r'\s=\s' + rhs_re_str + r'$' 1055 global_ir_value_re = re.compile(global_ir_value_re_str, flags=(re.M)) 1056 lines = [] 1057 for m in global_ir_value_re.finditer(raw_tool_output): 1058 lines.append(m.group(0)) 1059 1060 for prefix in prefixes: 1061 if glob_val_dict[prefix] is None: 1062 continue 1063 if nameless_value.check_prefix in glob_val_dict[prefix]: 1064 if lines == glob_val_dict[prefix][nameless_value.check_prefix]: 1065 continue 1066 if prefix == prefixes[-1]: 1067 warn('Found conflicting asm under the same prefix: %r!' % (prefix,)) 1068 else: 1069 glob_val_dict[prefix][nameless_value.check_prefix] = None 1070 continue 1071 glob_val_dict[prefix][nameless_value.check_prefix] = lines 1072 1073def add_global_checks(glob_val_dict, comment_marker, prefix_list, output_lines, global_vars_seen_dict, is_analyze, is_before_functions): 1074 printed_prefixes = set() 1075 for nameless_value in ir_nameless_values: 1076 if nameless_value.global_ir_prefix is None: 1077 continue 1078 if nameless_value.is_before_functions != is_before_functions: 1079 continue 1080 for p in prefix_list: 1081 global_vars_seen = {} 1082 checkprefixes = p[0] 1083 if checkprefixes is None: 1084 continue 1085 for checkprefix in checkprefixes: 1086 if checkprefix in global_vars_seen_dict: 1087 global_vars_seen.update(global_vars_seen_dict[checkprefix]) 1088 else: 1089 global_vars_seen_dict[checkprefix] = {} 1090 if (checkprefix, nameless_value.check_prefix) in printed_prefixes: 1091 break 1092 if not glob_val_dict[checkprefix]: 1093 continue 1094 if nameless_value.check_prefix not in glob_val_dict[checkprefix]: 1095 continue 1096 if not glob_val_dict[checkprefix][nameless_value.check_prefix]: 1097 continue 1098 1099 check_lines = [] 1100 global_vars_seen_before = [key for key in global_vars_seen.keys()] 1101 for line in glob_val_dict[checkprefix][nameless_value.check_prefix]: 1102 if _global_value_regex: 1103 matched = False 1104 for regex in _global_value_regex: 1105 if re.match('^@' + regex + ' = ', line): 1106 matched = True 1107 break 1108 if not matched: 1109 continue 1110 tmp = generalize_check_lines([line], is_analyze, set(), global_vars_seen) 1111 check_line = '%s %s: %s' % (comment_marker, checkprefix, tmp[0]) 1112 check_lines.append(check_line) 1113 if not check_lines: 1114 continue 1115 1116 output_lines.append(comment_marker + SEPARATOR) 1117 for check_line in check_lines: 1118 output_lines.append(check_line) 1119 1120 printed_prefixes.add((checkprefix, nameless_value.check_prefix)) 1121 1122 # Remembe new global variables we have not seen before 1123 for key in global_vars_seen: 1124 if key not in global_vars_seen_before: 1125 global_vars_seen_dict[checkprefix][key] = global_vars_seen[key] 1126 break 1127 1128 if printed_prefixes: 1129 output_lines.append(comment_marker + SEPARATOR) 1130 return printed_prefixes 1131 1132 1133def check_prefix(prefix): 1134 if not PREFIX_RE.match(prefix): 1135 hint = "" 1136 if ',' in prefix: 1137 hint = " Did you mean '--check-prefixes=" + prefix + "'?" 1138 warn(("Supplied prefix '%s' is invalid. Prefix must contain only alphanumeric characters, hyphens and underscores." + hint) % 1139 (prefix)) 1140 1141 1142def verify_filecheck_prefixes(fc_cmd): 1143 fc_cmd_parts = fc_cmd.split() 1144 for part in fc_cmd_parts: 1145 if "check-prefix=" in part: 1146 prefix = part.split('=', 1)[1] 1147 check_prefix(prefix) 1148 elif "check-prefixes=" in part: 1149 prefixes = part.split('=', 1)[1].split(',') 1150 for prefix in prefixes: 1151 check_prefix(prefix) 1152 if prefixes.count(prefix) > 1: 1153 warn("Supplied prefix '%s' is not unique in the prefix list." % (prefix,)) 1154 1155 1156def get_autogennote_suffix(parser, args): 1157 autogenerated_note_args = '' 1158 for action in parser._actions: 1159 if not hasattr(args, action.dest): 1160 continue # Ignore options such as --help that aren't included in args 1161 # Ignore parameters such as paths to the binary or the list of tests 1162 if action.dest in ('tests', 'update_only', 'tool_binary', 'opt_binary', 1163 'llc_binary', 'clang', 'opt', 'llvm_bin', 'verbose', 1164 'force_update'): 1165 continue 1166 value = getattr(args, action.dest) 1167 if action.const is not None: # action stores a constant (usually True/False) 1168 # Skip actions with different constant values (this happens with boolean 1169 # --foo/--no-foo options) 1170 if value != action.const: 1171 continue 1172 if parser.get_default(action.dest) == value: 1173 continue # Don't add default values 1174 if action.dest == 'filters': 1175 # Create a separate option for each filter element. The value is a list 1176 # of Filter objects. 1177 for elem in value: 1178 opt_name = 'filter-out' if elem.is_filter_out else 'filter' 1179 opt_value = elem.pattern() 1180 new_arg = '--%s "%s" ' % (opt_name, opt_value.strip('"')) 1181 if new_arg not in autogenerated_note_args: 1182 autogenerated_note_args += new_arg 1183 else: 1184 autogenerated_note_args += action.option_strings[0] + ' ' 1185 if action.const is None: # action takes a parameter 1186 if action.nargs == '+': 1187 value = ' '.join(map(lambda v: '"' + v.strip('"') + '"', value)) 1188 autogenerated_note_args += '%s ' % value 1189 if autogenerated_note_args: 1190 autogenerated_note_args = ' %s %s' % (UTC_ARGS_KEY, autogenerated_note_args[:-1]) 1191 return autogenerated_note_args 1192 1193 1194def check_for_command(line, parser, args, argv, argparse_callback): 1195 cmd_m = UTC_ARGS_CMD.match(line) 1196 if cmd_m: 1197 for option in shlex.split(cmd_m.group('cmd').strip()): 1198 if option: 1199 argv.append(option) 1200 args = parser.parse_args(filter(lambda arg: arg not in args.tests, argv)) 1201 if argparse_callback is not None: 1202 argparse_callback(args) 1203 return args, argv 1204 1205def find_arg_in_test(test_info, get_arg_to_check, arg_string, is_global): 1206 result = get_arg_to_check(test_info.args) 1207 if not result and is_global: 1208 # See if this has been specified via UTC_ARGS. This is a "global" option 1209 # that affects the entire generation of test checks. If it exists anywhere 1210 # in the test, apply it to everything. 1211 saw_line = False 1212 for line_info in test_info.ro_iterlines(): 1213 line = line_info.line 1214 if not line.startswith(';') and line.strip() != '': 1215 saw_line = True 1216 result = get_arg_to_check(line_info.args) 1217 if result: 1218 if warn and saw_line: 1219 # We saw the option after already reading some test input lines. 1220 # Warn about it. 1221 print('WARNING: Found {} in line following test start: '.format(arg_string) 1222 + line, file=sys.stderr) 1223 print('WARNING: Consider moving {} to top of file'.format(arg_string), 1224 file=sys.stderr) 1225 break 1226 return result 1227 1228def dump_input_lines(output_lines, test_info, prefix_set, comment_string): 1229 for input_line_info in test_info.iterlines(output_lines): 1230 line = input_line_info.line 1231 args = input_line_info.args 1232 if line.strip() == comment_string: 1233 continue 1234 if line.strip() == comment_string + SEPARATOR: 1235 continue 1236 if line.lstrip().startswith(comment_string): 1237 m = CHECK_RE.match(line) 1238 if m and m.group(1) in prefix_set: 1239 continue 1240 output_lines.append(line.rstrip('\n')) 1241 1242def add_checks_at_end(output_lines, prefix_list, func_order, 1243 comment_string, check_generator): 1244 added = set() 1245 generated_prefixes = set() 1246 for prefix in prefix_list: 1247 prefixes = prefix[0] 1248 tool_args = prefix[1] 1249 for prefix in prefixes: 1250 for func in func_order[prefix]: 1251 # The func order can contain the same functions multiple times. 1252 # If we see one again we are done. 1253 if (func, prefix) in added: 1254 continue 1255 if added: 1256 output_lines.append(comment_string) 1257 1258 # The add_*_checks routines expect a run list whose items are 1259 # tuples that have a list of prefixes as their first element and 1260 # tool command args string as their second element. They output 1261 # checks for each prefix in the list of prefixes. By doing so, it 1262 # implicitly assumes that for each function every run line will 1263 # generate something for that function. That is not the case for 1264 # generated functions as some run lines might not generate them 1265 # (e.g. -fopenmp vs. no -fopenmp). 1266 # 1267 # Therefore, pass just the prefix we're interested in. This has 1268 # the effect of generating all of the checks for functions of a 1269 # single prefix before moving on to the next prefix. So checks 1270 # are ordered by prefix instead of by function as in "normal" 1271 # mode. 1272 for generated_prefix in check_generator(output_lines, 1273 [([prefix], tool_args)], func): 1274 added.add((func, generated_prefix)) 1275 generated_prefixes.add(generated_prefix) 1276 return generated_prefixes 1277