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