1 /*
2 * Copyright 2023 Valve Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "util/list.h"
7 #include "agx_builder.h"
8 #include "agx_compiler.h"
9 #include "agx_opcodes.h"
10
11 /*
12 * Detect blocks with the sole contents:
13 *
14 * else n=1
15 * pop_exec n=1
16 *
17 * The else instruction is a no-op. To see that, consider the pseudocode for the
18 * sequence of operations "else n=1; pop_exec n=1":
19 *
20 * # else n=1
21 * if r0l == 0:
22 * r0l = 1
23 * elif r0l == 1:
24 * if [...]:
25 * r0l = 0
26 * else:
27 * r0l = 1
28 * exec_mask[thread] = (r0l == 0)
29 *
30 * # pop_exec n=1
31 * if r0l > 0:
32 * r0l -= 1
33 * exec_mask[thread] = (r0l == 0)
34 *
35 * That logic code simplifies to:
36 *
37 * if r0l > 0:
38 * r0l = r0l - 1
39 * exec_mask[thread] = (r0l == 0)
40 *
41 * which is just "pop_exec n=1".
42 *
43 * Therefore, this pass detects these blocks and deletes the else instruction.
44 * This has the effect of removing empty else blocks. Logically, that creates
45 * critical edges, so this pass can only run late (post-RA).
46 *
47 * The pass itself uses a simple state machine for pattern matching.
48 */
49
50 enum block_state {
51 STATE_ELSE = 0,
52 STATE_POP_EXEC,
53 STATE_DONE,
54
55 /* Must be last */
56 STATE_NONE,
57 };
58
59 static enum block_state
state_for_instr(const agx_instr * I)60 state_for_instr(const agx_instr *I)
61 {
62 switch (I->op) {
63 case AGX_OPCODE_ELSE_ICMP:
64 case AGX_OPCODE_ELSE_FCMP:
65 return (I->nest == 1) ? STATE_ELSE : STATE_NONE;
66
67 case AGX_OPCODE_POP_EXEC:
68 return (I->nest == 1) ? STATE_POP_EXEC : STATE_NONE;
69
70 default:
71 return STATE_NONE;
72 }
73 }
74
75 static bool
match_block(agx_block * blk)76 match_block(agx_block *blk)
77 {
78 enum block_state state = STATE_ELSE;
79
80 agx_foreach_instr_in_block(blk, I) {
81 if (state_for_instr(I) == state)
82 state++;
83 else
84 return false;
85 }
86
87 return (state == STATE_DONE);
88 }
89
90 void
agx_opt_empty_else(agx_context * ctx)91 agx_opt_empty_else(agx_context *ctx)
92 {
93 agx_foreach_block(ctx, blk) {
94 if (match_block(blk)) {
95 agx_instr *else_instr = agx_first_instr(blk);
96 assert(state_for_instr(else_instr) == STATE_ELSE && "block matched");
97
98 agx_remove_instruction(else_instr);
99 }
100 }
101 }
102