370 lines · python
1"""MIR test utility functions for UpdateTestChecks scripts."""2 3import re4import sys5from UpdateTestChecks import common6from UpdateTestChecks.common import (7 CHECK_RE,8 warn,9)10 11IR_FUNC_NAME_RE = re.compile(12 r"^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[A-Za-z0-9_.]+)\s*\("13)14IR_PREFIX_DATA_RE = re.compile(r"^ *(;|$)")15MIR_FUNC_NAME_RE = re.compile(r" *name: *(?P<func>[A-Za-z0-9_.-]+)")16MIR_BODY_BEGIN_RE = re.compile(r" *body: *\|")17MIR_BASIC_BLOCK_RE = re.compile(r" *bb\.[0-9]+.*:$")18MIR_PREFIX_DATA_RE = re.compile(r"^ *(;|bb.[0-9].*: *$|[a-z]+:( |$)|$)")19 20VREG_RE = re.compile(r"(%[0-9]+)(?:\.[a-z0-9_]+)?(?::[a-z0-9_]+)?(?:\([<>a-z0-9 ]+\))?")21MI_FLAGS_STR = (22 r"(frame-setup |frame-destroy |nnan |ninf |nsz |arcp |contract |afn "23 r"|reassoc |nuw |nsw |exact |nofpexcept |nomerge |unpredictable "24 r"|noconvergent |nneg |disjoint |nusw |samesign |inbounds )*"25)26VREG_DEF_FLAGS_STR = r"(?:dead |undef )*"27 28# Pattern to match the defined vregs and the opcode of an instruction that29# defines vregs. Opcodes starting with a lower-case 't' are allowed to match30# ARM's thumb instructions, like tADDi8 and t2ADDri.31VREG_DEF_RE = re.compile(32 r"^ *(?P<vregs>{2}{0}(?:, {2}{0})*) = "33 r"{1}(?P<opcode>[A-Zt][A-Za-z0-9_]+)".format(34 VREG_RE.pattern, MI_FLAGS_STR, VREG_DEF_FLAGS_STR35 )36)37 38MIR_FUNC_RE = re.compile(39 r"^---$"40 r"\n"41 r"^ *name: *(?P<func>[A-Za-z0-9_.-]+)$"42 r".*?"43 r"(?:^ *fixedStack: *(\[\])? *\n"44 r"(?P<fixedStack>.*?)\n?"45 r"^ *stack:"46 r".*?)?"47 r"^ *body: *\|\n"48 r"(?P<body>.*?)\n"49 r"^\.\.\.$",50 flags=(re.M | re.S),51)52 53 54def build_function_info_dictionary(55 test, raw_tool_output, triple, prefixes, func_dict, verbose56):57 for m in MIR_FUNC_RE.finditer(raw_tool_output):58 func = m.group("func")59 fixedStack = m.group("fixedStack")60 body = m.group("body")61 if verbose:62 print("Processing function: {}".format(func), file=sys.stderr)63 for l in body.splitlines():64 print(" {}".format(l), file=sys.stderr)65 66 # Vreg mangling67 mangled = []68 vreg_map = {}69 for func_line in body.splitlines(keepends=True):70 m = VREG_DEF_RE.match(func_line)71 if m:72 for vreg in VREG_RE.finditer(m.group("vregs")):73 if vreg.group(1) in vreg_map:74 name = vreg_map[vreg.group(1)]75 else:76 name = mangle_vreg(m.group("opcode"), vreg_map.values())77 vreg_map[vreg.group(1)] = name78 func_line = func_line.replace(79 vreg.group(1), "[[{}:%[0-9]+]]".format(name), 180 )81 for number, name in vreg_map.items():82 func_line = re.sub(83 r"{}\b".format(number), "[[{}]]".format(name), func_line84 )85 mangled.append(func_line)86 body = "".join(mangled)87 88 for prefix in prefixes:89 info = common.function_body(90 body, fixedStack, None, None, None, None, ginfo=None91 )92 if func in func_dict[prefix]:93 if (94 not func_dict[prefix][func]95 or func_dict[prefix][func].scrub != info.scrub96 or func_dict[prefix][func].extrascrub != info.extrascrub97 ):98 func_dict[prefix][func] = None99 else:100 func_dict[prefix][func] = info101 102 103def mangle_vreg(opcode, current_names):104 base = opcode105 # Simplify some common prefixes and suffixes106 if opcode.startswith("G_"):107 base = base[len("G_") :]108 if opcode.endswith("_PSEUDO"):109 base = base[: len("_PSEUDO")]110 # Shorten some common opcodes with long-ish names111 base = dict(112 IMPLICIT_DEF="DEF",113 GLOBAL_VALUE="GV",114 CONSTANT="C",115 FCONSTANT="C",116 MERGE_VALUES="MV",117 UNMERGE_VALUES="UV",118 INTRINSIC="INT",119 INTRINSIC_W_SIDE_EFFECTS="INT",120 INSERT_VECTOR_ELT="IVEC",121 EXTRACT_VECTOR_ELT="EVEC",122 SHUFFLE_VECTOR="SHUF",123 ).get(base, base)124 # Avoid ambiguity when opcodes end in numbers125 if len(base.rstrip("0123456789")) < len(base):126 base += "_"127 128 i = 0129 for name in current_names:130 if name.rstrip("0123456789") == base:131 i += 1132 if i:133 return "{}{}".format(base, i)134 return base135 136 137def find_mir_functions_with_one_bb(lines, verbose=False):138 result = []139 cur_func = None140 bbs = 0141 for line in lines:142 m = MIR_FUNC_NAME_RE.match(line)143 if m:144 if bbs == 1:145 result.append(cur_func)146 cur_func = m.group("func")147 bbs = 0148 m = MIR_BASIC_BLOCK_RE.match(line)149 if m:150 bbs += 1151 if bbs == 1:152 result.append(cur_func)153 return result154 155 156def add_mir_checks_for_function(157 test,158 output_lines,159 run_list,160 func_dict,161 func_name,162 single_bb,163 print_fixed_stack,164 first_check_is_next,165 at_the_function_name,166 check_indent=None,167):168 printed_prefixes = set()169 for run in run_list:170 for prefix in run[0]:171 if prefix in printed_prefixes:172 break173 # func_info can be empty if there was a prefix conflict.174 if not func_dict[prefix].get(func_name):175 continue176 if printed_prefixes:177 # Add some space between different check prefixes.178 indent = len(output_lines[-1]) - len(output_lines[-1].lstrip(" "))179 output_lines.append(" " * indent + ";")180 printed_prefixes.add(prefix)181 add_mir_check_lines(182 test,183 output_lines,184 prefix,185 ("@" if at_the_function_name else "") + func_name,186 single_bb,187 func_dict[prefix][func_name],188 print_fixed_stack,189 first_check_is_next,190 check_indent,191 )192 break193 else:194 warn(195 "Found conflicting asm for function: {}".format(func_name),196 test_file=test,197 )198 return output_lines199 200 201def add_mir_check_lines(202 test,203 output_lines,204 prefix,205 func_name,206 single_bb,207 func_info,208 print_fixed_stack,209 first_check_is_next,210 check_indent=None,211):212 func_body = str(func_info).splitlines()213 if single_bb:214 # Don't bother checking the basic block label for a single BB215 func_body.pop(0)216 217 if not func_body:218 warn(219 "Function has no instructions to check: {}".format(func_name),220 test_file=test,221 )222 return223 224 first_line = func_body[0]225 indent = len(first_line) - len(first_line.lstrip(" "))226 # A check comment, indented the appropriate amount227 if check_indent is not None:228 check = "{}; {}".format(check_indent, prefix)229 else:230 check = "{:>{}}; {}".format("", indent, prefix)231 232 output_lines.append("{}-LABEL: name: {}".format(check, func_name))233 234 if print_fixed_stack:235 output_lines.append("{}: fixedStack:".format(check))236 for stack_line in func_info.extrascrub.splitlines():237 filecheck_directive = check + "-NEXT"238 output_lines.append("{}: {}".format(filecheck_directive, stack_line))239 240 first_check = not first_check_is_next241 for func_line in func_body:242 if not func_line.strip():243 # The mir printer prints leading whitespace so we can't use CHECK-EMPTY:244 output_lines.append(check + "-NEXT: {{" + func_line + "$}}")245 continue246 filecheck_directive = check if first_check else check + "-NEXT"247 first_check = False248 check_line = "{}: {}".format(filecheck_directive, func_line[indent:]).rstrip()249 output_lines.append(check_line)250 251 252def should_add_mir_line_to_output(input_line, prefix_set):253 # Skip any check lines that we're handling as well as comments254 m = CHECK_RE.match(input_line)255 if (m and m.group(1) in prefix_set) or input_line.strip() == ";":256 return False257 return True258 259 260def add_mir_checks(261 input_lines,262 prefix_set,263 autogenerated_note,264 test,265 run_list,266 func_dict,267 print_fixed_stack,268 first_check_is_next,269 at_the_function_name,270):271 simple_functions = find_mir_functions_with_one_bb(input_lines)272 273 output_lines = []274 output_lines.append(autogenerated_note)275 276 func_name = None277 state = "toplevel"278 for input_line in input_lines:279 if input_line == autogenerated_note:280 continue281 282 if state == "toplevel":283 m = IR_FUNC_NAME_RE.match(input_line)284 if m:285 state = "ir function prefix"286 func_name = m.group("func")287 if input_line.rstrip("| \r\n") == "---":288 state = "document"289 output_lines.append(input_line)290 elif state == "document":291 m = MIR_FUNC_NAME_RE.match(input_line)292 if m:293 state = "mir function metadata"294 func_name = m.group("func")295 if input_line.strip() == "...":296 state = "toplevel"297 func_name = None298 if should_add_mir_line_to_output(input_line, prefix_set):299 output_lines.append(input_line)300 elif state == "mir function metadata":301 if should_add_mir_line_to_output(input_line, prefix_set):302 output_lines.append(input_line)303 m = MIR_BODY_BEGIN_RE.match(input_line)304 if m:305 if func_name in simple_functions:306 # If there's only one block, put the checks inside it307 state = "mir function prefix"308 continue309 state = "mir function body"310 add_mir_checks_for_function(311 test,312 output_lines,313 run_list,314 func_dict,315 func_name,316 single_bb=False,317 print_fixed_stack=print_fixed_stack,318 first_check_is_next=first_check_is_next,319 at_the_function_name=at_the_function_name,320 )321 elif state == "mir function prefix":322 m = MIR_PREFIX_DATA_RE.match(input_line)323 if not m:324 state = "mir function body"325 add_mir_checks_for_function(326 test,327 output_lines,328 run_list,329 func_dict,330 func_name,331 single_bb=True,332 print_fixed_stack=print_fixed_stack,333 first_check_is_next=first_check_is_next,334 at_the_function_name=at_the_function_name,335 )336 337 if should_add_mir_line_to_output(input_line, prefix_set):338 output_lines.append(input_line)339 elif state == "mir function body":340 if input_line.strip() == "...":341 state = "toplevel"342 func_name = None343 if should_add_mir_line_to_output(input_line, prefix_set):344 output_lines.append(input_line)345 elif state == "ir function prefix":346 m = IR_PREFIX_DATA_RE.match(input_line)347 if not m:348 state = "ir function body"349 add_mir_checks_for_function(350 test,351 output_lines,352 run_list,353 func_dict,354 func_name,355 single_bb=False,356 print_fixed_stack=print_fixed_stack,357 first_check_is_next=first_check_is_next,358 at_the_function_name=at_the_function_name,359 )360 361 if should_add_mir_line_to_output(input_line, prefix_set):362 output_lines.append(input_line)363 elif state == "ir function body":364 if input_line.strip() == "}":365 state = "toplevel"366 func_name = None367 if should_add_mir_line_to_output(input_line, prefix_set):368 output_lines.append(input_line)369 return output_lines370