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