xref: /aosp_15_r20/external/mesa3d/src/asahi/compiler/agx_opt_break_if.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 
10 /*
11  * Replaces conditional breaks:
12  *
13  *    if_cmp x, y, n=1 {
14  *       break #value
15  *    }
16  *    pop_exec n=1
17  *
18  * with break_if_*cmp pseudo-instructions for further optimization. This
19  * assumes agx_opt_empty_else has already run.
20  */
21 
22 static void
match_block(agx_context * ctx,agx_block * block)23 match_block(agx_context *ctx, agx_block *block)
24 {
25    agx_instr *if_ = agx_last_instr(block);
26    if (!if_ ||
27        (if_->op != AGX_OPCODE_IF_ICMP && if_->op != AGX_OPCODE_IF_FCMP) ||
28        if_->nest != 1)
29       return;
30 
31    /* If's fallthrough to the then */
32    agx_block *then_block = agx_next_block(block);
33    assert(block->successors[0] == then_block && "successors for if");
34 
35    /* We're searching for a single block then, so the next block is else */
36    agx_block *else_block = agx_next_block(then_block);
37    if (block->successors[1] != else_block)
38       return;
39 
40    if (!list_is_singular(&then_block->instructions) ||
41        !list_is_singular(&else_block->instructions))
42       return;
43 
44    agx_instr *break_ = agx_last_instr(then_block);
45    agx_instr *pop = agx_last_instr(else_block);
46 
47    if (break_->op != AGX_OPCODE_BREAK || pop->op != AGX_OPCODE_POP_EXEC ||
48        pop->nest != 1)
49       return;
50 
51    /* Find the successor of the if */
52    agx_block *after = else_block->successors[0];
53    assert(else_block->successors[1] == NULL && "single successor");
54 
55    /* We are dropping one level of nesting (the if) when rewriting */
56    assert(break_->nest >= 1 && "must at least break out of the if");
57    unsigned new_nest = break_->nest - 1;
58 
59    /* Rewrite */
60    agx_builder b = agx_init_builder(ctx, agx_before_block(after));
61 
62    if (if_->op == AGX_OPCODE_IF_FCMP) {
63       agx_break_if_fcmp(&b, if_->src[0], if_->src[1], new_nest,
64                         if_->invert_cond, if_->fcond, break_->target);
65    } else {
66       agx_break_if_icmp(&b, if_->src[0], if_->src[1], new_nest,
67                         if_->invert_cond, if_->icond, break_->target);
68    }
69 
70    agx_remove_instruction(if_);
71    agx_remove_instruction(break_);
72    agx_remove_instruction(pop);
73 }
74 
75 void
agx_opt_break_if(agx_context * ctx)76 agx_opt_break_if(agx_context *ctx)
77 {
78    agx_foreach_block(ctx, block) {
79       match_block(ctx, block);
80    }
81 }
82