1*52861809SThomas Lively#!/usr/bin/env python3 2*52861809SThomas Lively 3*52861809SThomas Lively"""A test case generator for register stackification. 4*52861809SThomas Lively 5*52861809SThomas LivelyThis script exhaustively generates small linear SSA programs, then filters them 6*52861809SThomas Livelybased on heuristics designed to keep interesting multivalue test cases and 7*52861809SThomas Livelyprints them as LLVM IR functions in a FileCheck test file. 8*52861809SThomas Lively 9*52861809SThomas LivelyThe output of this script is meant to be used in conjunction with 10*52861809SThomas Livelyupdate_llc_test_checks.py. 11*52861809SThomas Lively 12*52861809SThomas Lively ``` 13*52861809SThomas Lively ./multivalue-stackify.py > multivalue-stackify.ll 14*52861809SThomas Lively ../../../utils/update_llc_test_checks.py multivalue-stackify.ll 15*52861809SThomas Lively ``` 16*52861809SThomas Lively 17*52861809SThomas LivelyPrograms are represented internally as lists of operations, where each operation 18*52861809SThomas Livelyis a pair of tuples, the first of which specifies the operation's uses and the 19*52861809SThomas Livelysecond of which specifies its defs. 20*52861809SThomas Lively 21*52861809SThomas LivelyTODO: Before embarking on a rewrite of the register stackifier, an abstract 22*52861809SThomas Livelyinterpreter should be written to automatically check that the test assertions 23*52861809SThomas Livelygenerated by update_llc_test_checks.py have the same semantics as the functions 24*52861809SThomas Livelygenerated by this script. Once that is done, exhaustive testing can be done by 25*52861809SThomas Livelymaking `is_interesting` return True. 26*52861809SThomas Lively""" 27*52861809SThomas Lively 28*52861809SThomas Lively 29*52861809SThomas Livelyfrom itertools import product 30*52861809SThomas Livelyfrom collections import deque 31*52861809SThomas Lively 32*52861809SThomas Lively 33*52861809SThomas LivelyMAX_PROGRAM_OPS = 4 34*52861809SThomas LivelyMAX_PROGRAM_DEFS = 3 35*52861809SThomas LivelyMAX_OP_USES = 2 36*52861809SThomas Lively 37*52861809SThomas Lively 38*52861809SThomas Livelydef get_num_defs(program): 39*52861809SThomas Lively num_defs = 0 40*52861809SThomas Lively for _, defs in program: 41*52861809SThomas Lively num_defs += len(defs) 42*52861809SThomas Lively return num_defs 43*52861809SThomas Lively 44*52861809SThomas Lively 45*52861809SThomas Livelydef possible_ops(program): 46*52861809SThomas Lively program_defs = get_num_defs(program) 47*52861809SThomas Lively for num_defs in range(MAX_PROGRAM_DEFS - program_defs + 1): 48*52861809SThomas Lively for num_uses in range(MAX_OP_USES + 1): 49*52861809SThomas Lively if num_defs == 0 and num_uses == 0: 50*52861809SThomas Lively continue 51*52861809SThomas Lively for uses in product(range(program_defs), repeat=num_uses): 52*52861809SThomas Lively yield uses, tuple(program_defs + i for i in range(num_defs)) 53*52861809SThomas Lively 54*52861809SThomas Lively 55*52861809SThomas Livelydef generate_programs(): 56*52861809SThomas Lively queue = deque() 57*52861809SThomas Lively queue.append([]) 58*52861809SThomas Lively program_id = 0 59*52861809SThomas Lively while True: 60*52861809SThomas Lively program = queue.popleft() 61*52861809SThomas Lively if len(program) == MAX_PROGRAM_OPS: 62*52861809SThomas Lively break 63*52861809SThomas Lively for op in possible_ops(program): 64*52861809SThomas Lively program_id += 1 65*52861809SThomas Lively new_program = program + [op] 66*52861809SThomas Lively queue.append(new_program) 67*52861809SThomas Lively yield program_id, new_program 68*52861809SThomas Lively 69*52861809SThomas Lively 70*52861809SThomas Livelydef get_num_terminal_ops(program): 71*52861809SThomas Lively num_terminal_ops = 0 72*52861809SThomas Lively for _, defs in program: 73*52861809SThomas Lively if len(defs) == 0: 74*52861809SThomas Lively num_terminal_ops += 1 75*52861809SThomas Lively return num_terminal_ops 76*52861809SThomas Lively 77*52861809SThomas Lively 78*52861809SThomas Livelydef get_max_uses(program): 79*52861809SThomas Lively num_uses = [0] * MAX_PROGRAM_DEFS 80*52861809SThomas Lively for uses, _ in program: 81*52861809SThomas Lively for u in uses: 82*52861809SThomas Lively num_uses[u] += 1 83*52861809SThomas Lively return max(num_uses) 84*52861809SThomas Lively 85*52861809SThomas Lively 86*52861809SThomas Livelydef has_unused_op(program): 87*52861809SThomas Lively used = [False] * MAX_PROGRAM_DEFS 88*52861809SThomas Lively for uses, defs in program[::-1]: 89*52861809SThomas Lively if defs and all(not used[d] for d in defs): 90*52861809SThomas Lively return True 91*52861809SThomas Lively for u in uses: 92*52861809SThomas Lively used[u] = True 93*52861809SThomas Lively return False 94*52861809SThomas Lively 95*52861809SThomas Lively 96*52861809SThomas Livelydef has_multivalue_use(program): 97*52861809SThomas Lively is_multi = [False] * MAX_PROGRAM_DEFS 98*52861809SThomas Lively for uses, defs in program: 99*52861809SThomas Lively if any(is_multi[u] for u in uses): 100*52861809SThomas Lively return True 101*52861809SThomas Lively if len(defs) >= 2: 102*52861809SThomas Lively for d in defs: 103*52861809SThomas Lively is_multi[d] = True 104*52861809SThomas Lively return False 105*52861809SThomas Lively 106*52861809SThomas Lively 107*52861809SThomas Livelydef has_mvp_use(program): 108*52861809SThomas Lively is_mvp = [False] * MAX_PROGRAM_DEFS 109*52861809SThomas Lively for uses, defs in program: 110*52861809SThomas Lively if uses and all(is_mvp[u] for u in uses): 111*52861809SThomas Lively return True 112*52861809SThomas Lively if len(defs) <= 1: 113*52861809SThomas Lively if any(is_mvp[u] for u in uses): 114*52861809SThomas Lively return True 115*52861809SThomas Lively for d in defs: 116*52861809SThomas Lively is_mvp[d] = True 117*52861809SThomas Lively return False 118*52861809SThomas Lively 119*52861809SThomas Lively 120*52861809SThomas Livelydef is_interesting(program): 121*52861809SThomas Lively # Allow only multivalue single-op programs 122*52861809SThomas Lively if len(program) == 1: 123*52861809SThomas Lively return len(program[0][1]) > 1 124*52861809SThomas Lively 125*52861809SThomas Lively # Reject programs where the last two instructions are identical 126*52861809SThomas Lively if len(program) >= 2 and program[-1][0] == program[-2][0]: 127*52861809SThomas Lively return False 128*52861809SThomas Lively 129*52861809SThomas Lively # Reject programs with too many ops that don't produce values 130*52861809SThomas Lively if get_num_terminal_ops(program) > 2: 131*52861809SThomas Lively return False 132*52861809SThomas Lively 133*52861809SThomas Lively # The third use of a value is no more interesting than the second 134*52861809SThomas Lively if get_max_uses(program) >= 3: 135*52861809SThomas Lively return False 136*52861809SThomas Lively 137*52861809SThomas Lively # Reject nontrivial programs that have unused instructions 138*52861809SThomas Lively if has_unused_op(program): 139*52861809SThomas Lively return False 140*52861809SThomas Lively 141*52861809SThomas Lively # Reject programs that have boring MVP uses of MVP defs 142*52861809SThomas Lively if has_mvp_use(program): 143*52861809SThomas Lively return False 144*52861809SThomas Lively 145*52861809SThomas Lively # Otherwise if it has multivalue usage it is interesting 146*52861809SThomas Lively return has_multivalue_use(program) 147*52861809SThomas Lively 148*52861809SThomas Lively 149*52861809SThomas Livelydef make_llvm_type(num_defs): 150*52861809SThomas Lively if num_defs == 0: 151*52861809SThomas Lively return 'void' 152*52861809SThomas Lively else: 153*52861809SThomas Lively return '{' + ', '.join(['i32'] * num_defs) + '}' 154*52861809SThomas Lively 155*52861809SThomas Lively 156*52861809SThomas Livelydef make_llvm_op_name(num_uses, num_defs): 157*52861809SThomas Lively return f'op_{num_uses}_to_{num_defs}' 158*52861809SThomas Lively 159*52861809SThomas Lively 160*52861809SThomas Livelydef make_llvm_args(first_use, num_uses): 161*52861809SThomas Lively return ', '.join([f'i32 %t{first_use + i}' for i in range(num_uses)]) 162*52861809SThomas Lively 163*52861809SThomas Lively 164*52861809SThomas Livelydef print_llvm_program(program, name): 165*52861809SThomas Lively tmp = 0 166*52861809SThomas Lively def_data = [] 167*52861809SThomas Lively print(f'define void @{name}() {{') 168*52861809SThomas Lively for uses, defs in program: 169*52861809SThomas Lively first_arg = tmp 170*52861809SThomas Lively # Extract operands 171*52861809SThomas Lively for use in uses: 172*52861809SThomas Lively ret_type, var, idx = def_data[use] 173*52861809SThomas Lively print(f' %t{tmp} = extractvalue {ret_type} %t{var}, {idx}') 174*52861809SThomas Lively tmp += 1 175*52861809SThomas Lively # Print instruction 176*52861809SThomas Lively assignment = '' 177*52861809SThomas Lively if len(defs) > 0: 178*52861809SThomas Lively assignment = f'%t{tmp} = ' 179*52861809SThomas Lively result_var = tmp 180*52861809SThomas Lively tmp += 1 181*52861809SThomas Lively ret_type = make_llvm_type(len(defs)) 182*52861809SThomas Lively op_name = make_llvm_op_name(len(uses), len(defs)) 183*52861809SThomas Lively args = make_llvm_args(first_arg, len(uses)) 184*52861809SThomas Lively print(f' {assignment}call {ret_type} @{op_name}({args})') 185*52861809SThomas Lively # Update def_data 186*52861809SThomas Lively for i in range(len(defs)): 187*52861809SThomas Lively def_data.append((ret_type, result_var, i)) 188*52861809SThomas Lively print(' ret void') 189*52861809SThomas Lively print('}') 190*52861809SThomas Lively 191*52861809SThomas Lively 192*52861809SThomas Livelydef print_header(): 193*52861809SThomas Lively print('; NOTE: Test functions have been generated by multivalue-stackify.py.') 194*52861809SThomas Lively print() 195*52861809SThomas Lively print('; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue', 196*52861809SThomas Lively '| FileCheck %s') 197*52861809SThomas Lively print() 198*52861809SThomas Lively print('; Test that the multivalue stackification works') 199*52861809SThomas Lively print() 200*52861809SThomas Lively print('target triple = "wasm32-unknown-unknown"') 201*52861809SThomas Lively print() 202*52861809SThomas Lively for num_uses in range(MAX_OP_USES + 1): 203*52861809SThomas Lively for num_defs in range(MAX_PROGRAM_DEFS + 1): 204*52861809SThomas Lively if num_uses == 0 and num_defs == 0: 205*52861809SThomas Lively continue 206*52861809SThomas Lively ret_type = make_llvm_type(num_defs) 207*52861809SThomas Lively op_name = make_llvm_op_name(num_uses, num_defs) 208*52861809SThomas Lively args = make_llvm_args(0, num_uses) 209*52861809SThomas Lively print(f'declare {ret_type} @{op_name}({args})') 210*52861809SThomas Lively print() 211*52861809SThomas Lively 212*52861809SThomas Lively 213*52861809SThomas Livelyif __name__ == '__main__': 214*52861809SThomas Lively print_header() 215*52861809SThomas Lively for i, program in generate_programs(): 216*52861809SThomas Lively if is_interesting(program): 217*52861809SThomas Lively print_llvm_program(program, 'f' + str(i)) 218*52861809SThomas Lively print() 219