xref: /aosp_15_r20/external/mesa3d/src/asahi/compiler/agx_opt_empty_else.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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