1from __future__ import print_function 2import re 3import string 4import subprocess 5import sys 6import copy 7 8if sys.version_info[0] > 2: 9 class string: 10 expandtabs = str.expandtabs 11else: 12 import string 13 14##### Common utilities for update_*test_checks.py 15 16def should_add_line_to_output(input_line, prefix_set): 17 # Skip any blank comment lines in the IR. 18 if input_line.strip() == ';': 19 return False 20 # Skip any blank lines in the IR. 21 #if input_line.strip() == '': 22 # return False 23 # And skip any CHECK lines. We're building our own. 24 m = CHECK_RE.match(input_line) 25 if m and m.group(1) in prefix_set: 26 return False 27 28 return True 29 30# Invoke the tool that is being tested. 31def invoke_tool(exe, cmd_args, ir): 32 with open(ir) as ir_file: 33 # TODO Remove the str form which is used by update_test_checks.py and 34 # update_llc_test_checks.py 35 # The safer list form is used by update_cc_test_checks.py 36 if isinstance(cmd_args, list): 37 stdout = subprocess.check_output([exe] + cmd_args, stdin=ir_file) 38 else: 39 stdout = subprocess.check_output(exe + ' ' + cmd_args, 40 shell=True, stdin=ir_file) 41 if sys.version_info[0] > 2: 42 stdout = stdout.decode() 43 # Fix line endings to unix CR style. 44 return stdout.replace('\r\n', '\n') 45 46##### LLVM IR parser 47 48RUN_LINE_RE = re.compile('^\s*[;#]\s*RUN:\s*(.*)$') 49CHECK_PREFIX_RE = re.compile('--?check-prefix(?:es)?[= ](\S+)') 50PREFIX_RE = re.compile('^[a-zA-Z0-9_-]+$') 51CHECK_RE = re.compile(r'^\s*[;#]\s*([^:]+?)(?:-NEXT|-NOT|-DAG|-LABEL)?:') 52 53OPT_FUNCTION_RE = re.compile( 54 r'^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[\w-]+?)\s*\(' 55 r'(\s+)?[^)]*[^{]*\{\n(?P<body>.*?)^\}$', 56 flags=(re.M | re.S)) 57 58ANALYZE_FUNCTION_RE = re.compile( 59 r'^\s*\'(?P<analysis>[\w\s-]+?)\'\s+for\s+function\s+\'(?P<func>[\w-]+?)\':' 60 r'\s*\n(?P<body>.*)$', 61 flags=(re.X | re.S)) 62 63IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@(\w+)\s*\(') 64TRIPLE_IR_RE = re.compile(r'^\s*target\s+triple\s*=\s*"([^"]+)"$') 65TRIPLE_ARG_RE = re.compile(r'-mtriple[= ]([^ ]+)') 66MARCH_ARG_RE = re.compile(r'-march[= ]([^ ]+)') 67 68SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)') 69SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M) 70SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M) 71SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n') 72SCRUB_LOOP_COMMENT_RE = re.compile( 73 r'# =>This Inner Loop Header:.*|# in Loop:.*', flags=re.M) 74 75 76def error(msg, test_file=None): 77 if test_file: 78 msg = '{}: {}'.format(msg, test_file) 79 print('ERROR: {}'.format(msg), file=sys.stderr) 80 81def warn(msg, test_file=None): 82 if test_file: 83 msg = '{}: {}'.format(msg, test_file) 84 print('WARNING: {}'.format(msg), file=sys.stderr) 85 86def scrub_body(body): 87 # Scrub runs of whitespace out of the assembly, but leave the leading 88 # whitespace in place. 89 body = SCRUB_WHITESPACE_RE.sub(r' ', body) 90 # Expand the tabs used for indentation. 91 body = string.expandtabs(body, 2) 92 # Strip trailing whitespace. 93 body = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', body) 94 return body 95 96def do_scrub(body, scrubber, scrubber_args, extra): 97 if scrubber_args: 98 local_args = copy.deepcopy(scrubber_args) 99 local_args[0].extra_scrub = extra 100 return scrubber(body, *local_args) 101 return scrubber(body, *scrubber_args) 102 103# Build up a dictionary of all the function bodies. 104class function_body(object): 105 def __init__(self, string, extra): 106 self.scrub = string 107 self.extrascrub = extra 108 def __str__(self): 109 return self.scrub 110 111def build_function_body_dictionary(function_re, scrubber, scrubber_args, raw_tool_output, prefixes, func_dict, verbose): 112 for m in function_re.finditer(raw_tool_output): 113 if not m: 114 continue 115 func = m.group('func') 116 body = m.group('body') 117 scrubbed_body = do_scrub(body, scrubber, scrubber_args, extra = False) 118 scrubbed_extra = do_scrub(body, scrubber, scrubber_args, extra = True) 119 if 'analysis' in m.groupdict(): 120 analysis = m.group('analysis') 121 if analysis.lower() != 'cost model analysis': 122 warn('Unsupported analysis mode: %r!' % (analysis,)) 123 if func.startswith('stress'): 124 # We only use the last line of the function body for stress tests. 125 scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:]) 126 if verbose: 127 print('Processing function: ' + func, file=sys.stderr) 128 for l in scrubbed_body.splitlines(): 129 print(' ' + l, file=sys.stderr) 130 for prefix in prefixes: 131 if func in func_dict[prefix] and str(func_dict[prefix][func]) != scrubbed_body: 132 if func_dict[prefix][func] and func_dict[prefix][func].extrascrub == scrubbed_extra: 133 func_dict[prefix][func].scrub = scrubbed_extra 134 continue 135 else: 136 if prefix == prefixes[-1]: 137 warn('Found conflicting asm under the same prefix: %r!' % (prefix,)) 138 else: 139 func_dict[prefix][func] = None 140 continue 141 142 func_dict[prefix][func] = function_body(scrubbed_body, scrubbed_extra) 143 144##### Generator of LLVM IR CHECK lines 145 146SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*') 147 148# Match things that look at identifiers, but only if they are followed by 149# spaces, commas, paren, or end of the string 150IR_VALUE_RE = re.compile(r'(\s+)%([\w\.\-]+?)([,\s\(\)]|\Z)') 151 152# Create a FileCheck variable name based on an IR name. 153def get_value_name(var): 154 if var.isdigit(): 155 var = 'TMP' + var 156 var = var.replace('.', '_') 157 var = var.replace('-', '_') 158 return var.upper() 159 160 161# Create a FileCheck variable from regex. 162def get_value_definition(var): 163 return '[[' + get_value_name(var) + ':%.*]]' 164 165 166# Use a FileCheck variable. 167def get_value_use(var): 168 return '[[' + get_value_name(var) + ']]' 169 170# Replace IR value defs and uses with FileCheck variables. 171def genericize_check_lines(lines, is_analyze): 172 # This gets called for each match that occurs in 173 # a line. We transform variables we haven't seen 174 # into defs, and variables we have seen into uses. 175 def transform_line_vars(match): 176 var = match.group(2) 177 if var in vars_seen: 178 rv = get_value_use(var) 179 else: 180 vars_seen.add(var) 181 rv = get_value_definition(var) 182 # re.sub replaces the entire regex match 183 # with whatever you return, so we have 184 # to make sure to hand it back everything 185 # including the commas and spaces. 186 return match.group(1) + rv + match.group(3) 187 188 vars_seen = set() 189 lines_with_def = [] 190 191 for i, line in enumerate(lines): 192 # An IR variable named '%.' matches the FileCheck regex string. 193 line = line.replace('%.', '%dot') 194 # Ignore any comments, since the check lines will too. 195 scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line) 196 if is_analyze: 197 lines[i] = scrubbed_line 198 else: 199 lines[i] = IR_VALUE_RE.sub(transform_line_vars, scrubbed_line) 200 return lines 201 202 203def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, is_asm, is_analyze): 204 printed_prefixes = [] 205 for p in prefix_list: 206 checkprefixes = p[0] 207 for checkprefix in checkprefixes: 208 if checkprefix in printed_prefixes: 209 break 210 # TODO func_dict[checkprefix] may be None, '' or not exist. 211 # Fix the call sites. 212 if func_name not in func_dict[checkprefix] or not func_dict[checkprefix][func_name]: 213 continue 214 215 # Add some space between different check prefixes, but not after the last 216 # check line (before the test code). 217 if is_asm: 218 if len(printed_prefixes) != 0: 219 output_lines.append(comment_marker) 220 221 printed_prefixes.append(checkprefix) 222 output_lines.append(check_label_format % (checkprefix, func_name)) 223 func_body = str(func_dict[checkprefix][func_name]).splitlines() 224 225 # For ASM output, just emit the check lines. 226 if is_asm: 227 output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0])) 228 for func_line in func_body[1:]: 229 output_lines.append('%s %s-NEXT: %s' % (comment_marker, checkprefix, func_line)) 230 break 231 232 # For IR output, change all defs to FileCheck variables, so we're immune 233 # to variable naming fashions. 234 func_body = genericize_check_lines(func_body, is_analyze) 235 236 # This could be selectively enabled with an optional invocation argument. 237 # Disabled for now: better to check everything. Be safe rather than sorry. 238 239 # Handle the first line of the function body as a special case because 240 # it's often just noise (a useless asm comment or entry label). 241 #if func_body[0].startswith("#") or func_body[0].startswith("entry:"): 242 # is_blank_line = True 243 #else: 244 # output_lines.append('%s %s: %s' % (comment_marker, checkprefix, func_body[0])) 245 # is_blank_line = False 246 247 is_blank_line = False 248 249 for func_line in func_body: 250 if func_line.strip() == '': 251 is_blank_line = True 252 continue 253 # Do not waste time checking IR comments. 254 func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line) 255 256 # Skip blank lines instead of checking them. 257 if is_blank_line: 258 output_lines.append('{} {}: {}'.format( 259 comment_marker, checkprefix, func_line)) 260 else: 261 output_lines.append('{} {}-NEXT: {}'.format( 262 comment_marker, checkprefix, func_line)) 263 is_blank_line = False 264 265 # Add space between different check prefixes and also before the first 266 # line of code in the test function. 267 output_lines.append(comment_marker) 268 break 269 270def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict, func_name): 271 # Label format is based on IR string. 272 check_label_format = '{} %s-LABEL: @%s('.format(comment_marker) 273 add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, False, False) 274 275def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, func_name): 276 check_label_format = '{} %s-LABEL: \'%s\''.format(comment_marker) 277 add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, check_label_format, False, True) 278 279 280def check_prefix(prefix): 281 if not PREFIX_RE.match(prefix): 282 hint = "" 283 if ',' in prefix: 284 hint = " Did you mean '--check-prefixes=" + prefix + "'?" 285 warn(("Supplied prefix '%s' is invalid. Prefix must contain only alphanumeric characters, hyphens and underscores." + hint) % 286 (prefix)) 287 288 289def verify_filecheck_prefixes(fc_cmd): 290 fc_cmd_parts = fc_cmd.split() 291 for part in fc_cmd_parts: 292 if "check-prefix=" in part: 293 prefix = part.split('=', 1)[1] 294 check_prefix(prefix) 295 elif "check-prefixes=" in part: 296 prefixes = part.split('=', 1)[1].split(',') 297 for prefix in prefixes: 298 check_prefix(prefix) 299 if prefixes.count(prefix) > 1: 300 warn("Supplied prefix '%s' is not unique in the prefix list." % (prefix,)) 301