123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- from __future__ import print_function
- import re
- import string
- import subprocess
- import sys
- import copy
- if sys.version_info[0] > 2:
- class string:
- expandtabs = str.expandtabs
- else:
- import string
- ##### Common utilities for update_*test_checks.py
- def should_add_line_to_output(input_line, prefix_set):
- # Skip any blank comment lines in the IR.
- if input_line.strip() == ';':
- return False
- # Skip any blank lines in the IR.
- #if input_line.strip() == '':
- # return False
- # And skip any CHECK lines. We're building our own.
- m = CHECK_RE.match(input_line)
- if m and m.group(1) in prefix_set:
- return False
- return True
- # Invoke the tool that is being tested.
- def invoke_tool(exe, cmd_args, ir):
- with open(ir) as ir_file:
- # TODO Remove the str form which is used by update_test_checks.py and
- # update_llc_test_checks.py
- # The safer list form is used by update_cc_test_checks.py
- if isinstance(cmd_args, list):
- stdout = subprocess.check_output([exe] + cmd_args, stdin=ir_file)
- else:
- stdout = subprocess.check_output(exe + ' ' + cmd_args,
- shell=True, stdin=ir_file)
- if sys.version_info[0] > 2:
- stdout = stdout.decode()
- # Fix line endings to unix CR style.
- return stdout.replace('\r\n', '\n')
- ##### LLVM IR parser
- RUN_LINE_RE = re.compile('^\s*[;#]\s*RUN:\s*(.*)$')
- CHECK_PREFIX_RE = re.compile('--?check-prefix(?:es)?[= ](\S+)')
- PREFIX_RE = re.compile('^[a-zA-Z0-9_-]+$')
- CHECK_RE = re.compile(r'^\s*[;#]\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL)?:')
- OPT_FUNCTION_RE = re.compile(
- r'^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[\w-]+?)\s*\('
- r'(\s+)?[^)]*[^{]*\{\n(?P<body>.*?)^\}$',
- flags=(re.M | re.S))
- ANALYZE_FUNCTION_RE = re.compile(
- r'^\s*\'(?P<analysis>[\w\s-]+?)\'\s+for\s+function\s+\'(?P<func>[\w-]+?)\':'
- r'\s*\n(?P<body>.*)$',
- flags=(re.X | re.S))
- IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@(\w+)\s*\(')
- TRIPLE_IR_RE = re.compile(r'^\s*target\s+triple\s*=\s*"([^"]+)"$')
- TRIPLE_ARG_RE = re.compile(r'-mtriple[= ]([^ ]+)')
- MARCH_ARG_RE = re.compile(r'-march[= ]([^ ]+)')
- SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)')
- SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M)
- SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M)
- SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n')
- SCRUB_LOOP_COMMENT_RE = re.compile(
- r'# =>This Inner Loop Header:.*|# in Loop:.*', flags=re.M)
- def error(msg, test_file=None):
- if test_file:
- msg = '{}: {}'.format(msg, test_file)
- print('ERROR: {}'.format(msg), file=sys.stderr)
- def warn(msg, test_file=None):
- if test_file:
- msg = '{}: {}'.format(msg, test_file)
- print('WARNING: {}'.format(msg), file=sys.stderr)
- def scrub_body(body):
- # Scrub runs of whitespace out of the assembly, but leave the leading
- # whitespace in place.
- body = SCRUB_WHITESPACE_RE.sub(r' ', body)
- # Expand the tabs used for indentation.
- body = string.expandtabs(body, 2)
- # Strip trailing whitespace.
- body = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', body)
- return body
- def do_scrub(body, scrubber, scrubber_args, extra):
- if scrubber_args:
- local_args = copy.deepcopy(scrubber_args)
- local_args[0].extra_scrub = extra
- return scrubber(body, *local_args)
- return scrubber(body, *scrubber_args)
- # Build up a dictionary of all the function bodies.
- class function_body(object):
- def __init__(self, string, extra):
- self.scrub = string
- self.extrascrub = extra
- def __str__(self):
- return self.scrub
- def build_function_body_dictionary(function_re, scrubber, scrubber_args, raw_tool_output, prefixes, func_dict, verbose):
- for m in function_re.finditer(raw_tool_output):
- if not m:
- continue
- func = m.group('func')
- body = m.group('body')
- scrubbed_body = do_scrub(body, scrubber, scrubber_args, extra = False)
- scrubbed_extra = do_scrub(body, scrubber, scrubber_args, extra = True)
- if 'analysis' in m.groupdict():
- analysis = m.group('analysis')
- if analysis.lower() != 'cost model analysis':
- warn('Unsupported analysis mode: %r!' % (analysis,))
- if func.startswith('stress'):
- # We only use the last line of the function body for stress tests.
- scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:])
- if verbose:
- print('Processing function: ' + func, file=sys.stderr)
- for l in scrubbed_body.splitlines():
- print(' ' + l, file=sys.stderr)
- for prefix in prefixes:
- if func in func_dict[prefix] and str(func_dict[prefix][func]) != scrubbed_body:
- if func_dict[prefix][func] and func_dict[prefix][func].extrascrub == scrubbed_extra:
- func_dict[prefix][func].scrub = scrubbed_extra
- continue
- else:
- if prefix == prefixes[-1]:
- warn('Found conflicting asm under the same prefix: %r!' % (prefix,))
- else:
- func_dict[prefix][func] = None
- continue
- func_dict[prefix][func] = function_body(scrubbed_body, scrubbed_extra)
- ##### Generator of LLVM IR CHECK lines
- SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*')
- # Match things that look at identifiers, but only if they are followed by
- # spaces, commas, paren, or end of the string
- IR_VALUE_RE = re.compile(r'(\s+)%([\w\.\-]+?)([,\s\(\)]|\Z)')
- # Create a FileCheck variable name based on an IR name.
- def get_value_name(var):
- if var.isdigit():
- var = 'TMP' + var
- var = var.replace('.', '_')
- var = var.replace('-', '_')
- return var.upper()
- # Create a FileCheck variable from regex.
- def get_value_definition(var):
- return '[[' + get_value_name(var) + ':%.*]]'
- # Use a FileCheck variable.
- def get_value_use(var):
- return '[[' + get_value_name(var) + ']]'
- # Replace IR value defs and uses with FileCheck variables.
- def genericize_check_lines(lines, is_analyze):
- # This gets called for each match that occurs in
- # a line. We transform variables we haven't seen
- # into defs, and variables we have seen into uses.
- def transform_line_vars(match):
- var = match.group(2)
- if var in vars_seen:
- rv = get_value_use(var)
- else:
- vars_seen.add(var)
- rv = get_value_definition(var)
- # re.sub replaces the entire regex match
- # with whatever you return, so we have
- # to make sure to hand it back everything
- # including the commas and spaces.
- return match.group(1) + rv + match.group(3)
- vars_seen = set()
- lines_with_def = []
- for i, line in enumerate(lines):
- # An IR variable named '%.' matches the FileCheck regex string.
- line = line.replace('%.', '%dot')
- # Ignore any comments, since the check lines will too.
- scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line)
- if is_analyze:
- lines[i] = scrubbed_line
- else:
- lines[i] = IR_VALUE_RE.sub(transform_line_vars, scrubbed_line)
- return lines
- def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_asm, is_analyze):
- printed_prefixes = []
- for p in prefix_list:
- checkprefixes = p[0]
- for checkprefix in checkprefixes:
- if checkprefix in printed_prefixes:
- break
- # TODO func_dict[checkprefix] may be None, '' or not exist.
- # Fix the call sites.
- if func_name not in func_dict[checkprefix] or not func_dict[checkprefix][func_name]:
- continue
- # Add some space between different check prefixes, but not after the last
- # check line (before the test code).
- if is_asm:
- if len(printed_prefixes) != 0:
- output_lines.append(comment_marker)
- printed_prefixes.append(checkprefix)
- output_lines.append(check_label_format % (checkprefix, func_name))
- func_body = str(func_dict[checkprefix][func_name]).splitlines()
- # For ASM output, just emit the check lines.
- if is_asm:
- output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
- for func_line in func_body[1:]:
- output_lines.append('%s %s-NEXT: %s' % (comment_marker, checkprefix, func_line))
- break
- # For IR output, change all defs to FileCheck variables, so we're immune
- # to variable naming fashions.
- func_body = genericize_check_lines(func_body, is_analyze)
- # This could be selectively enabled with an optional invocation argument.
- # Disabled for now: better to check everything. Be safe rather than sorry.
- # Handle the first line of the function body as a special case because
- # it's often just noise (a useless asm comment or entry label).
- #if func_body[0].startswith("#") or func_body[0].startswith("entry:"):
- # is_blank_line = True
- #else:
- # output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0]))
- # is_blank_line = False
- is_blank_line = False
- for func_line in func_body:
- if func_line.strip() == '':
- is_blank_line = True
- continue
- # Do not waste time checking IR comments.
- func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line)
- # Skip blank lines instead of checking them.
- if is_blank_line:
- output_lines.append('{} {}: {}'.format(
- comment_marker, checkprefix, func_line))
- else:
- output_lines.append('{} {}-NEXT: {}'.format(
- comment_marker, checkprefix, func_line))
- is_blank_line = False
- # Add space between different check prefixes and also before the first
- # line of code in the test function.
- output_lines.append(comment_marker)
- break
- def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict, func_name):
- # Label format is based on IR string.
- check_label_format = '{} %s-LABEL: @%s('.format(comment_marker)
- add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, False, False)
- def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, func_name):
- check_label_format = '{} %s-LABEL: \'%s\''.format(comment_marker)
- add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, False, True)
- def check_prefix(prefix):
- if not PREFIX_RE.match(prefix):
- hint = ""
- if ',' in prefix:
- hint = " Did you mean '--check-prefixes=" + prefix + "'?"
- warn(("Supplied prefix '%s' is invalid. Prefix must contain only alphanumeric characters, hyphens and underscores." + hint) %
- (prefix))
- def verify_filecheck_prefixes(fc_cmd):
- fc_cmd_parts = fc_cmd.split()
- for part in fc_cmd_parts:
- if "check-prefix=" in part:
- prefix = part.split('=', 1)[1]
- check_prefix(prefix)
- elif "check-prefixes=" in part:
- prefixes = part.split('=', 1)[1].split(',')
- for prefix in prefixes:
- check_prefix(prefix)
- if prefixes.count(prefix) > 1:
- warn("Supplied prefix '%s' is not unique in the prefix list." % (prefix,))
|