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