152861809SThomas Lively#!/usr/bin/env python3 252861809SThomas Lively 352861809SThomas Lively"""A test case generator for register stackification. 452861809SThomas Lively 552861809SThomas LivelyThis script exhaustively generates small linear SSA programs, then filters them 652861809SThomas Livelybased on heuristics designed to keep interesting multivalue test cases and 752861809SThomas Livelyprints them as LLVM IR functions in a FileCheck test file. 852861809SThomas Lively 952861809SThomas LivelyThe output of this script is meant to be used in conjunction with 1052861809SThomas Livelyupdate_llc_test_checks.py. 1152861809SThomas Lively 1252861809SThomas Lively ``` 1352861809SThomas Lively ./multivalue-stackify.py > multivalue-stackify.ll 1452861809SThomas Lively ../../../utils/update_llc_test_checks.py multivalue-stackify.ll 1552861809SThomas Lively ``` 1652861809SThomas Lively 1752861809SThomas LivelyPrograms are represented internally as lists of operations, where each operation 1852861809SThomas Livelyis a pair of tuples, the first of which specifies the operation's uses and the 1952861809SThomas Livelysecond of which specifies its defs. 2052861809SThomas Lively 2152861809SThomas LivelyTODO: Before embarking on a rewrite of the register stackifier, an abstract 2252861809SThomas Livelyinterpreter should be written to automatically check that the test assertions 2352861809SThomas Livelygenerated by update_llc_test_checks.py have the same semantics as the functions 2452861809SThomas Livelygenerated by this script. Once that is done, exhaustive testing can be done by 2552861809SThomas Livelymaking `is_interesting` return True. 2652861809SThomas Lively""" 2752861809SThomas Lively 2852861809SThomas Lively 2952861809SThomas Livelyfrom itertools import product 3052861809SThomas Livelyfrom collections import deque 3152861809SThomas Lively 3252861809SThomas Lively 3352861809SThomas LivelyMAX_PROGRAM_OPS = 4 3452861809SThomas LivelyMAX_PROGRAM_DEFS = 3 3552861809SThomas LivelyMAX_OP_USES = 2 3652861809SThomas Lively 3752861809SThomas Lively 3852861809SThomas Livelydef get_num_defs(program): 3952861809SThomas Lively num_defs = 0 4052861809SThomas Lively for _, defs in program: 4152861809SThomas Lively num_defs += len(defs) 4252861809SThomas Lively return num_defs 4352861809SThomas Lively 4452861809SThomas Lively 4552861809SThomas Livelydef possible_ops(program): 4652861809SThomas Lively program_defs = get_num_defs(program) 4752861809SThomas Lively for num_defs in range(MAX_PROGRAM_DEFS - program_defs + 1): 4852861809SThomas Lively for num_uses in range(MAX_OP_USES + 1): 4952861809SThomas Lively if num_defs == 0 and num_uses == 0: 5052861809SThomas Lively continue 5152861809SThomas Lively for uses in product(range(program_defs), repeat=num_uses): 5252861809SThomas Lively yield uses, tuple(program_defs + i for i in range(num_defs)) 5352861809SThomas Lively 5452861809SThomas Lively 5552861809SThomas Livelydef generate_programs(): 5652861809SThomas Lively queue = deque() 5752861809SThomas Lively queue.append([]) 5852861809SThomas Lively program_id = 0 5952861809SThomas Lively while True: 6052861809SThomas Lively program = queue.popleft() 6152861809SThomas Lively if len(program) == MAX_PROGRAM_OPS: 6252861809SThomas Lively break 6352861809SThomas Lively for op in possible_ops(program): 6452861809SThomas Lively program_id += 1 6552861809SThomas Lively new_program = program + [op] 6652861809SThomas Lively queue.append(new_program) 6752861809SThomas Lively yield program_id, new_program 6852861809SThomas Lively 6952861809SThomas Lively 7052861809SThomas Livelydef get_num_terminal_ops(program): 7152861809SThomas Lively num_terminal_ops = 0 7252861809SThomas Lively for _, defs in program: 7352861809SThomas Lively if len(defs) == 0: 7452861809SThomas Lively num_terminal_ops += 1 7552861809SThomas Lively return num_terminal_ops 7652861809SThomas Lively 7752861809SThomas Lively 7852861809SThomas Livelydef get_max_uses(program): 7952861809SThomas Lively num_uses = [0] * MAX_PROGRAM_DEFS 8052861809SThomas Lively for uses, _ in program: 8152861809SThomas Lively for u in uses: 8252861809SThomas Lively num_uses[u] += 1 8352861809SThomas Lively return max(num_uses) 8452861809SThomas Lively 8552861809SThomas Lively 8652861809SThomas Livelydef has_unused_op(program): 8752861809SThomas Lively used = [False] * MAX_PROGRAM_DEFS 8852861809SThomas Lively for uses, defs in program[::-1]: 8952861809SThomas Lively if defs and all(not used[d] for d in defs): 9052861809SThomas Lively return True 9152861809SThomas Lively for u in uses: 9252861809SThomas Lively used[u] = True 9352861809SThomas Lively return False 9452861809SThomas Lively 9552861809SThomas Lively 9652861809SThomas Livelydef has_multivalue_use(program): 9752861809SThomas Lively is_multi = [False] * MAX_PROGRAM_DEFS 9852861809SThomas Lively for uses, defs in program: 9952861809SThomas Lively if any(is_multi[u] for u in uses): 10052861809SThomas Lively return True 10152861809SThomas Lively if len(defs) >= 2: 10252861809SThomas Lively for d in defs: 10352861809SThomas Lively is_multi[d] = True 10452861809SThomas Lively return False 10552861809SThomas Lively 10652861809SThomas Lively 10752861809SThomas Livelydef has_mvp_use(program): 10852861809SThomas Lively is_mvp = [False] * MAX_PROGRAM_DEFS 10952861809SThomas Lively for uses, defs in program: 11052861809SThomas Lively if uses and all(is_mvp[u] for u in uses): 11152861809SThomas Lively return True 11252861809SThomas Lively if len(defs) <= 1: 11352861809SThomas Lively if any(is_mvp[u] for u in uses): 11452861809SThomas Lively return True 11552861809SThomas Lively for d in defs: 11652861809SThomas Lively is_mvp[d] = True 11752861809SThomas Lively return False 11852861809SThomas Lively 11952861809SThomas Lively 12052861809SThomas Livelydef is_interesting(program): 12152861809SThomas Lively # Allow only multivalue single-op programs 12252861809SThomas Lively if len(program) == 1: 12352861809SThomas Lively return len(program[0][1]) > 1 12452861809SThomas Lively 12552861809SThomas Lively # Reject programs where the last two instructions are identical 12652861809SThomas Lively if len(program) >= 2 and program[-1][0] == program[-2][0]: 12752861809SThomas Lively return False 12852861809SThomas Lively 12952861809SThomas Lively # Reject programs with too many ops that don't produce values 13052861809SThomas Lively if get_num_terminal_ops(program) > 2: 13152861809SThomas Lively return False 13252861809SThomas Lively 13352861809SThomas Lively # The third use of a value is no more interesting than the second 13452861809SThomas Lively if get_max_uses(program) >= 3: 13552861809SThomas Lively return False 13652861809SThomas Lively 13752861809SThomas Lively # Reject nontrivial programs that have unused instructions 13852861809SThomas Lively if has_unused_op(program): 13952861809SThomas Lively return False 14052861809SThomas Lively 14152861809SThomas Lively # Reject programs that have boring MVP uses of MVP defs 14252861809SThomas Lively if has_mvp_use(program): 14352861809SThomas Lively return False 14452861809SThomas Lively 14552861809SThomas Lively # Otherwise if it has multivalue usage it is interesting 14652861809SThomas Lively return has_multivalue_use(program) 14752861809SThomas Lively 14852861809SThomas Lively 14952861809SThomas Livelydef make_llvm_type(num_defs): 15052861809SThomas Lively if num_defs == 0: 151*b71edfaaSTobias Hieta return "void" 15252861809SThomas Lively else: 153*b71edfaaSTobias Hieta return "{" + ", ".join(["i32"] * num_defs) + "}" 15452861809SThomas Lively 15552861809SThomas Lively 15652861809SThomas Livelydef make_llvm_op_name(num_uses, num_defs): 157*b71edfaaSTobias Hieta return f"op_{num_uses}_to_{num_defs}" 15852861809SThomas Lively 15952861809SThomas Lively 16052861809SThomas Livelydef make_llvm_args(first_use, num_uses): 161*b71edfaaSTobias Hieta return ", ".join([f"i32 %t{first_use + i}" for i in range(num_uses)]) 16252861809SThomas Lively 16352861809SThomas Lively 16452861809SThomas Livelydef print_llvm_program(program, name): 16552861809SThomas Lively tmp = 0 16652861809SThomas Lively def_data = [] 167*b71edfaaSTobias Hieta print(f"define void @{name}() {{") 16852861809SThomas Lively for uses, defs in program: 16952861809SThomas Lively first_arg = tmp 17052861809SThomas Lively # Extract operands 17152861809SThomas Lively for use in uses: 17252861809SThomas Lively ret_type, var, idx = def_data[use] 173*b71edfaaSTobias Hieta print(f" %t{tmp} = extractvalue {ret_type} %t{var}, {idx}") 17452861809SThomas Lively tmp += 1 17552861809SThomas Lively # Print instruction 176*b71edfaaSTobias Hieta assignment = "" 17752861809SThomas Lively if len(defs) > 0: 178*b71edfaaSTobias Hieta assignment = f"%t{tmp} = " 17952861809SThomas Lively result_var = tmp 18052861809SThomas Lively tmp += 1 18152861809SThomas Lively ret_type = make_llvm_type(len(defs)) 18252861809SThomas Lively op_name = make_llvm_op_name(len(uses), len(defs)) 18352861809SThomas Lively args = make_llvm_args(first_arg, len(uses)) 184*b71edfaaSTobias Hieta print(f" {assignment}call {ret_type} @{op_name}({args})") 18552861809SThomas Lively # Update def_data 18652861809SThomas Lively for i in range(len(defs)): 18752861809SThomas Lively def_data.append((ret_type, result_var, i)) 188*b71edfaaSTobias Hieta print(" ret void") 189*b71edfaaSTobias Hieta print("}") 19052861809SThomas Lively 19152861809SThomas Lively 19252861809SThomas Livelydef print_header(): 193*b71edfaaSTobias Hieta print("; NOTE: Test functions have been generated by multivalue-stackify.py.") 19452861809SThomas Lively print() 195*b71edfaaSTobias Hieta print("; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue", "| FileCheck %s") 19652861809SThomas Lively print() 197*b71edfaaSTobias Hieta print("; Test that the multivalue stackification works") 19852861809SThomas Lively print() 19952861809SThomas Lively print('target triple = "wasm32-unknown-unknown"') 20052861809SThomas Lively print() 20152861809SThomas Lively for num_uses in range(MAX_OP_USES + 1): 20252861809SThomas Lively for num_defs in range(MAX_PROGRAM_DEFS + 1): 20352861809SThomas Lively if num_uses == 0 and num_defs == 0: 20452861809SThomas Lively continue 20552861809SThomas Lively ret_type = make_llvm_type(num_defs) 20652861809SThomas Lively op_name = make_llvm_op_name(num_uses, num_defs) 20752861809SThomas Lively args = make_llvm_args(0, num_uses) 208*b71edfaaSTobias Hieta print(f"declare {ret_type} @{op_name}({args})") 20952861809SThomas Lively print() 21052861809SThomas Lively 21152861809SThomas Lively 212*b71edfaaSTobias Hietaif __name__ == "__main__": 21352861809SThomas Lively print_header() 21452861809SThomas Lively for i, program in generate_programs(): 21552861809SThomas Lively if is_interesting(program): 216*b71edfaaSTobias Hieta print_llvm_program(program, "f" + str(i)) 21752861809SThomas Lively print() 218