xref: /llvm-project/llvm/test/CodeGen/WebAssembly/multivalue-stackify.py (revision b71edfaa4ec3c998aadb35255ce2f60bba2940b0)
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