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