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 56IR_FUNCTION_RE = re.compile('^\s*define\s+(?:internal\s+)?[^@]*@(\w+)\s*\(') 57TRIPLE_IR_RE = re.compile(r'^\s*target\s+triple\s*=\s*"([^"]+)"$') 58TRIPLE_ARG_RE = re.compile(r'-mtriple[= ]([^ ]+)') 59MARCH_ARG_RE = re.compile(r'-march[= ]([^ ]+)') 60 61SCRUB_LEADING_WHITESPACE_RE = re.compile(r'^(\s+)') 62SCRUB_WHITESPACE_RE = re.compile(r'(?!^(| \w))[ \t]+', flags=re.M) 63SCRUB_TRAILING_WHITESPACE_RE = re.compile(r'[ \t]+$', flags=re.M) 64SCRUB_KILL_COMMENT_RE = re.compile(r'^ *#+ +kill:.*\n') 65SCRUB_LOOP_COMMENT_RE = re.compile( 66 r'# =>This Inner Loop Header:.*|# in Loop:.*', flags=re.M) 67 68def scrub_body(body): 69 # Scrub runs of whitespace out of the assembly, but leave the leading 70 # whitespace in place. 71 body = SCRUB_WHITESPACE_RE.sub(r' ', body) 72 # Expand the tabs used for indentation. 73 body = string.expandtabs(body, 2) 74 # Strip trailing whitespace. 75 body = SCRUB_TRAILING_WHITESPACE_RE.sub(r'', body) 76 return body 77 78# Build up a dictionary of all the function bodies. 79def build_function_body_dictionary(function_re, scrubber, scrubber_args, raw_tool_output, prefixes, func_dict, verbose): 80 for m in function_re.finditer(raw_tool_output): 81 if not m: 82 continue 83 func = m.group('func') 84 scrubbed_body = scrubber(m.group('body'), *scrubber_args) 85 if func.startswith('stress'): 86 # We only use the last line of the function body for stress tests. 87 scrubbed_body = '\n'.join(scrubbed_body.splitlines()[-1:]) 88 if verbose: 89 print('Processing function: ' + func, file=sys.stderr) 90 for l in scrubbed_body.splitlines(): 91 print(' ' + l, file=sys.stderr) 92 for prefix in prefixes: 93 if func in func_dict[prefix] and func_dict[prefix][func] != scrubbed_body: 94 if prefix == prefixes[-1]: 95 print('WARNING: Found conflicting asm under the ' 96 'same prefix: %r!' % (prefix,), file=sys.stderr) 97 else: 98 func_dict[prefix][func] = None 99 continue 100 101 func_dict[prefix][func] = scrubbed_body 102 103##### Generator of LLVM IR CHECK lines 104 105SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*') 106 107# Match things that look at identifiers, but only if they are followed by 108# spaces, commas, paren, or end of the string 109IR_VALUE_RE = re.compile(r'(\s+)%([\w\.\-]+?)([,\s\(\)]|\Z)') 110 111# Create a FileCheck variable name based on an IR name. 112def get_value_name(var): 113 if var.isdigit(): 114 var = 'TMP' + var 115 var = var.replace('.', '_') 116 var = var.replace('-', '_') 117 return var.upper() 118 119 120# Create a FileCheck variable from regex. 121def get_value_definition(var): 122 return '[[' + get_value_name(var) + ':%.*]]' 123 124 125# Use a FileCheck variable. 126def get_value_use(var): 127 return '[[' + get_value_name(var) + ']]' 128 129# Replace IR value defs and uses with FileCheck variables. 130def genericize_check_lines(lines): 131 # This gets called for each match that occurs in 132 # a line. We transform variables we haven't seen 133 # into defs, and variables we have seen into uses. 134 def transform_line_vars(match): 135 var = match.group(2) 136 if var in vars_seen: 137 rv = get_value_use(var) 138 else: 139 vars_seen.add(var) 140 rv = get_value_definition(var) 141 # re.sub replaces the entire regex match 142 # with whatever you return, so we have 143 # to make sure to hand it back everything 144 # including the commas and spaces. 145 return match.group(1) + rv + match.group(3) 146 147 vars_seen = set() 148 lines_with_def = [] 149 150 for i, line in enumerate(lines): 151 # An IR variable named '%.' matches the FileCheck regex string. 152 line = line.replace('%.', '%dot') 153 # Ignore any comments, since the check lines will too. 154 scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line) 155 lines[i] = IR_VALUE_RE.sub(transform_line_vars, scrubbed_line) 156 return lines 157 158 159def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict, func_name): 160 # Label format is based on IR string. 161 check_label_format = '{} %s-LABEL: @%s('.format(comment_marker) 162 163 printed_prefixes = [] 164 for p in prefix_list: 165 checkprefixes = p[0] 166 for checkprefix in checkprefixes: 167 if checkprefix in printed_prefixes: 168 break 169 if not func_dict[checkprefix][func_name]: 170 continue 171 # Add some space between different check prefixes, but not after the last 172 # check line (before the test code). 173 #if len(printed_prefixes) != 0: 174 # output_lines.append(';') 175 printed_prefixes.append(checkprefix) 176 output_lines.append(check_label_format % (checkprefix, func_name)) 177 func_body = func_dict[checkprefix][func_name].splitlines() 178 179 # For IR output, change all defs to FileCheck variables, so we're immune 180 # to variable naming fashions. 181 func_body = genericize_check_lines(func_body) 182 183 # This could be selectively enabled with an optional invocation argument. 184 # Disabled for now: better to check everything. Be safe rather than sorry. 185 186 # Handle the first line of the function body as a special case because 187 # it's often just noise (a useless asm comment or entry label). 188 #if func_body[0].startswith("#") or func_body[0].startswith("entry:"): 189 # is_blank_line = True 190 #else: 191 # output_lines.append('; %s: %s' % (checkprefix, func_body[0])) 192 # is_blank_line = False 193 194 is_blank_line = False 195 196 for func_line in func_body: 197 if func_line.strip() == '': 198 is_blank_line = True 199 continue 200 # Do not waste time checking IR comments. 201 func_line = SCRUB_IR_COMMENT_RE.sub(r'', func_line) 202 203 # Skip blank lines instead of checking them. 204 if is_blank_line == True: 205 output_lines.append('{} {}: {}'.format( 206 comment_marker, checkprefix, func_line)) 207 else: 208 output_lines.append('{} {}-NEXT: {}'.format( 209 comment_marker, checkprefix, func_line)) 210 is_blank_line = False 211 212 # Add space between different check prefixes and also before the first 213 # line of code in the test function. 214 output_lines.append(comment_marker) 215 break 216 return output_lines 217