3 """A test case generator for register stackification.
5 This script exhaustively generates small linear SSA programs, then filters them
6 based on heuristics designed to keep interesting multivalue test cases and
7 prints them as LLVM IR functions in a FileCheck test file.
9 The output of this script is meant to be used in conjunction with
10 update_llc_test_checks.py.
13 ./multivalue-stackify.py > multivalue-stackify.ll
14 ../../../utils/update_llc_test_checks.py multivalue-stackify.ll
17 Programs are represented internally as lists of operations, where each operation
18 is a pair of tuples, the first of which specifies the operation's uses and the
19 second of which specifies its defs.
21 TODO: Before embarking on a rewrite of the register stackifier, an abstract
22 interpreter should be written to automatically check that the test assertions
23 generated by update_llc_test_checks.py have the same semantics as the functions
24 generated by this script. Once that is done, exhaustive testing can be done by
25 making `is_interesting` return True.
29 from itertools
import product
30 from collections
import deque
38 def get_num_defs(program
):
40 for _
, defs
in program
:
45 def possible_ops(program
):
46 program_defs
= get_num_defs(program
)
47 for num_defs
in range(MAX_PROGRAM_DEFS
- program_defs
+ 1):
48 for num_uses
in range(MAX_OP_USES
+ 1):
49 if num_defs
== 0 and num_uses
== 0:
51 for uses
in product(range(program_defs
), repeat
=num_uses
):
52 yield uses
, tuple(program_defs
+ i
for i
in range(num_defs
))
55 def generate_programs():
60 program
= queue
.popleft()
61 if len(program
) == MAX_PROGRAM_OPS
:
63 for op
in possible_ops(program
):
65 new_program
= program
+ [op
]
66 queue
.append(new_program
)
67 yield program_id
, new_program
70 def get_num_terminal_ops(program
):
72 for _
, defs
in program
:
75 return num_terminal_ops
78 def get_max_uses(program
):
79 num_uses
= [0] * MAX_PROGRAM_DEFS
80 for uses
, _
in program
:
86 def has_unused_op(program
):
87 used
= [False] * MAX_PROGRAM_DEFS
88 for uses
, defs
in program
[::-1]:
89 if defs
and all(not used
[d
] for d
in defs
):
96 def has_multivalue_use(program
):
97 is_multi
= [False] * MAX_PROGRAM_DEFS
98 for uses
, defs
in program
:
99 if any(is_multi
[u
] for u
in uses
):
107 def has_mvp_use(program
):
108 is_mvp
= [False] * MAX_PROGRAM_DEFS
109 for uses
, defs
in program
:
110 if uses
and all(is_mvp
[u
] for u
in uses
):
113 if any(is_mvp
[u
] for u
in uses
):
120 def is_interesting(program
):
121 # Allow only multivalue single-op programs
122 if len(program
) == 1:
123 return len(program
[0][1]) > 1
125 # Reject programs where the last two instructions are identical
126 if len(program
) >= 2 and program
[-1][0] == program
[-2][0]:
129 # Reject programs with too many ops that don't produce values
130 if get_num_terminal_ops(program
) > 2:
133 # The third use of a value is no more interesting than the second
134 if get_max_uses(program
) >= 3:
137 # Reject nontrivial programs that have unused instructions
138 if has_unused_op(program
):
141 # Reject programs that have boring MVP uses of MVP defs
142 if has_mvp_use(program
):
145 # Otherwise if it has multivalue usage it is interesting
146 return has_multivalue_use(program
)
149 def make_llvm_type(num_defs
):
153 return "{" + ", ".join(["i32"] * num_defs
) + "}"
156 def make_llvm_op_name(num_uses
, num_defs
):
157 return f
"op_{num_uses}_to_{num_defs}"
160 def make_llvm_args(first_use
, num_uses
):
161 return ", ".join([f
"i32 %t{first_use + i}" for i
in range(num_uses
)])
164 def print_llvm_program(program
, name
):
167 print(f
"define void @{name}() {{")
168 for uses
, defs
in program
:
172 ret_type
, var
, idx
= def_data
[use
]
173 print(f
" %t{tmp} = extractvalue {ret_type} %t{var}, {idx}")
178 assignment
= f
"%t{tmp} = "
181 ret_type
= make_llvm_type(len(defs
))
182 op_name
= make_llvm_op_name(len(uses
), len(defs
))
183 args
= make_llvm_args(first_arg
, len(uses
))
184 print(f
" {assignment}call {ret_type} @{op_name}({args})")
186 for i
in range(len(defs
)):
187 def_data
.append((ret_type
, result_var
, i
))
193 print("; NOTE: Test functions have been generated by multivalue-stackify.py.")
195 print("; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue", "| FileCheck %s")
197 print("; Test that the multivalue stackification works")
199 print('target triple = "wasm32-unknown-unknown"')
201 for num_uses
in range(MAX_OP_USES
+ 1):
202 for num_defs
in range(MAX_PROGRAM_DEFS
+ 1):
203 if num_uses
== 0 and num_defs
== 0:
205 ret_type
= make_llvm_type(num_defs
)
206 op_name
= make_llvm_op_name(num_uses
, num_defs
)
207 args
= make_llvm_args(0, num_uses
)
208 print(f
"declare {ret_type} @{op_name}({args})")
212 if __name__
== "__main__":
214 for i
, program
in generate_programs():
215 if is_interesting(program
):
216 print_llvm_program(program
, "f" + str(i
))